PURPOSE
Full-scene post-processing pipeline applied to the gameplay layer after world/particle/effect drawing but before HUD. Provides cinematic mastering through bloom, atmospheric fog, world- and screen-space dust motes, bokeh lens particles, vignette, CRT scanlines, god rays, and CSS-filter contrast/saturation. Per-planet presets (dark, sunlit) toggle vignette, god rays, and bloom intensity. Mobile gets a lighter pipeline (1/8-resolution bloom, vignette only).
OWNS
- Bloom scratch canvases (
_bloomCvsat 1/4 res;_bloomLiteCvsat 1/8 res for mobile) with viewport-resize rebuild guards. - Mastering preset table
PP_PRESETSkeyed bydark/sunlit, with_activePresetmutable at runtime viasetMode/patchPreset. - Behind-ship dust pool (
_dustBehind, 40 motes in world space with parallax 0.3) and above-ship dust pool (_dustAbove, 20 motes in screen space). - Pre-baked mote stamp (
_moteStamp, 128px warm radial gradient) and bokeh stamp (_bokehStamp, 128px warm-core / cool-ring soft circle). - Atmospheric fog noise tile cache
_fogTileskeyed by${type}_${tintR}_${tintG}_${tintB}, plus legacy_fogTilefor the default noise variant. - Pre-baked vignette canvas (
_vignetteCvs, oversized byVIGNETTE_MARGINon each side, elliptical squeeze 0.85) and scanline canvas (_scanlineCvs, 1px lines at 3px gap, alpha 0.025). - Internal game time accumulator
_gameTime, advanced insideupdate. - Seeded RNG
_sr, hash noise_hashNoise, tileable_valueNoise, and_fbmoctave summation for fog texture synthesis.
READS FROM
W,H,camerafrom../corefor viewport dimensions and world-to-screen transforms.camera.x,camera.y,camera.zoomfor dust parallax projection and fog drift offsets.PostProcessingModetype from../../data/planet-configforsetModeargument shape._activePresetfields (bloomAlphaMult,vignetteEnabled,contrastBoost,saturationShift,godRaysEnabled) gating most pipeline branches.
PUSHES TO
- The caller-supplied
CanvasRenderingContext2D— composites bloom (lighter), dust motes (source-overwithglobalAlpha), fog (screen), contrast/saturation (CSSfilterself-blit), vignette (source-overdrawImage), god rays (screenwith rotated linear gradients), and scanlines (source-overdrawImage). - No data stores, no events, no telemetry — purely pixel output and internal canvas caches.
DOES NOT
- Does not own or modify world entities, simulation state, or input.
- Does not draw HUD, UI, or text — strictly screen-space visual mastering of the gameplay layer.
- Does not allocate per frame in the hot path; dust pools, stamps, fog tiles, vignette, and scanlines are all pre-baked or pool-reused.
- Does not handle shield-break inverse-color overlay (section header exists but body is empty).
- Does not own
Camerainstance management; theCameraimport is unused outside of the type reference at top. - Does not provide a high-quality fog tile rebuild on resize (fog tiles are size-fixed at
FOG_TILE_SIZE = 512). - Does not validate preset names beyond falling back to
darkfor unknown modes.
Signals
setMode(mode)is the planet-level swap signal — called at run start andlevel_advancebased onPlanetDef.postProcessing.patchPreset(patch)is the VFX-dashboard live-tweak signal — mutates the active preset in place viaObject.assign.qualityparameter ondrawScreenEffects('high' | 'mobile' | 'low') gates which pipeline stages run;'low'is the thermal-emergency mode that drops everything except vignette.- Viewport resize is implicit — every per-frame draw helper checks current
W/Hagainst cached dimensions and rebuilds its scratch canvas inline.
Entry points
PostProcessing.update(dt)— advances_gameTime, lazy-initializes both dust pools on first call, advances dust positions and wraps them in world/screen space.PostProcessing.drawFog(ctx, alphaOverride?, texType?, tintR?, tintG?, tintB?, parallaxMult?, speedMult?)— draws a tileable fog layer at the requested texture variant (noise|wispy|clouds|dense|patchy); call after shadows, before sticker blit.PostProcessing.drawDustBehind(ctx)— projects world-space behind-dust motes to screen space usingcameraparallax and zoom; call insidedrawWorldimmediately before the sticker blit pass.PostProcessing.drawBokeh(ctx, count = 12)— draws screen-space soft bokeh circles at deterministic hash-seeded positions with slow sine drift.PostProcessing.drawScreenEffects(ctx, quality)— composites bloom (full or lite or skipped) → above-dust (high only) → contrast/saturation filter (preset-gated) → vignette (preset-gated) → god rays (preset-gated, not in low) → scanlines (high only).PostProcessing.setMode(mode)— swap active preset; unknown modes fall back todark.PostProcessing.patchPreset(patch)— partial in-place override of the active preset.
Pattern notes
- Pre-bake everything that is constant per resolution: vignette, scanlines, fog tiles, mote stamp, bokeh stamp. The hot path is a sequence of
drawImageblits withglobalAlphaandglobalCompositeOperationswaps; no per-framecreateRadialGradient, nocreateImageData, no per-mote gradient construction. - Downsampled bloom uses CSS
filter: blur(Npx)on the offscreen scratch canvas, then a single'lighter'additive blit back at full size. Sunlit-class presets withbloomAlphaMult >= 3.0accumulate multiple passes (3) for layered glow. - Bloom blur radius scales with preset:
BLOOM_BLUR_PX * bloomAlphaMult * 0.5clamped to 60px. Same scaling for mobile lite bloom. - Dust mote pool is fixed-size with seeded RNG (
_srbased onMath.sinof large primes), giving deterministic but visually-random initial placement. Wobble is per-mote sine/cosine of_gameTimeagainst a per-mote phase, speed, and amplitude. - World-space dust wraps inside a
DUST_BEHIND_FIELD-sized box centered oncamera.{x,y} * DUST_BEHIND_PARALLAX. Screen-space dust wraps at viewport edges with aradius * 2margin so blobs do not pop in or out. - Fog texture builder uses tileable FBM (
_fbmsumming octaves of_valueNoisewith periodic wrap-around). Variants encode pattern shape —noiseis classic FBM,wispyis ridged absolute-value with sharpening,cloudsuses domain warping,denseis low-frequency,patchythresholds FBM for hard clear/thick zones. Each tile is then softened with a 4-pass low-alpha self-blit before being cached. - Fog parallax and drift are independently scalable per call via
parallaxMultandspeedMult, so multiple layers can share the same texture cache at different perceived depths. - Vignette is drawn into an oversized canvas (screen +
VIGNETTE_MARGIN * 2per axis) by filling pure black and then carving a radial gradient hole with'destination-out'. Elliptical squeeze (VIGNETTE_H_SQUEEZE = 0.85) achieves a portrait-friendly tighter-sides look. The runtime blit offsets by-VIGNETTE_MARGINso edges are guaranteed pure black even after camera zoom shenanigans. - Contrast/saturation pass self-blits the canvas with a CSS
filterstring; this is only invoked when the preset actually requests non-zero values, so default presets pay no cost. - God rays are tall rotated linear gradients composited with
'screen'blend at very low alpha; angle drifts atGOD_RAY_ROTATION_SPEEDrad/sec; each successive beam is wider and slightly more transparent. Suppressed in'low'quality. - Public surface is a single
PostProcessingobject literal, not a class — module-level singletons hold all state, matching the codebase’s other rendering subsystems (e.g.background.ts).