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.

SOURCE MIP BLUR var(L) = 2.6 COMPUTE MONGREL var(L) = 4.4 MIP blur: Spatial detail → gone Local contrast → gone Mean luminance → preserved Compute mongrel: Spatial detail → gone (oriented noise) Local contrast → preserved This is the TTM signature.

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.

Source frame half-res RGBA PASS 1: STATS crowding-stats.wgsl 8×8 workgroups Oklab L: mean, σ Oklab a, b: tile mean 4-dir orientation energy CMF mip level 48 bytes/tile → storage buffer PASS 2: SYNTH crowding-synth.wgsl Read tile stats 4-grating oriented noise Scale by σL Tile-mean chrominance Oklab → sRGB pack alpha = (mip - 0.5) / 1.5 TEXTURE5 RGBA8 + alpha → fragment shader Two dispatches + async readback: <0.3ms on Apple M1 integrated GPU

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.

0° 2° 3° 6° 10° Foveal Transition Peripheral filtering three measured eccentricities
Three eccentricities sampled by the comparison script. 3° straddles the foveal boundary and is dimmed in the table. 6° and 10° are true peripheral, where the two pipelines diverge.

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.64.41.69 0.93
6° 4.14.11.04 0.89
10° 6.66.81.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

#HypothesisResult
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