I’m currently working on a little text animation in three.js and wanted one of the letters to rotate around its y-axis. Seems straight-forward enough: I would expect a transform origin at the shape centre as in CSS, and that’s indeed how most three standard geometries work. But not custom models or, in my case, ExtrudeGeometries. The next video shows what really happens:
How to centre individual letters in words, or any other objects in groups for that matter, has not been well documented yet1. Since it took me an entire afternoon to figure it out, wanted to share this code bit with you.
Centring the letter destroys relative positions
Typically, you would correct this problem by simply calling geometry.center() on all letters before you create your meshes. But that doesn’t work when your letters are part of a word since it puts all letters at world position 0, 0 right in the middle of your screen on top of each other.

This happens since all objects in a group share a common transform centre in three.js, which makes sense: With most models you would want all parts to stay in sync (think of a crane with its arm moving up and down, while the crane bases rotates). But with letter animations we usually want to transform each individual letter in place.
Centring both the letters and the group
First, create ….
Creating each letter’s centre individually:
meshes.forEach( ( mesh, i ) => {
// Get the original letter coords. The word is positioned at 0,0 with its upper left corner.
mesh.geometry.computeBoundingBox();
let pos = mesh.localToWorld( mesh.geometry.boundingBox.getCenter( new THREE.Vector3() ) );
// Set transform origin. But now all letters are in the middle of the canvas on top of each other.
mesh.geometry.center();
// Restore original letter position
mesh.position.set( ...pos );
// Three.js mirrors svgs on load. Reset to normal
mesh.scale.y *= -1;
mesh.name = `letter-${ i }`;
group.add( mesh );
} );
Centring the entire word:
const bounds = new THREE.Box3().setFromObject( group );
const middle = bounds.getCenter( new THREE.Vector3() ).clone();
middle.multiplyScalar( -1 );
group.position.set( ...middle )
scene.add( group );
Does this work with TextGeometries?
I’m sorry but I would not know how. With TextGeometries, you get one geometry for your entire text. This is perfect if you want to apply effects through shaders. But unfortunately, it means you can’t transform individual letters.