Sizes are the minified JavaScript each page loads, compressed with gzip here (26 September 2026); assets, workers and fonts come on top:
| Library (version) | License | Gzipped JS | WebGPU path | Best for |
|---|---|---|---|---|
| twgl.js 3,005 (7.0.0) | MIT | 29 KB | No | Raw WebGL with less typing |
| regl 5,583 (2.1.1) | MIT | 28 KB | No (WebGL 1) | Custom data-driven shaders |
| OGL 4,660 (1.0.11) | Unlicense | 39 KB | No | Light 3D and shader effects |
| PixiJS 313,967 (8.21.0) | MIT | 233 KB | Yes | 2D games, sprites |
| MapLibre GL JS 34,367 (6.11.2) | BSD-3-Clause | 302 KB | No | Vector maps |
| deck.gl 492,297 (9.4.0) | MIT | 575 KB | Separate luma.gl package | Large data layers |
| CesiumJS 76,128 (1.145.0) | Apache-2.0 | 1,753 KB | No | Globes, 3D Tiles |
| Three.js 30,815 (0.186.1) | MIT | 194 KB | Yes, WebGL 2 fallback | General 3D (Three.js) |
| Babylon.js 89,603 (9.28.0) | Apache-2.0 | 1,848 KB (UMD) | Yes, separate engine | 3D apps, games (Babylon.js) |
| PlayCanvas 62,178 engine (2.22.4) | MIT | 632 KB | Yes | Games, editor workflow |
All ten are permissively licensed open source; the costs lie in services such as Cesium ion or MapTiler 21,580 tiles, the PlayCanvas Editor, Unity 2,152 above its revenue threshold and Sketchfab 2,761 's paid tiers.
<!doctype html>
<style>
body { margin: 0; font: 11px system-ui, sans-serif; background: #f7f4ee; color: #333; }
.wrap { position: relative; max-width: 600px; }
canvas { display: block; width: 100%; }
.lbl { position: absolute; transform: translateY(-50%); white-space: nowrap; font-size: 10.5px; }
.key { max-width: 600px; margin: 4px 8px; }
.key span { display: inline-block; width: 10px; height: 10px; margin: 0 4px 0 12px; vertical-align: -1px; }
</style>
<div class="wrap"><canvas id="c" width="600" height="340"></canvas></div>
<div class="key"><span style="background:#2a9d8f"></span>WebGPU path <span style="background:#e09a10"></span>separate package or engine
<span style="background:#8a8378"></span>WebGL only</div>
<script>
// [library (version), license, gzipped KB, WebGPU: 2 yes, 1 separate, 0 no, best for] (npm and jsDelivr, 26 September 2026)
const rows = [['twgl.js 7.0.0', 'MIT', 29, 0, 'raw WebGL, less typing'], ['regl 2.1.1', 'MIT', 28, 0, 'data-driven shaders'],
['OGL 1.0.11', 'Unlicense', 39, 0, 'light 3D, effects'], ['Three.js 0.186.1', 'MIT', 194, 2, 'general 3D'],
['PixiJS 8.21.0', 'MIT', 233, 2, '2D games, sprites'], ['MapLibre GL 6.11.2', 'BSD-3', 302, 0, 'vector maps'],
['deck.gl 9.4.0', 'MIT', 575, 1, 'large data layers'], ['PlayCanvas 2.22.4', 'MIT', 632, 2, 'games, editor'],
['CesiumJS 1.145.0', 'Apache-2.0', 1753, 0, 'globes, 3D Tiles'], ['Babylon.js 9.28.0', 'Apache-2.0', 1848, 1, '3D apps, games']];
const colours = [[0.54, 0.51, 0.47], [0.88, 0.6, 0.06], [0.16, 0.62, 0.56]];
const gl = document.getElementById('c').getContext('webgl2');
const wrap = document.querySelector('.wrap');
const left = 118, right = 420, top0 = 20, rowH = 30;
const x = (kb) => left + Math.log10(kb / 10) / Math.log10(3000 / 10) * (right - left); // 10 KB to 3,000 KB, log scale
gl.enable(gl.SCISSOR_TEST);
gl.clearColor(0.97, 0.96, 0.93, 1); gl.clear(gl.COLOR_BUFFER_BIT);
for (const kb of [10, 30, 100, 300, 1000, 3000]) { // grid lines, bottom-left origin for scissor
gl.scissor(Math.round(x(kb)), 340 - top0 - rows.length * rowH, 1, rows.length * rowH);
gl.clearColor(0.82, 0.79, 0.74, 1); gl.clear(gl.COLOR_BUFFER_BIT);
wrap.insertAdjacentHTML('beforeend', `<div class="lbl" style="left:${x(kb) / 6 - 2}%;top:${(top0 + rows.length * rowH + 10) / 3.4}%;color:#888">${kb >= 1000 ? kb / 1000 + ' MB' : kb + ' KB'}</div>`);
}
rows.forEach(([name, license, kb, webgpu, use], i) => {
const y = top0 + i * rowH;
gl.scissor(left, 340 - y - rowH + 6, Math.round(x(kb) - left), rowH - 12);
gl.clearColor(...colours[webgpu], 1); gl.clear(gl.COLOR_BUFFER_BIT);
const mid = (y + rowH / 2) / 3.4;
wrap.insertAdjacentHTML('beforeend', `<div class="lbl" style="left:1%;top:${mid}%"><b>${name}</b></div>` +
`<div class="lbl" style="left:${(x(kb) + 5) / 6}%;top:${mid}%">${kb.toLocaleString()} KB</div>` +
`<div class="lbl" style="left:${(right + 50) / 6}%;top:${mid}%;color:#666">${license}<br>${use}</div>`);
});
</script>