Babylon.js Versus Three.js

The two solve the same problems with different defaults. Three.js and this chapter taught them side by side:

Where each chapter solved the same problem
Topic Three.js 30,815 (Three.js) Babylon.js 89,603 (Babylon.js)
Scene graph, axes Object3D, right-handed (5.2.1) TransformNode, left-handed (Scene Graph)
Render loop setAnimationLoop (5.2.4) runRenderLoop, observables (runRenderLoop)
Camera control OrbitControls add-on (5.8) Cameras with inputs built in (Cameras)
Geometry BufferGeometry (5.3) MeshBuilder, VertexData, CSG2 (Meshes and MeshBuilder)
PBR materials MeshStandardMaterial (5.4.2) PBRMaterial, plugins (The Standard and PBR Materials)
Shader graphs TSL in code (5.12.3) Node Material Editor 89,603 and JSON (The Node Material Editor)
Shadows Shadow maps; CSM add-on (5.5.2) CascadedShadowGenerator built in (Cascaded Shadows)
glTF GLTFLoader add-on (5.6.3) @babylonjs/loaders (glTF and Containers)
Animation AnimationMixer (5.7.2) Animation groups, blending (Animation)
Picking Raycaster (5.9.1) scene.pick, pointer observables (Actions and Picking)
GUI, physics Third-party (5.15.2) First-party GUI and Havok 516,349 (7.12-7.14)
WebGPU WebGPURenderer with TSL (5.13) WebGPUEngine, WGSL core shaders (WebGPUEngine Versus Engine)
Debugging Renderer info, DevTools (5.14.4) Inspector (Inspector and Tools)

Three.js is the smaller, more popular core: a one-box scene with orbit controls bundled to 556,288 bytes (139,045 gzipped) against 1,516,036 (351,593) for Babylon's scene of npm Packages, and npm 2,036 counted 15.8 million weekly downloads of three against 388,000 of @babylonjs/core (18 to 24 September 2026); GitHub 29 stars stand at 115,915 and 26,113. Babylon.js answers with breadth and stability: first-party physics, GUI, audio, XR and tools on one version number, no breaking changes between yearly majors (Release Cadence), where any rNNN may break (Releases and Versions), and a paid core team at Microsoft. Both are permissive, MIT against Apache-2.0 with its explicit patent grant. Pick Three.js when you assemble your own stack or want its examples; pick Babylon.js when you want the engine to arrive assembled.

Babylon.js and Three.js side by side on bundle size, weekly npm downloads and GitHub stars, each scaled to the largerHTMLLive
<!doctype html>
<style>
  body { margin: 0; background: #f7f3ec; font: 13px system-ui, sans-serif; color: #333; }
  canvas { width: 100%; max-width: 600px; height: 340px; 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>
// A small 3D bar chart: boxes on a floor, GUI labels under and above them
function createChart(canvas, { radius = 12, target = new BABYLON.Vector3(0, 1.2, 0), alpha = -Math.PI / 2 } = {}) {
  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', alpha, 1.25, radius, target, scene);
  camera.attachControl(canvas, true);
  new BABYLON.HemisphericLight('sky', new BABYLON.Vector3(0.4, 1, -0.7), scene);
  const ui = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI('ui');
  const text = (anchor, value, offsetY, { size = 11, color = '#333', bold = false } = {}) => {
    const block = new BABYLON.GUI.TextBlock('', value);
    Object.assign(block, { color, fontSize: size, resizeToFit: true, fontWeight: bold ? 'bold' : 'normal' });
    ui.addControl(block);
    block.linkWithMesh(anchor);
    block.linkOffsetY = offsetY;
    return block;
  };
  const bar = (x, height, hex, top, bottom, z = 0, width = 0.8) => {
    const box = BABYLON.MeshBuilder.CreateBox('bar', { width, depth: width, height: Math.max(height, 0.02) }, scene);
    box.position.set(x, Math.max(height, 0.02) / 2, z);
    box.material = new BABYLON.StandardMaterial(hex, scene);
    box.material.diffuseColor = BABYLON.Color3.FromHexString(hex);
    const head = new BABYLON.TransformNode('head', scene);
    head.position.set(x, height, z);
    const foot = new BABYLON.TransformNode('foot', scene);
    foot.position.set(x, 0, z);
    if (top) text(head, top, -14, { bold: true });
    if (bottom) text(foot, bottom, 22);
    return box;
  };
  const title = (value) => {
    const block = new BABYLON.GUI.TextBlock('', value);
    Object.assign(block, { color: '#555', fontSize: 14, height: '24px', top: '8px', verticalAlignment: 0 });
    ui.addControl(block);
  };
  engine.runRenderLoop(() => scene.render());
  addEventListener('resize', () => engine.resize());
  return { engine, scene, camera, ui, bar, text, title };
}

const { bar, text, title, scene } = createChart(document.getElementById('view'), { radius: 12.5, target: new BABYLON.Vector3(0, 1.3, 0) });
title('Babylon.js 9.28 (blue) and Three.js 0.186 (orange), September 2026');
// Each metric is scaled to its larger value, so the pairs compare, not the metrics
const METRICS = [
  ['bundle, gzipped', 351593, 139045, (v) => `${(v / 1000).toFixed(0)} KB`],
  ['npm per week', 388000, 15800000, (v) => v >= 1e6 ? `${(v / 1e6).toFixed(1)} M` : `${(v / 1000).toFixed(0)} k`],
  ['GitHub stars', 26113, 115915, (v) => v.toLocaleString('en')],
];
METRICS.forEach(([name, babylon, three, format], i) => {
  const x = (i - 1) * 3.9, max = Math.max(babylon, three);
  bar(x - 0.5, 2.6 * babylon / max, '#1f5f8b', format(babylon));
  bar(x + 0.5, 2.6 * three / max, '#e09a10', format(three));
  const foot = new BABYLON.TransformNode('foot', scene);
  foot.position.x = x;
  text(foot, name, 22, { size: 12 });
});
/* Beyond the numbers: Babylon.js ships physics, GUI, audio, XR and tools first-party on one
   version, with no breaking changes between yearly majors; Three.js keeps a smaller core that
   you assemble with add-ons, and any rNNN release may break. MIT (Three.js) and Apache-2.0
   (Babylon.js, with an explicit patent grant) are both permissive. */
</script>