Sharing Bone Rigs Across Characters in glTF
Reusing bone rigs across multiple animated characters in a single glTF file is an effective way to optimize 3D asset workflows and minimize file size. This guide outlines how the glTF 2.0 specification handles skeletal sharing, explaining how multiple meshes bind to a unified joint hierarchy, how data accessors are reused, and how animations interact with shared skeletal nodes.
Understanding the glTF Skinning Architecture
In the glTF specification, skeletal animation relies on three primary components:
- Nodes: Represent the transformation hierarchy of the scene, including individual skeleton joints.
- Skins: Define collections of joint nodes, an
optional skeleton root node, and an accessor pointing to the
inverseBindMatrices. - Meshes: Contain primitive geometry with vertex
attributes for joint indices (
JOINTS_0) and bone weights (WEIGHTS_0).
Because a mesh node explicitly declares its associated skin via a
skin property index, multiple nodes can point to the same
skin definition or share underlying data buffers.
Method 1: Binding Multiple Meshes to a Single Rig
If the objective is to display multiple characters or modular parts
(such as clothing, armor, or swappable body parts) moving in perfect
synchronization, multiple mesh nodes can reference the exact same
skin instance.
In the glTF JSON structure:
- Define the joint hierarchy once under the
nodesarray. - Create a single entry in the
skinsarray containing the list of joint node indices. - Create multiple character nodes, assign each character its distinct
meshindex, and set theskinproperty on all of them to the same skin index:
"nodes": [
{ "name": "Character_A", "mesh": 0, "skin": 0 },
{ "name": "Character_B", "mesh": 1, "skin": 0 }
]Every mesh referencing skin: 0 will evaluate its vertex
positions against the transforms of that single joint hierarchy.
Transforming the bones will deform all attached characters
simultaneously.
Method 2: Independent Characters Reusing Rig Data
Because glTF animation channels target specific node indices directly, two independently moving characters cannot share the exact same joint nodes inside a single scene. If character instances must move independently within the same glTF file, each requires its own set of joint nodes.
However, you can still share the structural data across characters to save memory:
- Shared Inverse Bind Matrices: Multiple
skinobjects can reference the sameaccessorindex for theirinverseBindMatrices. As long as the rest poses and bone counts are identical, this large matrix array does not need duplication. - Shared Keyframe Samplers: If characters perform
identical animations at different times or offsets,
animation.channelscan target the separate joint nodes while theirsamplerproperties point to the same input (timestamps) and output (TRS values) accessors.
"skins": [
{
"inverseBindMatrices": 4,
"joints": [10, 11, 12]
},
{
"inverseBindMatrices": 4,
"joints": [20, 21, 22]
}
]Best Practices for Implementation
- Standardize Joint Indices: Ensure that all shared
meshes use the exact same bone index mapping in their
JOINTS_0vertex attributes so they align with the sharedjointsarray order. - Root Node Offsets: When binding several full characters to one rig for synchronized display, use local vertex offsets in the mesh primitives rather than node transforms, as skinning overrides normal node-level translation.
- Runtime Retargeting: For game engines (such as Three.js, Babylon.js, or Godot), the most efficient setup is often exporting the rig and animation once, exporting character meshes separately, and cloning or retargeting the skeleton dynamically at runtime.