Initialize Three.js AnimationMixer for Character Animation
This article provides a step-by-step guide on how to properly
initialize and use the AnimationMixer in Three.js to play
complex character animations. You will learn how to bind the mixer to a
3D model, load animation clips, trigger playback using animation
actions, and correctly update the mixer within the rendering loop to
achieve smooth transitions.
Step 1: Load the Model and Instantiate the Mixer
To animate a character, you first need a rigged 3D model (typically
in GLTF/GLB format) that contains embedded skeleton animations. When the
loader successfully imports the model, you initialize the
AnimationMixer by passing the root object of the model to
the mixer’s constructor.
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
let mixer;
const loader = new GLTFLoader();
loader.load('character.glb', (gltf) => {
const model = gltf.scene;
scene.add(model);
// Initialize the AnimationMixer with the root model object
mixer = new THREE.AnimationMixer(model);
});Step 2: Create and Play Animation Actions
The loaded GLTF object contains an array of
AnimationClip objects inside gltf.animations.
To play these animations, you must convert the clips into controllable
AnimationAction objects using the mixer’s
clipAction method.
// Access the animation clips array from the loaded GLTF
const clips = gltf.animations;
// Find a specific clip by name (e.g., "Idle" or "Run")
const idleClip = THREE.AnimationClip.findByName(clips, 'Idle');
// Create an AnimationAction for the clip
const idleAction = mixer.clipAction(idleClip);
// Play the animation action
idleAction.play();Step 3: Update the Mixer in the Render Loop
The AnimationMixer requires manual updates to progress
the animation frames. You must calculate the time elapsed between frames
(delta time) using a THREE.Clock and pass it to the mixer’s
update method inside your requestAnimationFrame loop.
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
// Get the delta time in seconds
const delta = clock.getDelta();
// Update the animation mixer
if (mixer) {
mixer.update(delta);
}
renderer.render(scene, camera);
}
animate();Step 4: Handle Complex Animations and Blending
For complex character states (like transitioning from walking to
running), you can smoothly blend animations. Three.js allows you to
crossfade actions using the crossFadeTo method, which
prevents abrupt visual snapping.
let currentAction = idleAction;
function transitionTo(nextAction, duration = 0.5) {
if (currentAction === nextAction) return;
// Reset the next action so it starts from the beginning
nextAction.reset();
nextAction.play();
nextAction.setEffectiveWeight(1);
// Crossfade from the current action to the next action
currentAction.crossFadeTo(nextAction, duration, true);
// Update the current action reference
currentAction = nextAction;
}