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.

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.