Fractions Skill Score computed by a WebGPU compute shader · checked against the CPU reference · MET-AL
The Fractions Skill Score is the one MET operator with no cheap closed form over partial sums — it needs a sliding window over the whole grid. That makes it the natural GPU target. This page runs FSS five ways — the trusted integral-image CPU reference and four WebGPU compute kernels (a naive per-cell one, an O(cells) separable one, a prefix-scan integral image, and a multi-block scan that lifts the single-workgroup line limit) — proves all agree, races them across grid size and neighborhood radius, then renders the fraction fields GPU-to-screen from the same integral image, with no readback. A pros/cons table weighs each level of the exploration.
WebGPU adapter in this browser (the shader runs here; if there's none, everything falls back to CPU).
Real AIGFS-vs-URMA case (8,051 cells). Same threshold and neighborhood fed to both paths;
the scored-center count n must match exactly, the scores to floating-point tolerance.
Warm median wall-clock across grid sizes, from the real case (8k cells) to 2048² (4.2 M cells), at the neighborhood set above. GPU time includes input upload + dispatch + readback (an honest end-to-end).
Fixed 1024² grid, growing the neighborhood r. The naive kernel is O(cells·(2r+1)²) so it climbs steeply; the CPU integral image and the GPU separable kernel are O(cells) and stay flat. Watch the naive kernel fall behind even the CPU at large r while the separable kernel holds its lead.
The prefix-scan kernel above scans each grid line with a single workgroup, so a line
can hold at most 256 threads × 8 = 2048 cells. The multi-block scan lifts that ceiling:
each line is split into 2048-cell blocks scanned in parallel, a second pass scans the per-line block totals
into offsets, and a third adds them back — the textbook parallel-prefix pattern. Below we run a grid wider
than 2048, where the single-block kernel must bail out and only the multi-block kernel (and the CPU)
can finish — both still bit-for-bit matching the reference.
Real case, at the threshold set in section 2. Drag the r slider here (mirrored with section 2)
to resmooth and watch every panel — and the score — update live. Left of each pair:
the raw binary exceedance (does this cell cross the threshold?). Right: the neighborhood fraction
the FSS actually compares — each cell replaced by the share of exceedances within r cells. FSS rewards forecasts
whose fraction fields agree even when the binary fields don't line up point-for-point; the last panel is the
per-cell |Pf − Po| that the score integrates.
Same fraction fields as section 6, but now computed from the same integral image the prefix-scan kernel builds: a compute pass runs the multi-block SAT scan, a colorize pass box-queries it and writes turbo colors straight into a texture, then a render pass samples that texture onto the canvas. Nothing is read back to JavaScript — the whole scan → box-query → colormap → pixels path stays on the GPU. Move the r slider to recompute live.
Five levels of the same score, each a different point on the effort-vs-payoff curve. Toggle between what each buys you, what it costs you, and how it actually measures up.
MET-AL · WebGPU FSS prototype — four GPU kernels (naive · separable · prefix-scan · multi-block), checked against the integral-image CPU reference; the on-screen field shares the scan's integral image. De-identified real case. Verified live on this machine's GPU.