| .github | |
| examples | |
| scripts | |
| src | |
| test | |
| .gitignore | |
| CLAUDE.md | |
| index.html | |
| package-lock.json | |
| package.json | |
| README.md | |
| test.html | |
| tsconfig.json | |
| vite.config.ts |
math-webgpu-sandbox#
Write a MATLAB script, run it on your GPU. The sandbox compiles MATLAB-syntax
scripts to fused WebGPU compute kernels, times them with the script's own
tic/toc, and — because everything you write is plain MATLAB — you can
paste the same script into real MATLAB and compare the numbers. An optional
in-browser CPU run through numbl's normal engine gives a
third column without leaving the page.
Live page: https://concept-collection.github.io/math-webgpu-sandbox/
n = 8000000;
x = rand(n, 1);
y = zeros(n, 1);
tic;
for k = 1:600
y = y + 0.1*sin(x + k) .* exp(-x) + x.^2;
end
toc
fprintf('checksum %.4f\n', mean(y));
That loop body is one GPU kernel, compiled once and replayed 600 times — about five billion element-updates, a few seconds on an integrated GPU.
How it works#
The script is parsed and lowered by numbl's JIT pipeline (reached through a numbl-src vite alias, the same
arrangement turing-surface
uses), which fixes every type and shape at compile time — n = 2048; A = rand(n); pins static shapes via exact-value propagation. The back end is
this repo's: a planner maps the typed IR onto WebGPU, and an executor replays
the resulting op sequence.
- Fusion. numbl's inline pass folds single-use temps back into their
consumer, and a sandbox-side pass (
src/mgpu/fuse.ts) folds the rest — transcendentals, comparisons, logicals — which numbl's C backend declines but WGSL handles. One source line becomes one kernel. Generators fuse too:x = 2*rand(n,1) - 1is a single kernel that hashes its way to uniform variates per element, touching no other buffer. - Reductions (
sum/mean/prod/max/min/norm/dot) take a fused loader, sosum(a.*b + c)reads its operands exactly once. Vectors (andX(:)) reduce fully in two passes; matrices reduce per column, which in MATLAB's column-major layout is the contiguous direction. A * Bis a 16×16-tiled shared-memory GEMM (column-major, adapted from matmul-bench); matrix transpose is a tiled relayout; vector transpose,X(:)andreshapeare views of the same buffer (free) when the source is never reassigned.forloops replay, they don't unroll. The body is planned once; the loop variable lives in a dynamic-offset uniform with one slot per iteration, and all iterations are encoded into one submit.randinside a loop mixes the iteration counter into its stream, so every pass draws fresh values.tic/tocare synchronization points: pending GPU work is submitted and awaited, then wall-clock time is taken on the host. That is the same thing MATLAB's synchronous tic/toc measures, which is what makes the number comparable when you paste the script there.
What is (and isn't) supported#
Supported: elementwise math on real arrays (including comparisons, &/|/~,
two-arg max/min, mod/rem, .^), zeros/ones/eye/rand/randn/
linspace/ranges, A*B, transpose, the reductions above, A(:)/reshape,
counted for loops, tic/toc, disp/fprintf, semicolon-suppressed or
echoed assignments, and literal row/matrix constants.
Deliberately rejected, with a source-located error rather than a wrong
answer: indexing/slicing beyond (:), if/while, complex numbers,
user-defined functions, variables that change size, and printing inside for
loops (the body has nowhere to run host I/O — it replays on the GPU).
The comparison is honest, with two caveats#
- The GPU computes in f32; WebGPU has no f64. MATLAB defaults to double.
Timing comparisons are still meaningful; for the closest apples-to-apples,
use
singlearrays in MATLAB. Values agree to single precision, and the scripts print checksums so you can see that they do. rand/randnhere are deterministic counter-based generators (PCG-flavored hash of element index and call site). Statistics match MATLAB's; individual draws do not, so checksums of random data are close but not equal.
Development#
npm install # numbl must be checked out as a sibling: ../../numbl
npm run dev
npm run test:node # correctness suite on desktop WebGPU (Dawn)
npm run test:gpu # same suite in headless Chrome (SwiftShader fallback)
The GPU compiler runs from numbl's TypeScript sources directly (through the
numbl-src alias), so no numbl build is needed.
License#
Apache-2.0