Every feature of the store has a Three.js 30,815 answer, but not always in the box:
| Store feature | Babylon.js 89,603 (section) | Three.js route |
|---|---|---|
| Walk camera with collisions | UniversalCamera (Walkthrough Camera) | PointerLockControls; collisions by hand |
| Window and door cut-outs | CSG2 (CSG2) | three-bvh-csg 0.0.18 (MIT) |
| Cascaded shadows | Built in (Room Lighting) | CSM add-on |
| HDR environment, skybox | .env, default skybox (HDR Room Lighting) | HDRLoader, PMREMGenerator |
| Spine highlight | Node material (Spine Highlight) | TSL (5.12.4) |
| Details panel | 2D GUI (BookNest's Book Details Panel) | HTML overlay, CSS2DRenderer |
| Floating 3D panel | 3D GUI (Info Panel) | three-mesh-ui 6.5.4, last release March 2023 |
| Falling books | Havok 516,349 (Falling Books) | Rapier 0.21.0 (Apache-2.0) |
| Dust motes | Particle system (Dust Motes) | Points updated by hand |
| Storefront look | Rendering pipeline (Storefront Look) | EffectComposer passes |
| Debugging | Inspector (Debugging the Scene) | lil-gui 1,638 , browser extensions |
| Ambient sound | Audio engine v2 (Ambient Sound) | PositionalAudio |
| VR visit | Default XR experience (VR Bookstore) | VRButton; teleporting by hand |
To price it in bytes, two entry files constructed each feature once, one with three 0.186.1 and its add-ons plus the three packages above, one with deep imports from @babylonjs/* 9.28.0, and esbuild 126 bundled both (they were bundled, not run). Without physics, Three.js's set came to 948,983 bytes (252,130 gzipped) and Babylon's to 5,010,034 (1,132,483). Physics nearly evens the score: Rapier's -compat build embeds its WebAssembly and adds 4,334,647 bytes (1,651,923), bringing Three.js to 5,285,415 (1,904,855), while Havok adds 34,901 bytes of JavaScript and a 2,094,563-byte .wasm file (658,471 gzipped), for about 1.80 MB gzipped in all.
So the store would cost about the same to download in Three.js, less if you drop physics, and more to build: three third-party packages, one of them dormant, plus teleportation, collisions and particles written by hand, and no Inspector. That trade, a leaner core against an engine that arrives assembled, is the whole comparison in miniature.
<!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: 9.5, target: new BABYLON.Vector3(0, 1.5, 0) });
title('The store\'s features, bundled with esbuild, gzipped');
const MB = 1e6, SCALE = 1.4; // metres per megabyte
// [name, features without physics, physics, colors]
const STACKS = [
['Three.js + add-ons', 252130, 1651923, ['#e09a10', '#f3c56b'], 'Rapier -compat (embedded wasm)'],
['Babylon.js deep imports', 1132483, 658471, ['#1f5f8b', '#7fa7c6'], 'Havok JS + .wasm'],
];
STACKS.forEach(([name, features, physics, [dark, light], physicsName], i) => {
const x = (i - 0.5) * 3.2;
const lower = bar(x, features / MB * SCALE, dark, '', '', 0, 1.3);
const upper = BABYLON.MeshBuilder.CreateBox('physics', { width: 1.3, depth: 1.3, height: physics / MB * SCALE }, scene);
upper.position.set(x, (features + physics / 2) / MB * SCALE, 0);
upper.material = new BABYLON.StandardMaterial(light, scene);
upper.material.diffuseColor = BABYLON.Color3.FromHexString(light);
const top = new BABYLON.TransformNode('top', scene);
top.position.set(x, (features + physics) / MB * SCALE, 0);
text(top, `${((features + physics) / MB).toFixed(2)} MB in all`, -14, { bold: true });
text(upper, `+ ${physicsName}\n${(physics / 1000).toFixed(0)} KB`, 0, { size: 10 });
text(lower, `${(features / 1000).toFixed(0)} KB`, 0, { size: 10, color: 'white' });
const foot = new BABYLON.TransformNode('foot', scene);
foot.position.x = x;
text(foot, name, 22, { size: 12 });
});
/* About the same download either way, less in Three.js without physics, but more to build
there: three third-party packages (one dormant) plus collisions, teleporting and particles
written by hand, and no Inspector. */
</script>