Create Three.js AnimationClip with KeyframeTracks

This article explains how to structure a Three.js AnimationClip using multiple KeyframeTracks. You will learn how to define individual tracks for properties like position, rotation, and scale, combine them into a single cohesive animation clip, and play the resulting animation on a 3D object using the AnimationMixer.

Understanding the Components

To build an animation in Three.js from scratch, you need three core components: 1. KeyframeTrack: Defines the values of a specific property (like position or scale) at specific points in time. 2. AnimationClip: A container that holds one or more KeyframeTrack objects, representing a complete animation sequence. 3. AnimationMixer: The player engine that updates the object’s properties in the render loop based on the AnimationClip.


Step 1: Create the KeyframeTracks

Each KeyframeTrack requires a target property name, an array of times (in seconds), and an array of corresponding values.

Three.js provides specialized track classes depending on the data type: * VectorKeyframeTrack (for position and scale) * QuaternionKeyframeTrack (for rotation) * NumberKeyframeTrack (for opacity or morph target influences)

Here is how to define position and scale tracks:

import * as THREE from 'three';

// Define the time keyframes (in seconds)
const times = [0, 1, 2]; // Start, middle, and end of the animation

// Define position values for each keyframe (X, Y, Z coordinates)
const positionValues = [
  0, 0, 0,  // Time 0: Origin
  0, 5, 0,  // Time 1: Moved up 5 units
  0, 0, 0   // Time 2: Returned to origin
];

// Define scale values for each keyframe (X, Y, Z scale factors)
const scaleValues = [
  1, 1, 1,  // Time 0: Normal scale
  2, 2, 2,  // Time 1: Double scale
  1, 1, 1   // Time 2: Normal scale
];

// Create the tracks
// The first argument is the property binding path relative to the target object
const positionTrack = new THREE.VectorKeyframeTrack('.position', times, positionValues);
const scaleTrack = new THREE.VectorKeyframeTrack('.scale', times, scaleValues);

Step 2: Combine Tracks into an AnimationClip

Once your tracks are defined, instantiate a THREE.AnimationClip. The constructor accepts a name for the animation, the overall duration (set to -1 to automatically calculate it from the tracks), and an array containing your KeyframeTrack objects.

// Combine the position and scale tracks into a single clip
const animationDuration = -1; // Auto-calculate duration from keyframes
const moveAndScaleClip = new THREE.AnimationClip('MoveAndScale', animationDuration, [
  positionTrack,
  scaleTrack
]);

Step 3: Play the Animation with the AnimationMixer

To apply the animation to a 3D object (such as a Mesh), you must associate the object with an AnimationMixer, create an action from the clip, and update the mixer inside your animation render loop.

// 1. Create your mesh
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

// 2. Initialize the AnimationMixer for the mesh
const mixer = new THREE.AnimationMixer(mesh);

// 3. Create an AnimationAction and play it
const action = mixer.clipAction(moveAndScaleClip);
action.play();

// 4. Update the mixer in the render loop
const clock = new THREE.Clock();

function animate() {
  requestAnimationFrame(animate);

  const delta = clock.getDelta(); // Get seconds passed since last frame
  mixer.update(delta);            // Update the animation evaluation

  renderer.render(scene, camera);
}

animate();