We use Blender for backend processing some scenes made in Three.js, but they both use different co-ordinate systems so we need to convert between them.

Ideal world

If you can you should use an export format like GLTF and not have to deal with any of this, but sometimes that just isn’t feasible.

For example we load completely different models into Blender than we load into Three.js, so using a GLTF intermediate then replacing each model seems very wasteful.

In our case we have a JSON representation of a scene created in Three.js with model ids and positions/rotations etc.

Three.js to Blender

The bad way

Initially I was using this hacky method, which absolutely works but shows what I knew about the wonders of matrices at the time:

import bpy, mathutils # Main Blender library and Blender's math library
three_js_point = [1.23, 4.56, 7.89] # Get this into Blender however you like
blender_point = mathutils.Vector((
	three_js_point[0],
	-1 * three_js_point[2],
	three_js_point[1]))
# Now apply it to an object in Blender with something like
bpy.context.object.location = blender_point

While that works, it does make some things like rotations a pain which I worked around needing to actually resolve…

The better way

So when I watched 3blue1brown’s series on Linear Algebra (which is fantastic), it inspired me to do this properly with a transform matrix.

If you have your co-ordinates from Three.js and you want to use them in Blender:

import bpy_extras, mathutils
axis_conversion_matrix = bpy_extras.io_utils.axis_conversion(
	from_forward='-Z',
	from_up='Y',
	to_forward='Y',
	to_up='Z')
three_js_point = (1.23, 4.56, 7.89) # Note this is a tuple, not a list
blender_point = axis_conversion_matrix @ mathutils.Vector(three_js_point)

I can’t recall where I first saw this method, I didn’t scour the API docs to find it. Maybe it was Github Copilot that showed me or some random SO answer…

This should also work for rotations and scales, but I haven’t tested it so let me know if you do.

Blender to Three.js

In case you need the reverse, with co-ordinates from Blender that you want to use in Three.js:

const blender_to_three_matrix = new THREE.Matrix4().set(
	1.0, 0.0, 0.0, 0.0,
	0.0, 0.0, 1.0, 0.0,
	0.0, -1.0, 0.0, 0.0,
	0.0, 0.0, 0.0, 1.0
);
// Or create the matrix with Matrix4.makeBasis - https://threejs.org/docs/#api/en/math/Matrix4.makeBasis
your_mesh.position.applyMatrix4(blender_to_three_matrix)

Or if you want to prepare the co-ordinates in blender:

blender_to_three_matrix = bpy_extras.io_utils.axis_conversion(from_forward='Y', from_up='Z', to_forward='-Z', to_up='Y')
three_js_point = blender_to_three_matrix @ bpy.context.object.location

I don’t have a use for this one myself but I hope it’s useful to someone!

A library for it

While writing this post, of course I found a nice example that can do this for you by the amazing gkjohnson (who also authored three-mesh-bvh) so check that out if you need to convert a bunch of things. There is also a related discussion on the Three.js Discourse that might be useful.

Other software

All 3D software has to pick one co-ordinate system or another. To compare your software of choice, check out the table on this page or the picture on this one.

You should be able to easily tweak a few of these examples to make them work with Unity / Unreal / Godot etc.

Fin.

If I had to take one general lesson, it would be that learning some more advanced maths is finally useful. 3blue1brown’s videos are a great place to start.

I wish someone had given me an experience like this to apply linear algebra when I first learnt it… But Lockhart is still lamenting I guess.

Good luck out there!