Animation System
Babylon.js provides a powerful animation system that enables smooth transitions and movements for all scene objects. From simple property changes to complex character animations, the framework offers comprehensive tools for bringing your 3D world to life.
Create animations by defining keyframes for any object property:
// Create a box to animate
const box = BABYLON.MeshBuilder.CreateBox("box", {size: 1}, scene);
// Create an animation for position
const animation = new BABYLON.Animation(
"positionAnimation", // Name
"position.y", // Property to animate
30, // Frames per second
BABYLON.Animation.ANIMATIONTYPE_FLOAT, // Type of value
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE // Loop mode
);
// Define keyframes
const keyFrames = [];
keyFrames.push({ frame: 0, value: 0 }); // Start at y=0
keyFrames.push({ frame: 30, value: 3 }); // Move to y=3 at frame 30
keyFrames.push({ frame: 60, value: 0 }); // Back to y=0 at frame 60
// Assign keyframes to animation
animation.setKeys(keyFrames);
// Attach animation to the box
box.animations = [animation];
// Start the animation
scene.beginAnimation(box, 0, 60, true); // true = loop
Add natural motion with easing functions:
// Create an easing function
const easingFunction = new BABYLON.CircleEase();
easingFunction.setEasingMode(BABYLON.EasingFunction.EASINGMODE_EASEINOUT);
// Apply to animation
animation.setEasingFunction(easingFunction);
Available easing types: BABYLON.BackEase, BABYLON.BounceEase, BABYLON.CircleEase, BABYLON.CubicEase, BABYLON.ElasticEase, BABYLON.ExponentialEase, BABYLON.PowerEase, BABYLON.QuadraticEase, BABYLON.QuarticEase, BABYLON.QuinticEase, BABYLON.SineEase
Smoothly transition between animations:
// Create a second animation
const animation2 = new BABYLON.Animation(
"scaleAnimation",
"scaling",
30,
BABYLON.Animation.ANIMATIONTYPE_VECTOR3,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
// Define keyframes for scaling
const scaleKeyFrames = [];
scaleKeyFrames.push({ frame: 0, value: new BABYLON.Vector3(1, 1, 1) });
scaleKeyFrames.push({ frame: 30, value: new BABYLON.Vector3(2, 0.5, 2) });
scaleKeyFrames.push({ frame: 60, value: new BABYLON.Vector3(1, 1, 1) });
animation2.setKeys(scaleKeyFrames);
// Start with position animation
const animatable = scene.beginAnimation(box, 0, 60, true);
// Later, blend to scale animation
setTimeout(() => {
box.animations = [animation2];
scene.beginAnimation(box, 0, 60, true, 1.0); // 1.0 = blend speed
}, 3000);
Synchronize multiple animations:
// Create an animation group
const animationGroup = new BABYLON.AnimationGroup("myGroup");
// Add animations to the group
animationGroup.addTargetedAnimation(animation, box);
// Create and add rotation animation
const rotationAnimation = new BABYLON.Animation(
"rotationAnimation",
"rotation.y",
30,
BABYLON.Animation.ANIMATIONTYPE_FLOAT,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
const rotationKeys = [];
rotationKeys.push({ frame: 0, value: 0 });
rotationKeys.push({ frame: 60, value: Math.PI * 2 });
rotationAnimation.setKeys(rotationKeys);
animationGroup.addTargetedAnimation(rotationAnimation, box);
// Control the animation group
animationGroup.play(true); // true = loop
// animationGroup.pause();
// animationGroup.stop();
// animationGroup.reset();
// Adjust speed
animationGroup.speedRatio = 0.5; // Half speed
// Animation group events
animationGroup.onAnimationEndObservable.add(() => {
console.log("Animation group completed");
});
Work with rigged character animations:
// Import a rigged model
BABYLON.SceneLoader.ImportMesh("", "models/", "character.glb", scene, (meshes, particleSystems, skeletons, animationGroups) => {
const character = meshes[0];
const skeleton = skeletons[0];
// Play an animation by name
const idleAnim = animationGroups.find(a => a.name === "Idle");
const walkAnim = animationGroups.find(a => a.name === "Walking");
const runAnim = animationGroups.find(a => a.name === "Running");
if (idleAnim) idleAnim.play(true);
Switch between animations with blending:
// Later, switch animations with blending
document.getElementById("walk").addEventListener("click", () => {
idleAnim.stop();
walkAnim.play(true);
walkAnim.speedRatio = 1.0;
});
document.getElementById("run").addEventListener("click", () => {
walkAnim.stop();
runAnim.play(true);
runAnim.speedRatio = 1.5;
});
Access skeleton bones for fine control:
// Access skeleton bones for fine control
const rightArm = skeleton.bones.find(b => b.name === "RightArm");
if (rightArm) {
// Override bone rotation
rightArm.rotation.z += Math.PI / 8;
}
});
Animate between different mesh shapes:
// Import a model with morph targets
BABYLON.SceneLoader.ImportMesh("", "models/", "face.glb", scene, (meshes) => {
const face = meshes[0];
// Access morph targets
const morphTargetManager = face.morphTargetManager;
if (morphTargetManager) {
// Get specific morph targets
const smileTarget = morphTargetManager.getTarget(0);
const frownTarget = morphTargetManager.getTarget(1);
const blinkTarget = morphTargetManager.getTarget(2);
// Animate morph target influences
let time = 0;
scene.onBeforeRenderObservable.add(() => {
time += scene.getEngine().getDeltaTime() / 1000;
// Smile-frown cycle
smileTarget.influence = Math.sin(time * 0.5) * 0.5 + 0.5;
frownTarget.influence = Math.sin(time * 0.5 + Math.PI) * 0.5 + 0.5;
// Occasional blink
blinkTarget.influence = Math.pow(Math.sin(time * 3), 16);
});
}
});
Trigger events at specific points in animations:
// Create an animation with events
const jumpAnimation = new BABYLON.Animation(
"jumpAnimation",
"position.y",
60,
BABYLON.Animation.ANIMATIONTYPE_FLOAT,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
const jumpKeys = [];
jumpKeys.push({ frame: 0, value: 0 });
jumpKeys.push({ frame: 30, value: 5 });
jumpKeys.push({ frame: 60, value: 0 });
jumpAnimation.setKeys(jumpKeys);
// Add events at specific frames
jumpAnimation.addEvent(new BABYLON.AnimationEvent(
10, // Frame number
function() {
// Event callback
console.log("Starting to jump");
// Play sound
const jumpSound = new BABYLON.Sound("jump", "sounds/jump.mp3", scene);
jumpSound.play();
}
));
jumpAnimation.addEvent(new BABYLON.AnimationEvent(
50, // Frame number
function() {
console.log("Landing");
// Play landing sound
const landSound = new BABYLON.Sound("land", "sounds/land.mp3", scene);
landSound.play();
}
));
// Apply and play
box.animations = [jumpAnimation];
scene.beginAnimation(box, 0, 60, true);
Create animations directly in code:
// Animate in the render loop
let time = 0;
scene.onBeforeRenderObservable.add(() => {
time += scene.getEngine().getDeltaTime() / 1000;
// Circular motion
sphere.position.x = Math.cos(time) * 5;
sphere.position.z = Math.sin(time) * 5;
// Bobbing motion
sphere.position.y = 2 + Math.sin(time * 2) * 0.5;
// Continuous rotation
box.rotation.y += 0.01;
box.rotation.x = Math.sin(time) * 0.2;
});
Babylon.js 7 includes enhanced animation capabilities:
// Animation curve editor integration
const curveEditor = new BABYLON.AnimationCurveEditor();
curveEditor.addAnimation(animation);
// Animation state machine
const stateMachine = new BABYLON.AnimationStateMachine("characterStates", scene);
// Define states
const idleState = new BABYLON.AnimationState("idle", idleAnim);
const walkState = new BABYLON.AnimationState("walk", walkAnim);
const runState = new BABYLON.AnimationState("run", runAnim);
// Add states to state machine
stateMachine.addState(idleState);
stateMachine.addState(walkState);
stateMachine.addState(runState);
// Define transitions between states
stateMachine.addTransition(idleState, walkState, "startWalking");
stateMachine.addTransition(walkState, runState, "startRunning");
stateMachine.addTransition(runState, walkState, "slowDown");
stateMachine.addTransition(walkState, idleState, "stop");
// Start with idle state
stateMachine.setCurrentState("idle");
// Later, trigger transitions
document.getElementById("walkButton").addEventListener("click", () => {
stateMachine.trigger("startWalking");
});
document.getElementById("runButton").addEventListener("click", () => {
stateMachine.trigger("startRunning");
});