vs AnimationMixer

Babylon's Animation System Compared with Three.js's AnimationMixer

Both engines load the same glTF clips and blend them by weight. Three.js 30,815 (The Animation System) separates data from playback and makes you advance time; Babylon 89,603 's scene advances everything in render(), in frames:

Babylon's animation system beside Three.js's
Concept Babylon.js Three.js
One animated property Animation (keys by frame) KeyframeTrack (times in seconds)
A clip of many properties AnimationGroup AnimationClip
A playing instance Animatable, or the group itself AnimationAction from mixer.clipAction()
Advancing time Automatic in scene.render() mixer.update(delta) every frame
Weights and crossfades weight, enableBlending setEffectiveWeight(), crossFadeTo()

Three.js's explicit update() makes fixed-step or server-side animation simpler.

One keyframe track played three ways: advanced by scene.render(), stepped by hand like mixer.update(), and crossfaded by weightHTMLLive
<!doctype html>
<style>
  body { margin: 0; background: #f7f3ec; font: 13px system-ui, sans-serif; color: #333; }
  canvas { width: 100%; max-width: 600px; height: 310px; display: block; touch-action: none; outline: none; }
</style>
<canvas id="view"></canvas>
<script src="https://cdn.jsdelivr.net/npm/babylonjs@9.28.0/babylon.js"></script>
<script src="https://cdn.jsdelivr.net/npm/babylonjs-gui@9.28.0/babylon.gui.min.js"></script>
<script>
const canvas = document.getElementById('view');
const engine = new BABYLON.Engine(canvas, true);
const scene = new BABYLON.Scene(engine);
scene.clearColor = BABYLON.Color4.FromHexString('#f7f3ecff');
const camera = new BABYLON.ArcRotateCamera('cam', -Math.PI / 2, 1.2, 6, new BABYLON.Vector3(0, 0.7, 0), scene);
camera.attachControl(canvas, true);
new BABYLON.HemisphericLight('sky', new BABYLON.Vector3(0.3, 1, -0.5), scene);
const ui = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI('ui');

// The shared data: an Animation is Babylon's KeyframeTrack, with keys in frames, not seconds
const hop = new BABYLON.Animation('hop', 'position.y', 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT,
  BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
hop.setKeys([{ frame: 0, value: 0.5 }, { frame: 30, value: 1.4 }, { frame: 60, value: 0.5 }]);
const spin = new BABYLON.Animation('spin', 'rotation.y', 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT,
  BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
spin.setKeys([{ frame: 0, value: 0 }, { frame: 60, value: Math.PI * 2 }]);

const makeBook = (x, hex, text) => {
  const book = BABYLON.MeshBuilder.CreateBox('book', { width: 0.6, height: 0.9, depth: 0.15 }, scene);
  book.position.set(x, 0.5, 0);
  book.material = new BABYLON.StandardMaterial(hex, scene);
  book.material.diffuseColor = BABYLON.Color3.FromHexString(hex);
  const base = new BABYLON.TransformNode('base', scene);
  base.position.x = x;
  const label = new BABYLON.GUI.TextBlock('', text);
  Object.assign(label, { color: '#333', fontSize: 11, resizeToFit: true });
  ui.addControl(label);
  label.linkWithMesh(base);
  label.linkOffsetY = 40;
  return book;
};
// 1. Babylon: the scene advances every Animatable inside scene.render()
const auto = makeBook(-2.1, '#1f5f8b', 'beginDirectAnimation\nadvanced by scene.render()');
scene.beginDirectAnimation(auto, [hop], 0, 60, true);

// 2. Three.js style: you own the clock and call update(delta), here with animation.evaluate()
const manual = makeBook(0, '#3f7d3a', 'evaluate(frame) by hand\nlike mixer.update(delta)');
let frame = 0;
scene.onBeforeRenderObservable.add(() => {
  frame = (frame + engine.getDeltaTime() / 1000 * 60) % 60;   // seconds -> frames
  manual.position.y = hop.evaluate(frame);
});

// 3. Weights: two animatables on one target blend; ramp the weights for a crossfade
const blended = makeBook(2.1, '#b5452f', 'weight 1 -> 0 and 0 -> 1\nlike crossFadeTo()');
const a = scene.beginDirectAnimation(blended, [hop], 0, 60, true);
const b = scene.beginDirectAnimation(blended, [spin], 0, 60, true);
scene.onBeforeRenderObservable.add(() => {
  const w = 0.5 + 0.5 * Math.sin(performance.now() / 1200);
  a.weight = w;                                      // hop fades out as spin fades in
  b.weight = 1 - w;
});
engine.runRenderLoop(() => scene.render());
addEventListener('resize', () => engine.resize());
</script>