Library Comparison

Comparing the Libraries by License, Maturity and Use Case

Every library in this section is free and open source; the differences are maturity, fallback and fit:

WebGPU libraries compared (GitHub 29 , npm 2,036 and READMEs, 26 September 2026)
Library (version) License Maturity (September 2026) WebGL fallback Use it for
Three.js 30,815 (0.186.1) MIT Mature; about 115,900 stars Yes, built in 3D scenes (Three.js)
Babylon.js 89,603 (9.28.0) Apache-2.0 Mature; about 26,100 stars Yes, separate engine 3D apps, games (Babylon.js)
PixiJS 313,967 (8.21.0) MIT Mature; about 48,200 stars Yes, automatic 2D games and sprites
Orillusion (0.9.2) MIT Beta, not for commercial use No Experiments
use.gpu (0.20.0) MIT Alpha, GitLab 377 No Reactive GPU plots
TypeGPU 320,420 (0.12.6) MIT Pre-1.0; about 3,200 stars No Typed raw WebGPU
wgpu-matrix 481 (3.4.2) MIT Stable, small Not needed Matrix math
ONNX Runtime Web 231,552 (1.30.0) MIT Mature (Microsoft) CPU WebAssembly Running ONNX models
Transformers.js 1,113 (4.3.0) Apache-2.0 Mature; about 16,300 stars CPU WebAssembly Hub models by task

Commercial tools sit on the same stack rather than replacing it: Unity 6 2,152 's Web builds offer a WebGPU backend (marked experimental through the 6.5 manual, no longer in 6.6), and hosted inference APIs take over when a model is too large. For BookNest, the choice would be Three.js or Babylon.js for its 3D store, PixiJS for a 2D game, and Transformers.js for search by meaning, with raw WebGPU (this chapter's code) kept for custom compute.

