Adjust Three.js Animation Timescale Dynamically
This article explains how to dynamically adjust the playback speed of
an ongoing animation in Three.js using the
AnimationAction.timeScale property. You will learn how to
access the active animation action, modify its speed in real-time, and
smoothly transition between different playback speeds to create seamless
visual effects.
Understanding the AnimationAction timeScale
In Three.js, animations are managed by an
AnimationMixer, which plays back AnimationClip
resources using AnimationAction objects. The playback rate
of any active AnimationAction is controlled by its
timeScale property.
The default value of timeScale is 1.0,
which represents normal playback speed. * A value of 2.0
doubles the speed. * A value of 0.5 halves the speed. * A
value of 0.0 pauses the animation. * Negative values (e.g.,
-1.0) play the animation backward.
Directly Modifying the Speed
To change the speed of an animation instantly, obtain a reference to
your AnimationAction and set its timeScale
property directly.
// Initialize the mixer and get the animation action
const mixer = new THREE.AnimationMixer(characterMesh);
const walkClip = THREE.AnimationClip.findByName(clips, 'Walk');
const walkAction = mixer.clipAction(walkClip);
// Play the animation
walkAction.play();
// Dynamically change the speed to double-time
walkAction.timeScale = 2.0;
// Dynamically change the speed to slow-motion
walkAction.timeScale = 0.25;This change takes effect immediately on the next frame update within
your requestAnimationFrame loop where
mixer.update(deltaTime) is called.
Transitioning Speed Smoothly
Abruptly changing the animation speed can look unnatural. To smoothly
transition between two speeds, you can interpolate the
timeScale value inside your application’s update loop.
Here is how to implement a smooth linear interpolation (lerp) for the speed adjustment:
let targetTimeScale = 1.0;
const transitionSpeed = 0.05; // Control how fast the speed adjusts
function update() {
requestAnimationFrame(update);
const clockDelta = clock.getDelta();
// Linearly interpolate the current timeScale toward the targetTimeScale
if (walkAction.timeScale !== targetTimeScale) {
walkAction.timeScale = THREE.MathUtils.lerp(
walkAction.timeScale,
targetTimeScale,
transitionSpeed
);
}
// Update the animation mixer
mixer.update(clockDelta);
renderer.render(scene, camera);
}
// Example trigger: Slow down animation when user holds a key
window.addEventListener('keydown', (e) => {
if (e.code === 'Space') {
targetTimeScale = 0.2; // Smoothly slow down to 20% speed
}
});
window.addEventListener('keyup', (e) => {
if (e.code === 'Space') {
targetTimeScale = 1.0; // Smoothly return to normal speed
}
});Using this approach ensures that transitions between fast running, walking, and slow-motion states look fluid and natural.