How to Package Multiple Animations in glTF
The glTF (GL Transmission Format) standard packages multiple concurrent animation clips by defining discrete animation objects within a single top-level array, allowing 3D engines to isolate, blend, and play them simultaneously. This article explains the underlying structure of glTF animations, how channels and samplers decouple movement data, and how runtimes process these isolated clips concurrently on the same 3D model.
The Top-Level Animation Array
In a glTF file, animations are organized inside a root-level array
named animations. Rather than storing one long, continuous
timeline containing every action, the format defines each distinct clip
as an independent object inside this array.
{
"animations": [
{ "name": "Walk", "channels": [...], "samplers": [...] },
{ "name": "Shoot", "channels": [...], "samplers": [...] }
]
}Assigning a string to the name property identifies each
clip (such as "Idle", "Run", or "Attack"). Because each entry is a
self-contained JSON object, the glTF specification establishes strict
data boundaries between different actions.
Data Isolation via Channels and Samplers
Each animation object isolates its keyframe tracks through two core
components: samplers and channels.
- Samplers: Define the animation data curves. A
sampler references an
inputaccessor (the timeline timestamps in seconds), anoutputaccessor (the transformation values, such as coordinates or quaternions), and aninterpolationmethod (such asLINEAR,STEP, orCUBICSPLINE). - Channels: Connect a sampler directly to a specific
target. A channel specifies a
nodeindex (a bone or mesh in the scene hierarchy) and apathproperty indicating what is being modified:"translation","rotation","scale", or"weights"(for morph targets).
By scoping samplers and channels strictly inside their parent animation object, glTF ensures that the keyframes of one clip do not interfere with or overwrite the keyframes of another inside the file asset.
Sharing and Isolating Buffer Data
Although animation clips are logically distinct, the binary keyframe
data stored in the underlying .bin buffers can be
structured in two ways:
- Dedicated Buffers: Each animation clip points to
unique
bufferViewsandaccessors, isolating the raw binary data completely. - Shared Time Accessors: Multiple clips can reference identical timeline accessors (for instance, if two clips share an identical frame duration and rate), reducing file size while keeping channel mappings logically separate.
Concurrent Playback in Runtimes
glTF specifies the data format, while the client runtime (such as Three.js, Babylon.js, Unity, or Unreal Engine) handles concurrency. When a runtime loads a glTF file with multiple clips, it exposes them as distinct animation actions.
Runtimes achieve concurrency through two primary methods:
- Target Masking / Layering: One animation can control the lower body (nodes assigned to the legs in a "Walk" clip), while another controls the upper body (nodes assigned to the spine and arms in a "Shoot" clip). Because the channels target distinct node indices, the clips evaluate concurrently without conflicts.
- Transform Blending: When two concurrent animations target the same node (such as blending a "Walk" clip into a "Run" clip), the runtime evaluates both channels at their current timestamps and calculates an interpolated mathematical blend (slerp for rotations, lerp for translations) based on weight parameters defined by the engine.