WebGPU libraries compared: GitHub stars as bars, with licence, maturity, WebGL fallback and best use for eachHTMLLive
<!doctype html>
<style>
  body { margin: 0; background: #f7f4ee; font: 14px system-ui, sans-serif; }
  .stage { position: relative; width: 100%; max-width: 600px; }
  .stage canvas { display: block; width: 100%; }
  .stage canvas + canvas { position: absolute; inset: 0; pointer-events: none; }
</style>
<div class="stage">
  <canvas id="view" width="600" height="390"></canvas>
  <canvas id="labels" width="600" height="390"></canvas>
</div>
<script>
const canvas = document.getElementById('view');
const ink = document.getElementById('labels').getContext('2d');

function showMessage(text) {                     // 2D fallback when WebGPU is missing
  const ctx = canvas.getContext('2d');
  ctx.fillStyle = '#fbeaea'; ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = '#8a2b2b'; ctx.font = '18px system-ui, sans-serif'; ctx.textAlign = 'center';
  ctx.fillText(text, canvas.width / 2, canvas.height / 2);
}

// name (version), licence, stars in thousands (null: not given), maturity (0 alpha .. 3 mature), fallback, use
const libs = [
  ['Three.js 0.186.1', 'MIT', 115.9, 3, 'yes, built in', '3D scenes'],
  ['PixiJS 8.21.0', 'MIT', 48.2, 3, 'yes, automatic', '2D games, sprites'],
  ['Babylon.js 9.28.0', 'Apache-2.0', 26.1, 3, 'yes, separate engine', '3D apps, games'],
  ['Transformers.js 4.3.0', 'Apache-2.0', 16.3, 3, 'CPU WebAssembly', 'Hub models by task'],
  ['ONNX Runtime Web 1.30.0', 'MIT', null, 3, 'CPU WebAssembly', 'ONNX models'],
  ['TypeGPU 0.12.6', 'MIT', 3.2, 1, 'no', 'typed raw WebGPU'],
  ['wgpu-matrix 3.4.2', 'MIT', null, 2, 'not needed', 'matrix math'],
  ['Orillusion 0.9.2', 'MIT', null, 1, 'no', 'experiments'],
  ['use.gpu 0.20.0', 'MIT', null, 0, 'no', 'reactive GPU plots']];
const maturityColor = [[0.80, 0.35, 0.25], [0.88, 0.60, 0.15], [0.55, 0.70, 0.30], [0.16, 0.56, 0.30]];
const maturityName = ['alpha', 'beta / pre-1.0', 'stable, small', 'mature'];

async function main() {
  const adapter = await navigator.gpu?.requestAdapter();
  if (!adapter) return showMessage('WebGPU is not available in this browser');
  const device = await adapter.requestDevice();
  const context = canvas.getContext('webgpu');
  const format = navigator.gpu.getPreferredCanvasFormat();
  context.configure({ device, format });

  const boxes = [];
  libs.forEach(([, , stars, maturity], i) => {
    const y = 40 + i * 36;
    boxes.push([8, y - 4, 584, 32, ...(i % 2 ? [0.95, 0.94, 0.91] : [1, 1, 1]), 6]);
    boxes.push([170, y + 6, 10, 10, ...maturityColor[maturity], 5]);              // maturity dot
    if (stars) boxes.push([188, y + 3, stars / 120 * 115, 16, 0.20, 0.45, 0.75, 3]);   // stars bar
  });
  ink.font = 'bold 12px system-ui, sans-serif'; ink.fillStyle = '#222';
  ink.fillText('library', 12, 26); ink.fillText('maturity, GitHub stars', 170, 26); ink.fillText('licence', 360, 26); ink.fillText('WebGL fallback / use it for', 440, 26);
  libs.forEach(([name, licence, stars, maturity, fallback, use], i) => {
    const y = 40 + i * 36;
    ink.font = 'bold 11.5px system-ui, sans-serif'; ink.fillStyle = '#222'; ink.fillText(name, 14, y + 15);
    ink.font = '11px system-ui, sans-serif'; ink.fillStyle = '#444';
    ink.fillText(stars ? `${stars}k` : maturityName[maturity], stars ? 194 + stars / 120 * 115 : 190, y + 15);
    ink.fillText(licence, 360, y + 15);
    ink.fillText(fallback, 440, y + 9); ink.fillStyle = '#1f4f8a'; ink.fillText(use, 440, y + 23);
  });
  ink.font = '11px system-ui, sans-serif'; ink.fillStyle = '#555';
  ink.fillText('GitHub, npm and READMEs, 26 September 2026. All free and open source; they differ in maturity, fallback and fit.', 12, 372);
  ink.fillText('BookNest: Three.js or Babylon.js for the 3D store, PixiJS for a 2D game, Transformers.js for search by meaning.', 12, 386);

  const module = device.createShaderModule({ code: `
    struct Box { rect: vec4f, style: vec4f }          // style: r, g, b, corner radius
    @group(0) @binding(0) var<storage> boxes: array<Box>;
    struct Out { @builtin(position) pos: vec4f, @location(0) local: vec2f,
                 @location(1) @interpolate(flat) i: u32 }
    @vertex fn vs(@builtin(vertex_index) v: u32, @builtin(instance_index) i: u32) -> Out {
      let corner = vec2f(f32(v & 1), f32(v >> 1));   // triangle-strip corners
      let r = boxes[i].rect;
      let px = r.xy + corner * r.zw;
      return Out(vec4f(px.x / 300 - 1, 1 - px.y / 195, 0, 1), corner * r.zw, i);
    }
    @fragment fn fs(in: Out) -> @location(0) vec4f {
      let b = boxes[in.i];
      let half = b.rect.zw / 2;
      let q = abs(in.local - half) - half + b.style.w; // signed distance to a rounded box
      let d = length(max(q, vec2f(0))) + min(max(q.x, q.y), 0) - b.style.w;
      let a = clamp(0.5 - d, 0, 1);
      return vec4f(b.style.rgb * a, a);               // premultiplied alpha
    }` });
  const blend = { srcFactor: 'one', dstFactor: 'one-minus-src-alpha' };
  const pipeline = device.createRenderPipeline({ layout: 'auto',
    vertex: { module }, primitive: { topology: 'triangle-strip' },
    fragment: { module, targets: [{ format, blend: { color: blend, alpha: blend } }] } });
  const data = new Float32Array(boxes.flat());
  const buffer = device.createBuffer({ size: data.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
  device.queue.writeBuffer(buffer, 0, data);
  const encoder = device.createCommandEncoder();
  const pass = encoder.beginRenderPass({ colorAttachments: [{ view: context.getCurrentTexture().createView(),
    clearValue: [0.97, 0.96, 0.93, 1], loadOp: 'clear', storeOp: 'store' }] });
  pass.setPipeline(pipeline);
  pass.setBindGroup(0, device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer } }] }));
  pass.draw(4, boxes.length);
  pass.end();
  device.queue.submit([encoder.finish()]);
}
main();
</script>