Animating Characters in Babylon.js
A static character in a 3D game feels lifeless. Animation is what turns a mesh into a character, giving it personality, weight, and readable intent. Walking, running, idling, jumping, attacking, taking damage, and dying all communicate the character's state to the player without any text or UI. Babylon.js provides the runtime systems to play, blend, and manage these animations, while the actual motion data comes from 3D tools like Blender, Maya, or motion capture.
Step 1: Import a Rigged Character
A rigged character is a mesh with a skeleton, which is a hierarchy of bones that deform the mesh when they move. In Blender, this is an Armature with weight-painted vertex groups. When exported to glTF, the skeleton becomes a collection of nodes with joint relationships, and each animation clip becomes a set of keyframes targeting those joints.
Load the character with SceneLoader.ImportMeshAsync("", "/models/", "character.glb", scene). The result object contains four arrays: meshes (the visible geometry), skeletons (the bone hierarchies), animationGroups (the named animation clips), and particleSystems (if any). The first mesh is typically a root transform node, with the actual character mesh as a child.
Verify the import by checking that result.skeletons.length is greater than zero and that result.animationGroups.length matches the number of clips you exported. If the skeleton is missing, the glTF file was exported without armature data. If animations are missing, check that "Include Animations" was enabled in the export settings. The Babylon.js Sandbox at sandbox.babylonjs.com can preview the file and show all included data.
Step 2: Play Animation Groups
Each animation clip from the glTF file becomes an AnimationGroup. Access them by name or index from the result: const idle = result.animationGroups.find(g => g.name === "Idle"). Stop all animations first with result.animationGroups.forEach(g => g.stop()), then start the one you want with idle.start(true). The boolean parameter enables looping.
Control playback with animGroup.speedRatio to change speed (2.0 is double speed, 0.5 is half speed), animGroup.goToFrame(n) to jump to a specific frame, and animGroup.pause() and animGroup.play() for pause and resume. The onAnimationEndObservable fires when a non-looping animation reaches its last frame, which is useful for triggering transitions after an attack animation finishes.
Animation groups operate on targeted animations internally. Each group contains multiple TargetedAnimation objects that animate specific bones. When you play a group, all its targeted animations play in sync. This means a "Walk" group simultaneously animates the hips, legs, arms, spine, and head bones to produce a coordinated walking motion.
Step 3: Blend Between Animations
Instant animation switching causes visible popping where the character jumps from one pose to another. Blending eliminates this by gradually transitioning between animations over a short duration, typically 100 to 300 milliseconds. In Babylon.js, you control blending through animation weights.
Each animation group has a weight property from 0.0 (fully inactive) to 1.0 (fully active). To transition from idle to walk, gradually decrease the idle weight from 1.0 to 0.0 while increasing the walk weight from 0.0 to 1.0 over a set number of frames. Both animations play simultaneously during the transition, and the final pose is a weighted average of the two.
Implement this with a simple crossfade function that runs on each frame via scene.onBeforeRenderObservable. Track the current animation, the target animation, and the blend progress. On each frame, advance the blend progress by the time delta divided by the blend duration. When the blend reaches 1.0, stop the old animation and set the new animation's weight to 1.0. This creates smooth, natural-looking transitions between any two animations.
Additive blending is another technique where an animation layer adds to the base pose rather than replacing it. A breathing animation can be layered on top of any base animation (idle, walk, run) to add subtle chest movement. Upper body actions like waving or pointing can be layered over lower body locomotion. Babylon.js supports this through the animation weight system by enabling the isAdditive property on specific animation groups.
Step 4: Build an Animation State Machine
A state machine organizes character animations into states (idle, walk, run, jump, fall, attack) with defined transitions between them. The machine tracks the current state and switches to a new state when conditions are met: the player presses the forward key (idle to walk), the player releases the key (walk to idle), the player presses jump while grounded (walk to jump), and so on.
Each state maps to an animation group and specifies which states it can transition to. The transition definition includes the blend duration and any conditions that must be true for the transition to fire. A simple implementation uses an object where keys are state names and values contain the animation group reference, looping behavior, and an array of possible transitions with their target state and condition function.
The state machine update runs on each frame. It evaluates the current state's transitions in priority order, checking each condition against the current input and game state. When a condition is true, it triggers the crossfade blend to the new state's animation and updates the current state tracker. Guard against re-entering the same state (which would restart the blend unnecessarily) by checking whether the target state is different from the current one.
For complex characters with many states, consider grouping states into layers. The locomotion layer handles movement animations, the upper body layer handles weapon and interaction animations, and the facial layer handles expressions. Each layer runs its own state machine and blends independently, with the results combined additively. This is the same architecture used in Unity's Animator Controller and Unreal's Animation Blueprints.
Step 5: Use Morph Targets for Facial Animation
Morph targets (also called blend shapes or shape keys) deform mesh vertices to create facial expressions. Each morph target defines vertex offsets for a specific expression: "smile" moves mouth corners up, "blink" closes the eyelids, "mouthOpen" drops the jaw. The glTF format supports morph targets natively, and Blender exports them as Shape Keys.
Access morph targets through the mesh's morphTargetManager property. Each target has an influence value from 0.0 (no effect) to 1.0 (full effect). Set morphTargetManager.getTarget(0).influence = 0.5 to apply a target at 50% strength. Multiple targets can be active simultaneously, so you can combine a smile with raised eyebrows and squinted eyes for a complex expression.
For real-time facial animation driven by speech, map viseme identifiers to morph target combinations. The viseme "AA" (open mouth) maps to the mouthOpen target at 0.8 and jawOpen at 0.6. The viseme "EE" maps to a wide mouth shape. During playback, interpolate between viseme poses based on timing data from a speech synthesis or speech recognition service. This creates the appearance of the character speaking the words, and it is the foundation for talking AI characters.
Step 6: Retarget Animations Across Characters
Animation retargeting applies motion from one skeleton to a different skeleton. This is valuable when you have a library of animations (from Mixamo, motion capture, or a dedicated animator) and want to reuse them across characters with different proportions. A walk cycle created for a tall character should work on a short character, adjusting for the difference in leg length.
Babylon.js provides animation retargeting through the AnimationGroup.clone() method with target remapping. The process involves creating a mapping between source bone names and target bone names, then applying the source animation's transforms to the target skeleton with proportional adjustments. If both characters follow the same naming convention (Hips, Spine, LeftUpperArm, LeftLowerArm, etc.), the mapping can be automated.
Mixamo is a popular source of free character animations that use standardized bone naming. Characters rigged and animated on Mixamo can be downloaded in FBX format and converted to glTF for use in Babylon.js. The standardized skeleton makes retargeting between Mixamo characters straightforward, though you may need to adjust root motion (the character's movement through space) for each specific game's movement system.
Good character animation in Babylon.js requires three things: properly rigged models with clean animation clips, a blending system that transitions smoothly between states, and a state machine that maps game input to the correct animation. The engine provides the runtime tools; your job is to organize them into a responsive, readable character controller.