WGSL compute shaders: WebGPU vs WebAssembly
Pick an effect, or edit the shader. The page runs the same WGSL on your GPU
with WebGPU, and as WebAssembly that it compiles in your browser with
wgsl-compute-to-wasm.wasm. Then it
compares the two outputs float for float. Nothing is uploaded.
Input
WebGPU
WebAssembly
Difference ×256, red to yellow
Loading…
Why the outputs can differ #
Wasm f32 arithmetic is IEEE 754 with correct rounding, so the Wasm output is
the same on every machine. WGSL lets GPUs compute sqrt and division less
precisely, and shader compilers can reorder or fuse floating-point operations.
Those differences are a few ULP (units in the last place) and show only in the
heatmap. The ULP count looks large near zero, where adjacent floats are closest
together: 7e-7 can be hundreds of ULP. Comic book and Bulge compare
floats or turn them into pixel indexes, so a one-ULP change could move an
outline or a sampled pixel; whether it does depends on the GPU and the image.
Mirror and Pop art index with integers, so they should match bit for
bit.
How it works #
Each shader reads one array<vec4f> of RGBA32F pixels (16 bytes each),
up to 256×256, and writes one vec4f per invocation. Pixel values are the
image's sRGB values divided by 255, without conversion to linear light.
- WebGPU: the page creates a compute pipeline with
layout: "auto", passes
each override as a pipeline constant, dispatches one invocation per pixel,
and copies the output buffer back.
- WebAssembly:
wgsl-compute-to-wasm.wasm compiles the source to a QIP
content component. Each override becomes a uniform_set_<name> export, and
render runs the entry point once per element with SIMD for vec4f.
The page sets width to the image width. A trailing // min..max comment on an
override gives it a slider. The compiler accepts a subset of WGSL: no loops,
no helper functions and no sin, cos or pow, so each effect reads its
neighbours one by one. Run it on the command line with qip:
qip run -i edges.wgsl -o edges.wasm text/wgsl/wgsl-compute-to-wasm.wasm
qip run -i photo.rgba32f -o edges.rgba32f edges.wasm -u width=256 -u threshold=0.3
To add an effect, add a <script type="text/wgsl" data-name="…"> block to
this page.