Animating individual letters in three.js

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:

Oh no! The letter “o” rotates around the top left corner of the word instead of it’s own transform centre. It disappears behind the background and even leaves the field of vision because the rotation radius is way too long. At 0:08 and 0:16 it comes back into view.

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.

Letters in three.js are stacked at position 0,0 after centreing their geometries.
Letters in three.js are stacked at position 0,0 after centring their geometries.

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 );
Nice! The letter “o” now rotates around its own center within the word, which creates bit of a door-opening feel.

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.

Would it be a bad idea to get in touch?

If you have a project in mind, need wise advice, or just want to say hi: Write on to { click/tap to reveal }

© 2026. Lazy Developer Studio. All rights reserved.