PURPOSE
Radial darkness overlay drawn directly into the main canvas 2D context each frame. Sized to the actual viewport in device pixels so the corners are fully dark regardless of aspect ratio, DPR, or active caller transform. Replaces an earlier pre-baked square canvas that left corners outside the dark band when stretched to 16:9.
OWNS
- The runtime opacity knob
window.__vignetteOpacity(default0.6viaVIGNETTE_DEFAULT_OPACITY). - Per-frame radial gradient construction and the single
fillRectcovering the screen. - URL-param ingestion for
?vignette=N(whereNis 0-100 percent). - The exported setter used by the dev console.
READS FROM
window.__vignetteOpacityeach frame (falls back to default if unset ornull/undefined).ctx.canvas.widthandctx.canvas.heightfor device-pixel dimensions.window.location.searchonce at boot for thevignetteURL param.
PUSHES TO
- The provided
CanvasRenderingContext2D— writes onefillRectcovering the entire canvas with the radial gradient. window.__vignetteOpacity— written byinitVignetteFromUrl(when URL param parses to a finite number) and bysetVignetteOpacity.
DOES NOT
- Does not read or trust the caller’s transform state — forces
setTransform(1,0,0,1,0,0)inside its own save/restore block so the overlay is never affected by camera offset, world zoom, or DPR scale. - Does not use the
_w/_hparameters — they are accepted for API compatibility and ignored. - Does not cache the gradient — rebuilds it every frame (cost ~0.3 ms total).
- Does not draw anything when opacity is
<= 0. - Does not register itself with any render loop — the caller invokes
drawVignetteexplicitly. - Does not throw or guard against a missing canvas; assumes a valid 2D context is passed.
Signals
- Opacity is encoded in the gradient color stops, not in
ctx.globalAlpha(which is forced to1). - Outer radius is the half-diagonal
Math.hypot(W, H) / 2so the corners hit the fully-dark stop. - Inner radius is
Math.min(W, H) * 0.20— a small transparent center proportional to the shorter viewport edge. - Opacity is clamped to
[0, 1]both at write time (setVignetteOpacity,initVignetteFromUrl) and at read time inside the color-stop expression.
Entry points
drawVignette(ctx, _w, _h)— call once per frame after the scene has been rendered.initVignetteFromUrl()— call once at app startup to seed opacity from?vignette=N.setVignetteOpacity(opacity)— runtime setter (0-1), returns the clamped value; backs__dev.setVignette.
Pattern notes
- Stateless module — no module-level mutable state beyond the
windowglobal it reads/writes. - The save/restore +
setTransformidentity reset is the load-bearing pattern: it makes the function safe to call from any render-loop position without coordinating with camera or DPR code. - Constant-extracted default (
VIGNETTE_DEFAULT_OPACITY) keeps the magic number out of the function body. - URL ingestion is gated on
typeof window === 'undefined'to stay safe under non-browser execution (e.g. tests, SSR). Number.isFiniteguard on the parsed URL value ensuresNaNfrom a malformed param is silently ignored rather than poisoning the global.