The two solve the same problems with different defaults. Three.js and this chapter taught them side by side:
| 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.
<!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>