github gpujs/gpu.js 2.20.0

10 hours ago

The WebGPU release. GPU.js gets a compute-shader backend, async becomes a property any kernel can have, and two long-standing correctness bugs are fixed.

WebGPU backend

new GPU({ mode: 'webgpu' }) compiles kernels to WGSL compute shaders over f32 storage buffers — no float packing, no texture layouts, no fragment-shader detour, because WebGPU has the primitives WebGL made this library emulate.

Measured on an Apple M1 Max (Chromium; WebGL2 via ANGLE Metal), results cross-validated between backends before timing:

Workload cpu webgl2 webgpu
matmul 1024×1024 incl. readback 2340 ms 18.5 ms 6.30 ms (2.9× vs webgl2, 371× vs cpu)
matmul 512×512 incl. readback 284 ms 3.40 ms 1.80 ms
3-kernel pipeline chain, end to end 18.5 ms 36.6 ms 9.00 ms (4× vs webgl2)

Kernel calls return a Promise in this mode — WebGPU has no synchronous readback, correctly — and pipeline: true resolves a GPU-resident buffer handle that passes into downstream kernels with no readback. The mode is explicit opt-in and never auto-selected. Reproduce the numbers with node scripts/benchmark-webgpu.mjs.

Not yet supported, each with a clear error: kernel maps, graphical, toString(), precision: 'unsigned', Math.random.

Asynchronous kernels

asyncMode: true (or kernel.setAsyncMode(true)) makes every call return a Promise of the usual result, on every backend. On WebGL2 that buys a genuinely non-blocking readback — pixel-pack buffer behind a GPU fence — so the main thread keeps running while the GPU works:

30 × matmul-512 with readback, 4 ms heartbeat beside it main thread blocked worst stall
webgl2, synchronous 101 ms (96%) 105 ms
webgl2 + asyncMode 3.3 ms (1.4%) 4.8 ms

mode: 'async' puts a whole GPU instance under that contract and picks the backend for you — the best provable one immediately, upgraded to WebGPU on a kernel's first call if an adapter answers. Write await kernel(...) once and the same code runs on WebGPU, WebGL2 or CPU. Measure your own with node scripts/benchmark-async.mjs.

Heads up for v3: the next major version makes async the default everywhere. Code written against mode: 'async' today already conforms. See the README's v3 notice and its five-step migration guide.

Correctness fixes

  • x * 0.5 and 0.5 * x now agree. Binary expressions took their type from the left operand, so an integer on the left rounded a fractional operand away: this.thread.x * 0.5 computed x * 1. Arithmetic now promotes the way JavaScript does; comparisons round the bound exactly (i < fi < ceil(f)), which also fixes fractional loop bounds — for (i = 0; i < 2.5; i++) ran twice and now runs three times.
  • Reusing a kernel with a different argument type gives that argument's answer. Passing an Input to a kernel built for a plain array (or the reverse) returned the previous call's values on the GL backends and NaN on cpu. Type re-detection is fixed, the cpu backend gained the kernel-switching it never had, and a stale framebuffer can no longer be returned as a result.
  • #300 (open since 2018) is fully closed. Statements are linearized, loop headers and switch bodies covered, and user functions called nested inside themselves are lifted. The matching workaround was filed upstream against ANGLE (angleproject:539654417) — the root cause is FXC miscompiling nested sampler-taking calls, not this library.
  • #854 assignment-as-expression parenthesization and #855 switch-case break also fixed.

Performance

Single-precision scalar WebGL2 kernels now render to R32F and read back one float per value where the driver allows, instead of four — a quarter of the readback bytes, ~2.8× end to end on readback-dominated work.

Learn GPGPU

There is now a free hands-on course: gpu.rocks/learn — 15 lessons, ~10 hours, no toolchain. It teaches GPGPU rather than an API: the mental model and the hardware lessons transfer to CUDA, WebGPU and Metal.

Verification

Node 2563 passing / 0 failing; browser suite on real hardware 3867 / 0; SwiftShader identical to its known baseline. Two adversarial multi-agent review rounds over the new backend produced 12 confirmed findings, all fixed with regression tests before this release.

Installing

npm install gpu.js@2.20.0
<script src="https://unpkg.com/gpu.js@2.20.0/dist/gpu-browser.min.js"></script>

Don't miss a new gpu.js release

NewReleases is sending notifications on new releases.