2026-03-11
Five Decades to Real Time
1974 to 2016, in 0.3 ms
v2.3.0 release notes →Scrutinizer simulates peripheral vision by degrading content away from your gaze. Until now, that meant MIP blur, which is fast and stable but averages away everything. v2.3.0 replaces it with a WebGPU compute pipeline that keeps what MIP blur destroys.
The theoretical basis is the Texture Tiling Model (Balas, Nakano & Rosenholtz 2009; Rosenholtz 2016): peripheral vision pools summary statistics such as mean, variance and orientation energy. A simulation that preserves those statistics while destroying spatial layout produces mongrels (Rosenholtz et al. 2012): textures that look wrong up close, right in the periphery. MIP blur approximates acuity falloff models from the 1970s. This catches Scrutinizer up with the last decade.
WebGPU is less mature than WebGL, and a fault can hard-crash the renderer. Whether this could run in real time on integrated graphics was an open question. It does, in about 900 lines and under 0.3 ms, with a safety harness that falls back to MIP blur if anything goes wrong.
The pipeline
Two WGSL compute passes run alongside the existing WebGL renderer on a half-resolution copy of each frame.
Pass 1 measures what’s in each tile (in Oklab, a perceptually uniform color space). Pass 2 synthesizes new pixels that match those measurements without preserving the original spatial layout. The result blends into the WebGL pipeline via eccentricity-dependent alpha. Implementation details in the v2.3.0 release notes.
Measuring the difference
Sample rectangular patches at each eccentricity from both rendering modes, convert to Oklab, and compare L-channel variance:
varianceRatio = var(mongrel_L) / var(mipBlur_L)
Above 1.0: the mongrel preserved more luminance contrast. Below 1.0: it smoothed more.
Results
We measured at three eccentricities using golden captures from a crowding test stimulus.
Each row averages rectangular patches above and below the fixation point at a given eccentricity. The main column is L Ratio, the luminance variance of the compute mongrel divided by that of MIP blur. Green means the mongrel preserved more local contrast (ratio > 1.0).
| Ecc | MIP | Mongrel | L Ratio | C Ratio |
|---|---|---|---|---|
| 3° * | 2.6 | 4.4 | 1.69 | 0.93 |
| 6° | 4.1 | 4.1 | 1.04 | 0.89 |
| 10° | 6.6 | 6.8 | 1.03 | 0.93 |
* 3° straddles the foveal boundary, so it is a transition zone. 28px column, golden captures from v2.2. The validation report holds summary ratios only, since regenerated from v2.3 captures (L ratio 1.028, C ratio 0.901); compare-crowding-modes.js prints the per-band, above/below values. These measurements cover 3–10°; extending to wider eccentricities (>10°) is a planned follow-up.
The L ratios at 6° and 10° are both above 1.0, by a modest but consistent 3–4%. Both modes pool color similarly (C Ratio ≈ 0.9), which is expected: color is pooled aggressively in the periphery regardless of method. Luminance is where the two pipelines diverge.
Score card
| # | Hypothesis | Result |
|---|---|---|
| H5 | Oklab L variance ratio > 1.0 at ≥6° | PASS (avg 1.04) |
| H6 | Chrominance variance ratio ≈ 1.0 at ≥6° (±0.25) | PASS (avg 0.91) |
| 3° L ratio ≈ 1.0 | 1.69 (transition zone, not passthrough) |
Finer pooling regions or a larger Gabor bank would likely increase the advantage. The pipeline and the measurement are in place. As of v2.3.0, this is the default rendering path.
Update (2026-10-01): A later isolation test
(validation journal,
Wave 7.5, 2026-03-24) found that crowding-synth.wgsl wrote near-zero RGB,
so mode 10 had been rendering through the fragment shader’s MIP fallback. That test
did not establish when the defect began, so the ratios above may not measure the
oriented-noise synthesis. Mode 10 stopped being the default in v2.6.
Also in v2.3.0
- Bouma-scaled edge density gate: the congestion map now gets MIP-sampled at the MIP level matching Bouma’s critical spacing (0.5 × eccentricity). The GPU MIP chain integrates edge density over a Bouma-sized neighborhood. The shader’s V1 stage handles eccentricity-dependent displacement; MIP handles spacing-dependent pooling.
- Seeded flanker RNG: the crowding test page now uses a deterministic
PRNG (mulberry32, seed 42) for flanker letter selection. Previous captures used
Math.random(), meaning the MIP blur and compute mongrel captures had different flanker arrangements, a content confound. Override with?seed=N. - Oklab variance metrics:
node scripts/compare-crowding-modes.jsnow outputs luminance and chrominance variance tables alongside the existing cyan-based crowding metrics.--jsonincludesluminancePatchesand per-hypothesis results.