Play Skeletal Animations from GLTF in Three.js
Working with 3D models in Three.js often involves animating
characters using skeletal rigs. This article provides a direct,
step-by-step guide on how to load a GLTF or GLB file, extract its
embedded skeletal animations, and play them seamlessly using the
Three.js AnimationMixer system.
1. Load the GLTF Model
First, you need to import and use the GLTFLoader to load
your 3D asset. Ensure you have a global variable to store the
AnimationMixer so it can be accessed inside your render
loop.
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
let mixer; // Global variable for the animation mixer
const loader = new GLTFLoader();
loader.load(
'path/to/model.gltf',
(gltf) => {
const model = gltf.scene;
scene.add(model);
// Enable shadows for the skeleton if needed
model.traverse((object) => {
if (object.isMesh) object.castShadow = true;
});
// Initialize animations
setupAnimations(gltf);
},
undefined,
(error) => {
console.error('An error occurred while loading the GLTF model:', error);
}
);2. Initialize the AnimationMixer and Play Clips
The gltf object returned by the loader contains an
animations array. This array holds
THREE.AnimationClip objects. To play these clips, you must
bind them to the loaded model using a
THREE.AnimationMixer.
function setupAnimations(gltf) {
const model = gltf.scene;
const animations = gltf.animations;
if (animations && animations.length) {
// Create the mixer for the model
mixer = new THREE.AnimationMixer(model);
// Option A: Play the first animation clip
const firstClip = animations[0];
const action = mixer.clipAction(firstClip);
action.play();
// Option B: Find and play an animation by its name
// const specificClip = THREE.AnimationClip.findByName(animations, 'Run');
// const action = mixer.clipAction(specificClip);
// action.play();
}
}3. Update the Mixer in the Animation Loop
For animations to progress over time, the AnimationMixer
must be updated on every frame. Use a THREE.Clock to
calculate the time delta (the time elapsed since the last frame) and
pass it to the mixer’s update method.
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
// Calculate the time delta
const delta = clock.getDelta();
// Update the animation mixer
if (mixer) {
mixer.update(delta);
}
// Render the scene
renderer.render(scene, camera);
}
animate();Controlling the Animation Playback
Once you have the AnimationAction object (returned by
mixer.clipAction()), you can control its playback using the
following methods:
action.stop(): Stops the animation and resets its time.action.pause = true: Pauses the animation at the current frame.action.setLoop(THREE.LoopOnce): Plays the animation only once instead of looping infinitely.action.fadeOut(duration): Smoothly fades out the animation over a set duration, useful for transitioning between states (e.g., walking to running).