All three JavaScript engines are free, permissive open source; their differences lie in the fine print, the bytes and the upgrade treadmill:
| Engine | Licence | Release model | One-box app, gzipped |
|---|---|---|---|
| Babylon.js 9.28.0 89,603 | Apache-2.0 | Yearly major, weekly minors (Release Cadence) | 351,593 bytes |
| Three.js 0.186.1 30,815 | MIT | rNNN every few weeks, may break (5.1.1) | 139,045 bytes |
| PlayCanvas 2.22.4 62,178 | MIT; paid Editor tiers | Semantic versions, 2.x since August 2024 | 517,699 bytes |
Licences. MIT asks you to keep the copyright notice. Apache-2.0 also asks you to keep any NOTICE file and mark modified files, and it grants a patent licence from every contributor, which ends for anyone who sues over patents in the work; legal departments often prefer that explicit grant. Check the add-ons too: Havok 516,349 's engine is MIT (Havok), Rapier Apache-2.0, and assets carry their own terms: Babylon's asset library is CC BY 4.0, Poly Haven 82,537 's environments CC0 (HDR and .env).
Size. Tree-shaking helps Three.js most, since its add-ons are separate files; Babylon's ES modules shrink a basic scene to about a sixth of the UMD build (270 KB against 1.68 MB, both Brotli) if you import each class from its own file (npm Packages); PlayCanvas's engine barely shrinks. Measure your real feature set, not a box, as The Three.js Version does.
Long-term support. None of them offers an LTS branch. Babylon.js patches only its newest minor but keeps its API stable within a major; Three.js may break in any release and relies on its Migration Guide; PlayCanvas published its last 1.x release, 1.77.0, in March 2025. Pin exact versions, keep all @babylonjs/* packages on one number, and schedule upgrades. For Unity-style LTS releases, look at the game engines of Unity, Unreal, Godot.
<!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: 11.5, target: new BABYLON.Vector3(0, 1.4, 0) });
title('One-box app, gzipped, 26 September 2026');
const ENGINES = [
['Babylon.js 9.28.0', 351593, '#1f5f8b', 'Apache-2.0 (patent grant)', 'yearly major, weekly minors'],
['Three.js 0.186.1', 139045, '#e09a10', 'MIT', 'rNNN releases may break'],
['PlayCanvas 2.22.4', 517699, '#b5452f', 'MIT; paid Editor tiers', 'semver, 2.x since 2024'],
];
ENGINES.forEach(([name, bytes, hex, licence, releases], i) => {
const x = (i - 1) * 3.6;
bar(x, bytes / 150000, hex, `${(bytes / 1000).toFixed(0)} KB`, '', 0, 1.2);
const foot = new BABYLON.TransformNode('foot', scene);
foot.position.x = x;
text(foot, name, 22, { size: 12, bold: true });
text(foot, licence, 40);
text(foot, releases, 56, { color: '#666', size: 10 });
});
/* None offers an LTS branch: Babylon.js patches only its newest minor but keeps the API stable
within a major. Pin exact versions and keep every @babylonjs/* package on one number.
Deep imports shrink Babylon's basic scene to about a sixth of the UMD build (270 KB against
1.68 MB, Brotli). */
</script>