engine/rendering

PURPOSE — Pixel pipeline. Drives the per-frame composition of the gameplay scene from the WebGL nebula at the bottom of the stack up through layered parallax, a hybrid WebGL2 sprite batch and Canvas 2D world pass, a multi-stage screen-space post-processing chain, and the lightweight Canvas 2D HUD on top. Owns the camera, the texture atlas, the procedural background renderers (nebula, fire, live shaders, cone-beam), the offscreen WebGL2 sprite batches with their additive glow companion, the parallax layer stack with palette-driven biome recipes, the reward-cinematic state machine, and every screen-space overlay (vignette, scanlines, bloom, fog, dust, god rays, gameplay grid). Pure-output: consumes ship / world / camera state and writes pixels — never mutates gameplay.

OWNS

  • Renderer orchestrator — main canvas 2D context handle, DPR transform, viewport clip, layer registry (background / world / particles / effects / hud by zIndex), one-shot sprite-batch init, atlas upload, world↔screen coordinate helpers, and the per-frame beginFrame / endFrame / render lifecycle that sequences background → world → particles → effects → optional lighting → HUD.
  • Camera — follow-cam targeting state, heat-driven targetZoom ramp, warp-puddle zoom multiplier, lerped position/zoom, screen-shake amplitude/duration with linear decay, transient zoom-pulse stack with selectable easing curves, the per-frame “remove last frame’s shake offset, apply new shake” pattern, and the toS / toSx / toSy / toW projection helpers consumed by every screen-space draw site.
  • PAL palette constant — flat hex/rgba table used as a baseline color reference by draw.ts and HUD primitives that don’t go through the biome palette system.
  • Background — WebGL nebula composite drawback, 10-layer parallax starfield with 3 object types (star / dust / rock) and 8 shape functions, archetype-driven star color / type strings, deterministic seeded sr() placement, pre-baked star layers cached per archetype/viewport (with per-frame parallax offset + tiled wrap), per-archetype dust wobble + twinkle on the live layers, and the surface-mode (SURF_MODE) dispatch that hands off to the parallax stack on planet biomes.
  • WebGL2 SpriteBatch pair — two offscreen WebGL2 canvases (one alpha-blend, one additive-blend _glows) each with their own instanced-quad shader (per-instance posSize / uvRegion / color / rotation), shared 4096² atlas texture, MAX_SPRITES cap with silent overflow drop, beginFrame / add / flush lifecycle, premultiplied-alpha math at insertion time, and the flush('normal' | 'additive') blend-mode switch.
  • Atlas builder — process-global named-region map, packing cursor (row-based with 2px padding), aspect-ratio side table, programmatic Canvas 2D rendering of enemy shapes / bullets / particles / utility stamps onto a 4096² atlas canvas at boot, reserveAtlasRegion / patchAtlasRegion / reuploadAtlas hooks consumed by the VFX-component runtime after the boot bake.
  • Player ship V4 loader — per-hull bundle of unlit diffuse PNG + optional points JSON pulled from /ships-v4/, idempotent concurrency-safe preloader, runtime manifest fetch with dev-endpoint fallback, asset cache keyed by exact case-sensitive hull filename.
  • Enemy sprite loader — per-archetype PNG cache for the eight enemy archetypes (bugs and city sets), downsampled to the SPRITE_TARGET_ENEMY target, archetype-extraction helper that strips rarity suffix.
  • Silhouette utilities — per-hull tallest-column silhouette height fraction and content-max fraction caches derived once from sprite alpha data, used to scale collision hulls to the rendered content; sprite downsample helper.
  • Sprite-orientation constant — module-wide SPRITE_FORWARD_OFFSET (-π/2) for the v4 south-facing convention.
  • Active hull / rarity state for the Canvas 2D fallback paint path on the player ship.
  • Hi-res offscreen cache — per-sprite-URL 2× pre-rendered offscreen canvases used by the Canvas 2D ship draw to keep glow passes from blurring the sprite itself.
  • Pre-baked shield-ring stamp cache — quantized-radius keyed offscreen canvases with concentric blurred arc strokes baked once; animated via globalAlpha only.
  • Shapes registry — name → polygon list for player, XP canister, coin, genesis station, the four base enemy archetypes (orb / charger / shooter / mortar), plus extras; consumed by draw.ts polygon fallback and by the enemy-orbit hull cache in vfx.
  • Camera shake / zoom-pulse latches (_shakeAmp / _shakeT / _zoomPulseFactor / _zoomPulseT / _zoomPulseEasing) and the per-frame _shakeLastX/Y / _zoomPulseLastDelta carry-over fields that let next frame’s lerp anchor on the true (unshaken) camera position.
  • PostProcessing pipeline — bloom (full-res 1/4 downsample + CSS-filter blur + additive blit), lite bloom (1/8 res, mobile-targeted, multi-pass for strong-bloom presets), behind-ship dust pool (world-space, parallax 0.3×, sine wobble) and above-ship dust pool (screen-space, atmospheric depth), pre-baked mote and bokeh stamps, multi-texture atmospheric fog (FBM / wispy / clouds / dense / patchy variants with per-tile seeded value-noise generation and softening passes), pre-baked elliptical vignette canvas with portrait squeeze and VIGNETTE_MARGIN overdraw, pre-baked CRT scanline canvas, volumetric god-ray gradient rectangles, the active mastering preset (dark / sunlit) and per-preset bloom multiplier / vignette enable / contrast / saturation / god-rays flags.
  • Stand-alone drawVignette — direct per-frame radial gradient driven by window.__vignetteOpacity for the simpler always-on darkness pass (distinct from the post-processing preset-gated vignette).
  • PostFxState store — bespoke subscription store (no Zustand) for the eleven art-style wetness sliders (Borderlands cel, synthwave, VHS, pixel art, oil paint, watercolor, comic, blueprint, thermal, stained glass, cyber-glitch), the polish layer (blur, hue, sat, light, anim-speed; 0.5 = identity for non-blur knobs), and the layer-FX toggles (bg stars, color sparkles, shooting stars with angle / variance / intensity); useSyncExternalStore hooks for React subscribers and a subscribePostFx raw subscription used by the nebula RAF.
  • Nebula engine — own offscreen WebGL canvas at 0.75× viewport resolution, simplex-noise FBM / ridged / voronoi fragment shader, archetype-driven cosine-palette uniforms, the eleven art-style preset uniforms wired straight to the post-FX store, polish-layer uniforms, layer-FX uniforms, drift / warp / density / threshold uniforms.
  • Fire engine — own offscreen WebGL2 canvas at 0.5× viewport resolution, FBM fragment shader, instanced quad pipeline with up to 64 fire instances per flush, world-space and screen-space spawn variants, cached camera state for the world→screen conversion at flush time, tint color, additive-blend draw call.
  • Live-shader runtime — per-component WebGL2 offscreen canvas at half-res by default, fragment-shader-only fullscreen-quad pipeline with declared-uniform binding, shared vertex shader, additive / alpha blend modes; one instance per registered live component.
  • Cone-beam render adapter — single shared LiveShaderRuntime for the cone-fire fragment shader, lazy-init with one-shot unavailability latch.
  • Warp-puddle render — procedurally-generated 1024² nebula canvas cached forever, ground-clip arc draw at the gameplay collision radius, additive halo + electric-arc decoration drawn on top, inside-view jet-black masking.
  • VFX-component runtime — per-component baked atlas region map (muzzle / body / impact / persistent categories), boot-time manifest fetch and PNG patch pass into the atlas builder, frame-region lookup for grid flipbooks at a given normalized t.
  • Glow-stamp module — pre-baked 128² white radial gradient stamp, per-tint color cache (max 16 entries with FIFO eviction), drawGlow / drawWhiteGlow Canvas-2D primitives used by every soft light source.
  • Additive-glow helpers (addGlow / addAura / addCanvasGlow) — outer color quad + inner white-hot core composite for the WebGL _glows batch, plus a Canvas-2D fallback that converts world→screen via Camera.toS and writes directly to the main ctx in lighter blend.
  • Squash-and-stretch system — per-entity wave-shape registry (sine / sawtooth / triangle / pulse), per-archetype + per-weapon-id amplitude and frequency tables, volume-preserving combined sx/sy getters consumed by draw.ts.
  • gameplay-grid — biome-dispatched in-world gameplay underlay (hex / wave / lat-long variants), pre-baked tileable canvases for hex and lat-long with palette-revision invalidation, live wave variant for the water surface, shared base-alpha + slow sine breathing modulation.
  • Boss background loop — per-encounter hidden <video> element life-cycle, autoplay / mute / loop config, idempotent start with bossId-change teardown.
  • Boss sprite map — boss id → bundled sprite metadata, consumed by the atlas builder and the boss draw path.
  • Card theme — rarity gradient table, rarity badge letter / fill / accent / effect-tier maps, card shadow / badge geometry constants, paintCardGradient helper consumed by the reward-cinematic card draws.
  • Medical canvas palette — Space-Grotesk-derived font helpers (medFontHead / medFontBody), color table, panel-shadow apply/clear used by the boss bar and other medical-chrome surfaces.
  • Mission-timer HUD — top-center banner with rank / mission name / countdown.
  • Damage-tag pill — small pill renderer for weapon damage-tag identity surfaces.
  • 3D-style stamp draws — crate, terrain, hub-spoke, shooting-star, artifact-pickup, and artifact-banner draw functions consumed by world / overlay passes.
  • UI icons — gear / heart sprite preload + cache (/icons/*.png).
  • Weapon icons — PNG icon loader and sticker-outline prebaker (15px white inner stroke + 7px black outer stroke at 512²), 48-sample unit-circle offset table baked at module load.
  • Weapon colors — weapon-id → tint hex lookup for projectiles, glows, and HUD chrome.
  • Wheel UI — pie-wheel slice definitions, spin / decel / tick / result animation phases, full-screen overlay layout.
  • hud.ts — HUD slot positions (weapon / modifier / artifact) cached per-frame for upgrade-show animation, boss-bar tween state (slide-in / fade-out, smoothed hp, flash timer, last-known name / color / hpMax), warning list with per-text cooldown, hit-indicator ring buffer (capped at 6, 30° refresh threshold), HP / shield gain-flash latches, control-mode latch (touch / mouse / arrows), HUD bounds for portrait-band narrowing, XP-flash latch, music-player layout bounds + hit-test, cog-press squash timer, reward-card slot animation overrides for the upgrade-show cinematic, mute / volume / heart hit-zones, supply-pod and portal edge-arrow indicators.
  • Reward-cinematic registry — module-private family → CinematicModule map populated at module-load time (level_up / weapon_box / artifact_box / shooting_star), per-frame CinematicContext snapshot shape, onStart / onUpdate / onStateTransition / drawBackdrop / drawCardOverride / drawOverlay / onEnd hook contract, the no-op default fallback for unregistered families, and the cinematic-tone audio helper.
  • Palette system — process-global active palette (default periwinkle), monotonic palette revision counter for downstream cache invalidation, biome-driven preset picker with seed support, slot resolver for hot-path color reads, preset registry.
  • Parallax system — biome-recipe state, four-slot bucketed layer arrays (back / mid / near / fg) sorted by depth, perf-tier pruning (high / mid / low) with id-pattern filters for dust-streaks / fine-grain / starfield / silhouette / fbm, per-biome layer prepare / dispose lifecycle, the silhouette-stamp cache disposer.
  • Parallax layers — starfield, ground-texture, silhouette-stamp (with shared silhouette-stamp cache), atmosphere-fbm; each layer owns its own per-recipe state and pre-bake.

READS FROM

  • engine/coreW / H (viewport CSS dimensions), dpr (device-pixel-ratio for the canvas transform), camera (position, zoom, target position / zoom), ship (anchor for camera follow, HUD bars, hit indicators, projectile reference position), game (phase, time, dilation flag, portal state, active boss def id, raw / dilated dt, weapon-slot count, focus state, control mode), world (enemy array for boss-bar aggregation, prop pool reference), playerInput (control-mode input state), uiScale, CFG (camera lerp, base zoom, heat-zoom slopes), Clock, PERF_FLAGS (no-background flag plus mobile / low-perf gating), SPRITE_TARGET_ENEMY config constant, render-diag pass timers, smoothed-fps reader.
  • engine/core/device-capabilitiesisMobile() for star-density, post-processing-quality, and per-recipe perf-tier gating.
  • engine/core/typesWorldState, ShipRarity, EnemyEntity shapes.
  • engine/world/levelingXP_THRESHOLDS for the HUD XP bar.
  • engine/world/artifacts — active-artifact list and flash state for HUD slot rendering.
  • engine/world/props — prop pool for the supply-pod edge-arrow indicator.
  • engine/world/warp-puddlesWarpPuddleGroup shape for the puddle render adapter.
  • engine/player/stateshasExclusiveState for Star-Power-specific HUD modulation.
  • engine/audio/micro-sfx — sub-audible cue plays from HUD interactions.
  • engine/audio/music-player — playback state, track metadata, volume / heart toggle for the canvas music-player surface; cinematic-tone helper for reward-cinematic audio.
  • engine/vfx/post-fx — read-only access to post-FX entries for the world-space pass (the renderer dispatches the draw; vfx owns the data).
  • engine/vfx/juiceShipRecoil combined sx/sy for the ship draw transform.
  • engine/affixes — affix VFX color lookup via the enemy-orbit pass.
  • data/weaponsWEAPON_MAP, resolveWeaponRarity, DamageTag type, weapon-emoji map.
  • data/weapon-icons — icon presence + path lookup.
  • data/modifiersMODIFIER_TYPE_MAP for HUD modifier slot rendering.
  • data/artifactsARTIFACT_MAP + tier color table for the HUD artifact column.
  • data/bossesBOSS_DEFS for boss-bar display-name / bar-color, BossBackgroundLoop shape for the bg-loop module.
  • data/kill-streaksSTREAK_MILESTONES for HUD killstreak escalation.
  • data/planet-configPostProcessingMode type wired to the post-processing preset switch.
  • data/nebula-archetypes — archetype list, star-color / star-type / surface-mode tables, planet archetype index lookup, plus Archetype type for the WebGL nebula uniforms.
  • data/vfx/atlas/<category>.manifest.json (HTTP fetch at boot) — VFX-component bake manifests.
  • data/vfx/live-shaders/dynamic_cone_fire.live — cone-beam GLSL source + id.
  • palette-presets table inside this system — biome → preset list mapping.

PUSHES TO

  • The main 2D canvas context owned by the React GameScreen — every world / particle / effect / HUD pass writes pixels via ctx.save / draw ops / ctx.restore, plus ctx.drawImage blits from the WebGL offscreens (nebula, sprite batch, glow batch, fire engine, live-shader runtimes, bloom scratch, vignette pre-bake, scanline pre-bake, parallax pre-bakes, gameplay-grid pre-bakes).
  • Six side WebGL canvases — main sprite batch, additive glow batch, nebula engine, fire engine, per-live-shader runtimes (cone-beam currently); each owns its own context and preserveDrawingBuffer: true so the next drawImage to the main canvas reads the prior flush.
  • Hidden DOM <video> element for the boss background loop (positioned off-screen, autoplay / muted / loop).
  • HUD subscriber callbacks via the bespoke post-FX store _listeners set — React components and the nebula RAF re-render / re-render-uniforms when knobs change.
  • Sub-system hooks back into atlas builder — VFX-component runtime reserves and patches atlas regions then triggers reuploadAtlas on the sprite batches.
  • HUD-relayed input hit-tests (hitTestCog, hitTestHelp, hitTestWeaponSlot, hitTestMusicPlayer) consumed by the React input layer — HUD writes module-local hit boxes during draw, the input layer reads them on the next pointer event.
  • Persistent palette-revision counter consumed by silhouette-stamp / fbm caches; bump on every setActivePalette* call.

DOES NOT

  • Run gameplay logic. The renderer reads ship / world / enemy / bullet / prop state but never mutates any of it — combat, weapons, movement, AI, spawning, scoring, kill resolution all live elsewhere.
  • Own the per-frame loop. The fixed-timestep tick lives in the bridge / harness; the renderer’s render(...) is the draw callback the harness invokes once per RAF.
  • Pool or recycle particles, damage numbers, sonar rings, explosion entities, or any VFX object. Those are owned by engine/vfx; this system only walks vfx’s bounded arrays inside the world / effects passes.
  • Detect or resolve collisions, knockback, hit boxes, or damage application. Sprite-rotation orientation and silhouette height fractions are used downstream by combat / physics but are computed and read-only here.
  • Decide auto-aim or weapon firing — the camera computes follow / shake / pulse from ship state and weapon-fired callbacks, but never selects a target or schedules a shot.
  • Manage audio playback beyond reading transport state for the canvas music-player surface and firing the cinematic-tone helper from reward cinematics.
  • Drive the upgrade / reward state machine. The state values, transitions, and choice arrays live in HUD-adjacent gameplay code; reward cinematics receive the state on every frame via CinematicContext and only render against it.
  • Persist any state to disk, KV, Supabase, or telemetry. Caches (atlas regions, baked star layers, baked silhouettes, glow-stamp tints, hi-res ship cache, shield-ring stamps, fog tiles, mote stamps, bokeh stamps, scanline canvas, vignette canvas, bloom scratch, palette-tinted assets) are in-memory only and recomputed on resize / palette / archetype change.
  • Render the React metagame shell (hub / ships / shop / missions / profile). Only the gameplay canvas + reward overlays.
  • Apply time dilation. The renderer reads game._dt / game._rawDt as provided; HUD boss-bar tween, hit indicators, and reward cinematic timers explicitly run on wall-dt so animations play through pause / slow-mo.
  • Compose camera shake or zoom-pulse triggers — both are public methods (Camera.shake, Camera.zoomPulse) called from weapons / combat / vfx; the renderer only stores and decays the active state.

Signals fired / Signals watched — none. The renderer talks to gameplay via direct module imports and to the React layer via the subscribePostFx callback set + module-local hit-box exports. No engine signal bus is involved.

Entry points

  • Renderer class — new Renderer(canvas) constructor builds the layer registry and initializes both WebGL sprite batches, beginFrame / endFrame for the per-frame lifecycle, render(...) for the top-level orchestrator that sequences background → world → particles → effects → optional lighting → HUD, resize for viewport changes, getCanvas / getContext / getDimensions for harness wiring, worldToScreen / screenToWorld / isVisible / getVisibleBounds projection helpers, setViewportClip / restoreViewport for explicit clipping, createLayer / getLayer / addToLayer / clearLayers for the (mostly-unused) layer registry, webglReady flag.
  • safeRadialGradient — defensive createRadialGradient wrapper that clamps degenerate radii / coords.
  • PAL — palette constant for legacy non-biome color lookups.
  • Camera.update / .shake / .zoomPulse / .toS / .toSx / .toSy / .toW — per-frame follow + zoom tick, transient effect triggers, and the projection helpers consumed by every screen-space draw.
  • Background.draw plus the parallax / palette / nebula barrel exports surfaced through index.ts.
  • Sprite-batch barrel — initSpriteBatch / getSpriteBatch / getGlowBatch plus the SpriteBatch class itself (add / flush / beginFrame / resize / uploadAtlas / reuploadAtlas / flushPendingGL).
  • Atlas-builder barrel — buildAtlas (one-shot boot bake), getAtlasRegion / tryGetAtlasRegion, reserveAtlasRegion / patchAtlasRegion, getAtlasAspect / setAtlasAspect.
  • Player-ship V4 — preloadShipV4 / getShipV4 / loadShipsV4Manifest / getShipV4SpritePath.
  • Enemy-sprite barrel — preloadEnemySprites / getEnemySprite / archetypeFromTypeId.
  • Sprite utilities — downsampleSprite, getSilhouetteCollisionRadius, SPRITE_FORWARD_OFFSET.
  • draw.ts exports — setActiveShipRarity / setActiveHullClass plus the per-entity drawShip / drawShipSilhouette / enemy / bullet / pickup / terrain / station draw functions invoked by the world pass.
  • VFX-component runtime — initVfxComponents (boot bake) / vfxFrameRegion (per-frame UV lookup).
  • Nebula engine — nebulaInit / nebulaResize / nebulaRender / nebulaGetCanvas / nebulaIsReady.
  • FireEngine.add / .addScreen / .setColor / .setCamera / .flush / .getCanvas / .resize / .isReady.
  • LiveShaderRuntimeinit / setUniforms / render / getCanvas / resize plus the cone-beam-render getConeBeamRuntime / resizeConeBeamRuntime adapter.
  • WarpPuddle render — drawWarpPuddleExterior / drawWarpPuddleInterior (the inside / outside view variants).
  • PostProcessing.update (dust tick + game-time advance) / .drawFog / .drawDustBehind / .drawBokeh / .drawScreenEffects / .setMode / .patchPreset.
  • drawVignette — stand-alone always-on radial darkness driven by window.__vignetteOpacity.
  • Post-FX store — getPostFxState / getPostFxValue / setPostFxValue / getPostFxIdentity / subscribePostFx / usePostFxState / usePostFxValue.
  • Glow primitives — drawGlow / drawWhiteGlow plus the additive helpers addGlow / addAura / addCanvasGlow.
  • Shapes.def / .get (registry) plus the predefined entries.
  • getShipSquash / getEnemySquash — combined sx/sy lookups per entity for the squash-and-stretch transform.
  • HUD — drawHUD (per-frame entry), plus the hit-test surface (hitTestCog, hitTestHelp, hitTestWeaponSlot, hitTestMusicPlayer), setHudBounds / setHudControlMode, pushWarning / dismissWarning, flashBossBar, pushHitIndicator, getBossBarHudOffset, getTopArtifactSlotPos, triggerCogPressAnim, getBossBar, setLanternFill (deprecated no-op), plus the reward-card draw entry points consumed by the cinematic registry.
  • Reward-cinematic barrel — getCinematicFor / resetActiveCinematic / registerCinematic, the CinematicContext / CinematicModule / RewardState types, and playCinematicTone.
  • Palette system — setActivePaletteByPresetId / setActivePaletteForBiome / resolvePaletteSlot / getActivePalette / getPaletteRevision.
  • Parallax system — setBiome / setPerfTier / drawSlot / disposeParallax / getActiveBiomeId; silhouette-stamp disposer for the cache.
  • Gameplay-grid — drawGameplayUnderlay invoked by the bridge after parallax fg and before events.
  • Boss bg loop — startBossBackgroundLoop / stopBossBackgroundLoop / renderBossBackgroundLoop.
  • UI / weapon icons — preloadUiIcons / getUiIcon, getWeaponIconImg / getOrBakeEmojiIcon.
  • Mission-timer HUD — drawMissionTimerBanner.
  • Damage-tag pill — drawDamageTagPill.
  • Wheel UI — full-screen overlay draw entry consumed by the event-spawn flow.
  • Card theme — paintCardGradient, plus the rarity gradient / badge tables consumed by reward cinematics.

Pattern notes

  • Three rendering tiers stack on every frame: WebGL pre-rendering (nebula → fire → live shaders → sprite batch alpha → glow batch additive, each on its own offscreen canvas) → Canvas 2D world / particles / effects pass (draws to the main canvas while compositing the WebGL canvases via ctx.drawImage) → Canvas 2D HUD pass (unaffected by post-processing). Post-processing sits between the two Canvas 2D tiers.
  • WebGL2 sprite batching is the dominant per-frame primitive for the gameplay layer. Two batches share one atlas — alpha batch for sprites, additive batch for glows — and the additive batch is always flushed AFTER the alpha batch so light layers on top of opaque pixels. The atlas is built programmatically at boot, then patched by the VFX-component runtime before the player ever sees a frame.
  • Canvas 2D is the fallback everywhere: if WebGL2 fails to initialize the renderer drops to polygon shapes via the Shapes registry and stamp blits. The hi-res ship cache, shield-ring stamp cache, glow-stamp tint cache, mote / bokeh / smoke / explosion stamps, baked star layers, baked silhouette layers, baked fbm canvases, baked hex / lat-long underlay tiles, and pre-baked vignette / scanline canvases are all Canvas-2D-only offscreen surfaces.
  • The atlas is the canonical asset surface for any WebGL-batched sprite. New visual content must reach the atlas via reserveAtlasRegionpatchAtlasRegionreuploadAtlas. Player ships and enemy sprites are loaded as standalone PNGs and either patched into the atlas (workbench-baked) or drawn via Canvas 2D from the hi-res cache (legacy ship pipeline).
  • Pre-baked offscreen-canvas stamps are the dominant performance pattern across this system. Every soft-falloff effect — glow stamps, shield rings, dust motes, bokeh, fog tiles, vignette, scanlines, smoke, sticker outlines on weapon icons, hi-res ship cache, baked star layers, baked silhouettes — bakes once and composites via drawImage thereafter. New effects should follow the same path rather than re-creating gradients per frame.
  • The camera follows the ship with linear interpolation (CFG.CAMERA_LERP) and applies heat-zoom + warp-puddle-zoom multiplicatively; shake and zoom pulse are stored as decay state that’s subtracted-then-reapplied each frame so the lerp always anchors on the true target position. Shake / pulse winners are picked by strength (amp * dur and |1-factor| * dur) — a stronger event overrides a weaker in-flight one rather than blending.
  • HUD is held to a strict performance contract: < 2ms per frame, no ctx.clip, no createRadialGradient, no shadowBlur, no globalCompositeOperation changes outside the reward-cinematic overlays. The HUD uses axis-aligned fillRect plus a bounded count of ctx.arc calls for weapon-slot GCD circles. The boss-bar / hit-indicator / portal-indicator / supply-pod-arrow sub-systems all use rectangles + simple paths.
  • Post-FX presets (dark / sunlit) gate the post-processing pipeline by multiplier and toggle — bloom alpha, vignette enable, contrast / saturation boost, god rays. Quality tiers (high / mobile / low) prune the full pipeline down to lite bloom + vignette on mobile and vignette-only under thermal emergency. The PostFxState store layers a second knob set on top: eleven art-style wetness sliders, three layer-FX toggles, five polish knobs — all consumed by the nebula fragment shader at flush time.
  • The parallax system is a four-slot sandwich (back / mid / near / fg) drawn around the gameplay layer: back after the nebula but before terrain, mid before terrain, near after terrain but before sprites, fg after sprites but before HUD. Layers inside a slot sort by depth. Perf tier (high / mid / low) prunes the layer list rather than reducing quality of kept layers — a low-tier scene keeps one starfield + one silhouette + one fbm per slot and drops everything else.
  • The palette system is process-global and revision-numbered: a palette is picked once per mission and never changes mid-mission, but downstream caches (baked silhouettes, baked fbm) key on the revision number so a runtime palette swap (e.g. from the workbench) invalidates them correctly. The palette dictates biome mood; specific entity colors (player ship, enemy archetype, weapon tint) still come from PAL / weapon-colors / shipsv4 diffuse — palette only paints the world.
  • The reward-cinematic registry is the only true plug-in surface in the renderer. Families register a CinematicModule at module-load time; HUD calls the module’s onStart / onUpdate / onStateTransition / drawBackdrop / drawCardOverride / drawOverlay / onEnd hooks during the reward state machine. Unregistered families fall back to the no-op default and get stock card rendering. New reveal animations are added by writing a module and importing it from the index — no HUD edits.
  • Three procedural background systems sit alongside the parallax sandwich, each with its own WebGL2 context, own shader pair, own offscreen canvas, and own resolution scale (nebula 0.75×, fire 0.5×, live shaders 0.5× by default). They never share contexts. The host Canvas 2D composites each one via a single drawImage after that engine’s flush — the WebGL2 canvases are created with preserveDrawingBuffer: true so the host can read the prior flush at any point.
  • Warp puddles invert the usual rendering order: from outside, a clipped nebula canvas shows through a crisp ctx.arc window; from inside, the world outside the puddle union turns jet black and the same nebula canvas becomes the full backdrop. The drawn edge is the gameplay collision radius — visual boundary and collision boundary are pixel-identical, and the additive halo + electric-arc decoration lives strictly on top.
  • Three explicit screen-space-vs-world-space invariants are load-bearing. (1) The renderer applies a setTransform(dpr, 0, 0, dpr, 0, 0) at frame start so game code can write in CSS pixels while output is at device pixels. (2) Every world-anchored draw site that mixes world and screen coordinates routes through Camera.toS rather than re-deriving the transform. (3) The HUD pass clears any transform back to identity (drawVignette is the canonical example) before drawing to ensure full-screen coverage regardless of camera state. Violating any of these produces “draws off-screen / misaligned” bugs.
  • Weapon icons bake a sticker-style outline (15px white inner + 7px black outer) at 512² using a 48-sample unit-circle stamp pass per icon. The bake is one-shot at icon-load time; per-frame draws are a single drawImage of the pre-baked sticker. Modifier upgrade icons share the same pipeline with a mod_ prefix to avoid collision with weapon ids.
  • HUD slot positions are cached per-frame in module-local arrays (_weaponSlotPos / _modSlotPos / _artSlotPos) and consumed by both the upgrade-show slot animation and the external getTopArtifactSlotPos hit query — write-during-draw / read-on-input is the pattern, and it relies on the render-then-input ordering in the harness.
  • The post-FX bespoke store is intentionally not Zustand. The store predates the broader Zustand adoption and exposes both a React useSyncExternalStore hook and a raw subscribePostFx callback used by the nebula RAF (which can’t be a React component). Identity replacement on every set keeps React equality checks cheap.
  • The dust pool, hit-indicator ring, and warning list all use plain arrays with capacity-based early-return on overflow. The only acquire/release pool in this system is the WebGL sprite batch’s instance buffer; everything else is either a fixed-size pre-populated array (dust) or a bounded log (warnings, hit indicators).
  • Removed-feature tombstones are preserved as no-ops or pass-through fields (setLanternFill is a documented no-op; CRT scanline-overlay scratch canvas was deleted but the scanline pre-bake path stayed; boss-state camera coupling was ripped but the call site comment preserves the invariant). Call sites in the bridge still reference these names — deleting them would be a churn cost without behavior benefit.
  • Boss VFX is split: the WebGL nebula bg-loop variant lives here (boss-bg-loop); the composable layer kit (dust motes, refraction sweeps, screen tints, per-swarm cores) lives in engine/vfx. The renderer hosts the bg-loop because it’s a <video> element + drawImage blit; the layer kit is in vfx because it’s a particle-style runtime.
  • Squash-and-stretch is volume-preserving by construction — when sx expands, sy contracts proportionally, and amplitudes are clamped to ~2.5% to stay subtle. The per-archetype wave shape (sine / sawtooth / triangle / pulse) is the load-bearing knob: enemy chargers and shooters get sawtooth / triangle for mechanical feel, the ship gets sine.
  • The medical-canvas palette is a separate font / color system from the main PAL table — used exclusively for the boss bar and adjacent “medical chrome” surfaces. Don’t reuse it for general HUD chrome.