Skeletal Animation
This document describes Three.js's skeletal animation system, which enables realistic character animation through hierarchical bone structures and vertex skinning. The system consists of three primary components: Skeleton hierarchies for defining bone transforms, SkinnedMesh for applying deformations to geometry, and AnimationMixer for controlling animation playback.
For basic 3D object transformation and hierarchy, see Scene Graph & Object3D. For morph target animation (an alternative to skeletal animation), see the geometry system documentation Geometry System. For loading animated models from external formats, see GLTF Import & Export.
Core Architecture
The skeletal animation system operates through a pipeline of transforms: bones define a hierarchy of coordinate spaces, the skeleton computes combined bone matrices, and the skinned mesh applies these matrices to deform vertices on the GPU.
Class Relationships
Diagram: Core skeletal animation classes and their relationships
Skeleton Hierarchy
Bone Class
The Bone class is a specialized Object3D that serves as a node in a skeletal hierarchy. It contains no additional logic beyond standard transformation properties.
Diagram: Example bone hierarchy for a humanoid character
| Property | Type | Description |
|---|---|---|
| type | string | Always set to "Bone" |
| isBone | boolean | Type flag, always true |
All transformation capabilities (position, rotation, scale, matrix operations) are inherited from Object3D.
Skeleton Class
The Skeleton class manages a collection of bones and computes the final transformation matrices needed for vertex skinning.
|--------------|----------------|---------------------------------------------------------------| | bones | Array<Bone> | Flat array of all bones in the skeleton | | boneInverses | Array<Matrix4> | Inverse bind pose matrices for each bone | | boneMatrices | Float32Array | Flattened array of 16 floats per bone for GPU upload |
| boneTexture | DataTexture | Optional texture containing bone matrices for large skeletons |
Key Methods:
calculateInverses()- Computes inverse bind matrices from current bone world transforms src/objects/Skeleton.js60-80update()- UpdatesboneMatricesarray with current bone transforms src/objects/Skeleton.js120-150computeBoneTexture()- Creates or updates the bone texture for GPU skinning src/objects/Skeleton.js155-200getBoneByName(name)- Retrieves a bone by its name property src/objects/Skeleton.js95-110
Diagram: Skeleton matrix computation pipeline
SkinnedMesh and Vertex Deformation
SkinnedMesh extends Mesh to add skeletal deformation capabilities. Each vertex is influenced by up to 4 bones, with the final position computed as a weighted blend of bone transformations.
Core Properties
| Property | Type | Description |
|---|---|---|
| skeleton | Skeleton | The skeleton controlling this mesh |
| bindMode | string | Either AttachedBindMode or DetachedBindMode |
| bindMatrix | Matrix4 | Transform from mesh space to bind pose space |
| bindMatrixInverse | Matrix4 | Inverse of bindMatrix |
Required Geometry Attributes
| Attribute | Type | Description |
|---|---|---|
| skinIndex | BufferAttribute | Bone indices (typically 4 per vertex) |
| skinWeight | BufferAttribute | Bone weights (typically 4 per vertex, normalized) |
Skinning Mathematics
The vertex deformation formula implemented in shaders:
transformedPosition = Σ(weight[i] × bone[index[i]] × originalPosition)For each vertex:
- Retrieve 4 bone indices and weights from attributes
- For each bone influence, transform the vertex position by that bone's matrix
- Multiply by the corresponding weight
- Sum all weighted contributions
Diagram: Multi-bone vertex skinning computation
CPU-Side Methods
SkinnedMesh provides CPU-side methods for computing skinned positions, used primarily for raycasting and bounding volume computation:
applyBoneTransform(index, vector)- Applies bone transformations to a single vertex src/objects/SkinnedMesh.js180-230boneTransform(index, target)- Legacy method, callsapplyBoneTransform()src/objects/SkinnedMesh.js240-250
These methods read skinIndex and skinWeight attributes and manually compute the weighted blend transformation.
Bind Modes
Bind modes control how the skeleton's bind pose relates to the mesh's coordinate space. This affects the computation of final bone matrices.
AttachedBindMode
Default mode where the mesh and skeleton share the same coordinate space. The skeleton's root bone is typically a child of the mesh.
Diagram: AttachedBindMode hierarchy
DetachedBindMode
The skeleton exists independently from the mesh. Requires explicit bindMatrix and bindMatrixInverse to transform between spaces.
Diagram: DetachedBindMode hierarchy
Bind Mode Comparison
| Aspect | AttachedBindMode | DetachedBindMode |
|---|---|---|
| Skeleton parent | Child of SkinnedMesh | Independent in scene |
| bindMatrix usage | Identity matrix | Required for coordinate transform |
| Typical use case | Character models | Shared skeleton across multiple meshes |
| Matrix formula | matrixWorld × boneInverse | bindMatrixInverse × matrixWorld × boneInverse |
The bind mode is set via the bind(skeleton, bindMatrix) method src/objects/SkinnedMesh.js120-150
Animation Playback System
The animation system consists of AnimationClip (keyframe data), AnimationMixer (playback controller), and AnimationAction (per-clip playback state).
Architecture
Diagram: Animation system architecture
AnimationClip Structure
An AnimationClip contains an array of KeyframeTrack objects, each targeting a specific property on a specific object:
Diagram: AnimationClip composition
Track Types and Property Paths
| Track Class | Data Type | Typical Use | Example Path |
|---|---|---|---|
| VectorKeyframeTrack | Vector3 | Position, scale | .bones[3].position |
| QuaternionKeyframeTrack | Quaternion | Rotation | .bones[5].quaternion |
| NumberKeyframeTrack | number | Morph weights, scalar props | .morphTargetInfluences[0] |
Property paths use PropertyBinding syntax to resolve targets on the root object examples/jsm/loaders/GLTFLoader.js3200-3300
AnimationMixer Usage Pattern
// Setup
const mixer = new AnimationMixer(skinnedMesh);
const action = mixer.clipAction(animationClip);
action.play();
// Animation loop
function animate() {
const delta = clock.getDelta();
mixer.update(delta); // Updates all active actions
renderer.render(scene, camera);
}The mixer's update(deltaTime) method:
- Advances playback time for all active actions
- Interpolates keyframe values based on current time
- Applies interpolated values to target properties via
PropertyBinding - Handles blending between multiple animations
Loading Skeletal Animations
GLTF Loader Integration
The GLTFLoader handles complete skeletal animation import, including skeleton creation, skinning setup, and animation clip construction.
Diagram: GLTF skeletal animation loading pipeline
Key Loader Methods
| Method | Purpose | Source |
|---|---|---|
| loadSkin(skinIndex) | Creates Skeleton from glTF skin definition | examples/jsm/loaders/GLTFLoader.js2800-2900 |
| loadAnimation(animationIndex) | Creates AnimationClip from glTF animation | examples/jsm/loaders/GLTFLoader.js3100-3300 |
| assignFinalMaterial(mesh) | Upgrades Mesh to SkinnedMesh if needed | examples/jsm/loaders/GLTFLoader.js2200-2300 |
The loader automatically:
- Converts glTF node hierarchy to
Boneobjects - Reads
inverseBindMatricesaccessor data - Creates
skinIndexandskinWeightbuffer attributes - Builds
KeyframeTrackobjects from animation samplers - Resolves animation channel targets to property paths
Exporting Skeletal Animations
GLTF Exporter Integration
The GLTFExporter serializes Three.js skeletal animations back to glTF format.
Diagram: GLTF skeletal animation export pipeline
Key Exporter Methods
| Method | Purpose | Source |
|---|---|---|
| processSkin(object) | Exports skeleton and inverse bind matrices | examples/jsm/exporters/GLTFExporter.js1900-2100 |
| processNode(object) | Exports bone transforms to nodes array | examples/jsm/exporters/GLTFExporter.js1600-1800 |
| processAnimation(clip) | Converts AnimationClip to glTF animation | examples/jsm/exporters/GLTFExporter.js2300-2600 |
The exporter handles:
- Building skin definitions with joint indices
- Writing inverse bind matrices to buffer accessors
- Converting
KeyframeTrackdata to glTF samplers - Resolving property paths to channel targets
- Supporting both
AttachedBindModeandDetachedBindMode
Performance Considerations
Bone Texture Mode
For skeletons with many bones (>64), Three.js can encode bone matrices into a DataTexture for more efficient GPU upload:
Diagram: Bone texture optimization for large skeletons
The texture is created automatically when skeleton.update() is called if the bone count exceeds the uniform limit src/objects/Skeleton.js155-200
Weight Normalization
The normalizeSkinWeights() method ensures that skin weights sum to 1.0 for each vertex, preventing visual artifacts:
skinnedMesh.normalizeSkinWeights(); // Call after modifying skinWeight attributeThis iterates through all vertices and normalizes the 4-component weight vectors src/objects/SkinnedMesh.js160-180
Bounding Volume Updates
SkinnedMesh provides methods to compute accurate bounding volumes that account for bone deformations:
computeBoundingBox()- CPU-side skinning to compute tight bounding box src/objects/SkinnedMesh.js250-280computeBoundingSphere()- CPU-side skinning to compute tight bounding sphere src/objects/SkinnedMesh.js285-315
These are expensive operations that should be called sparingly, ideally only when the animation significantly changes the mesh extents.