Three.js From Zero · Article s3-10

The Full Character Rig

← threejs-from-zeroS3 · Article 10 Season 3 FINALE
Article S3-10 · Three.js From Zero

The Full Character Rig

Season 3 finale. Every technique from S3-01 through S3-09 stacked into one character: skeleton with morphs (S3-01, S3-02), blend tree for locomotion (S3-03), IK for hand-reach and foot-plant (S3-04), procedural secondary motion (S3-05), physics-driven hit reactions (S3-06), motion-matched clip selection (S3-07), pipeline-imported animations (S3-08), facial-capture driving (S3-09).

The demo is a character cycling through four states — idle, walk, reach (IK), hit (physics). You can see which layer contributes to which bone. It's a complete game-ready character loop in one HTML file.

loading…

The rig pipeline in one picture

Per frame, for each bone, in order:

    1. BASE POSE      ← AnimationMixer output (keyframe or blend tree)
    2. + FACIAL       ← morph targets (camera or preset)
    3. + IK OVERRIDE  ← hand-reach, foot-plant (specific bones only)
    4. + PROCEDURAL   ← spring-damper (tail, breath, head-bob)
    5. = RENDERED POSE

When RAGDOLL state is active:
    Skip 1-4; read bone positions directly from physics bodies.
    On BLEND state: lerp ragdoll pose → animated pose over ~1s.

Every layer owns specific bones or specific channels (rotation vs translation vs scale). Conflicts resolved by order — later layers override earlier ones.

Layer ownership matrix

Bone groupOwner (by state)
Spine, pelvis, legsMixer (locomotion blend tree)
FeetIK (foot plant on terrain) overrides mixer
Arms & handsMixer (default) OR IK (if reach target set)
FingersMixer for gestures, IK for grabbing props
Head, neckMixer + spring damper (head-bob) + look-at (target-attention)
Face (morphs)Facial capture driver or preset
Tail, hair, cloakProcedural (spring-damper / Verlet)

The evaluation loop

function updateCharacter(dt) {
  // 1. Physics state transitions
  if (wantsRagdoll) {
    state = 'ragdoll';
    seedBodiesFromBones();
    mixer.stopAllAction();
    return;
  }

  if (state === 'ragdoll') {
    syncBonesFromBodies();
    if (ragdollSettled()) startBlendBack();
    return;
  }

  if (state === 'blending') {
    mixer.update(dt);
    lerpBonesFromCapturedRagdoll(blendProgress);
    if (blendProgress >= 1) state = 'anim';
    return;
  }

  // state === 'anim' — the normal pipeline

  // 2. Locomotion blend tree — sets most bones
  updateLocomotionBlend(speed, strafe);
  mixer.update(dt);

  // 3. Facial — runs independently, only affects head morphs
  updateFacialCapture(dt);

  // 4. IK — runs AFTER mixer, overrides specific bones
  if (reachTarget) solveArmIK(reachTarget);
  solveFootIK();
  updateLookAt(focusTarget);

  // 5. Procedural — runs LAST, adds secondary motion
  updateBreathing(t);
  updateTailSpring(dt);
  updateHeadBob(dt);
}

State machine driving the rig

ANIM ──[take damage]────→ RAGDOLL ──[settled]──→ BLENDING ──[done]──→ ANIM
  │                                                                    ↑
  ├──[reach button]─→ starts IK on arm, keeps ANIM                    ─┘
  ├──[move input]──→ updates locomotion axis
  └──[face cam on]─→ starts FacialCapture driver

Only RAGDOLL takes over fully. All other "modes" (reaching, walking, smiling) are additive layers on top of ANIM. That's what makes the rig flexible — the character keeps walking while reaching while smiling while getting tail-wagged.

Order matters

The evaluation order above is the canonical pipeline. A common bug is running procedural before IK — the spring stores a target based on pre-IK pose, then IK moves the arm, and next frame the spring yanks the arm back. Layers must run in the order you want them composed.

Networked characters

S2-08 multiplayer meets S3 rigs. Don't serialize every bone every frame — too heavy. Send input + state:

{
  pos: [x, y, z],
  rot: [yaw],
  state: 'anim',
  locomotion: { speed: 0.7, strafe: 0.0 },
  reachTarget: null,
  facialBlendshapes: [52 × int8],   // quantized to save bandwidth
  ragdollImpulse: null,
}

Remote clients reconstruct the full pose from these inputs. ~50 bytes per character per tick. Scales to 100 networked characters easily.

Performance — what costs what

LayerCost per character
Mixer update~0.1 ms
Foot IK (2 legs, CCDIK)~0.3 ms
Arm IK (two-bone)~0.1 ms
Procedural tail (10 joints)~0.05 ms
Ragdoll physics (Rapier, 10 bodies)~0.2 ms
FacialCapture (MediaPipe)~15 ms (runs separately, not per-character)

For 60fps, ~0.8 ms per character all-in. Budget: 30+ fully-rigged characters at 60 fps per frame budget (excluding rendering).

Common first-time integration bugs

  • IK pops when blend-back ends. IK target frame of reference changed (ragdoll → anim). Transition target smoothly.
  • Foot slides when turning. Foot IK is solving per-frame but your root motion doesn't match blend-tree output. Apply root-motion delta consistently.
  • Head-bob fights IK neck-look. Spring target for head-bob includes IK rotation → spring fights itself. Give head-bob its own bone (or run BEFORE look-at IK).
  • Character flickers between clips at low speed. Blend tree is swapping Idle ↔ Walk too aggressively. Add hysteresis (different thresholds for enter vs exit).
  • Facial capture pose interferes with "cutscene" face animation. During cutscenes, disable the cam driver. Layer-based authority.
  • Ragdoll activates during a critical action (jump). Add invincibility frames or exit-conditions from dynamic states.

What you just built

Starting from S3-01's cylinder-with-bones, you now have the full real-time 3D character stack. This is what you'd ship in:

  • A browser-based MMO or social app
  • A WebGL game
  • A virtual-avatar video chat
  • An AR/VR experience (S2-05, S2-06)
  • A 3D customer-support chatbot with live lip sync
  • A motion-capture demo / portfolio site

Every piece is standards-based, runs in any modern browser, and doesn't require a game engine. You can build shippable products with only what Season 3 teaches.

Exercises — integration challenges

  1. Record & playback: serialize a character's state every 100ms to JSON, replay exactly on reload. Tests that all layers deterministically compose.
  2. Multi-character scene: 10 copies of the rig in one scene, all animating. Find the performance breaking point on your machine.
  3. Mix + match pipelines: pull Mixamo clips (S3-08), drive face with MediaPipe (S3-09), physics hits from S2-03. Confirm layers stay consistent.

Season 3 complete 🎉

30 of 100 articles shipped.

Characters now: walk, talk, emote, react, survive hits, follow targets.

← back to the series

What's next — Season 4

Graphics Deep Dive. PBR from the math up. Advanced lighting, shadow techniques, SSR, volumetric fog, subsurface scattering, hair, ocean, NPR. PhD-level rendering with working demos.