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
Rendererorchestrator — main canvas 2D context handle, DPR transform, viewport clip, layer registry (background/world/particles/effects/hudbyzIndex), one-shot sprite-batch init, atlas upload, world↔screen coordinate helpers, and the per-framebeginFrame/endFrame/renderlifecycle that sequences background → world → particles → effects → optional lighting → HUD.Camera— follow-cam targeting state, heat-driventargetZoomramp, 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 thetoS/toSx/toSy/toWprojection helpers consumed by every screen-space draw site.PALpalette constant — flat hex/rgba table used as a baseline color reference bydraw.tsand 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 seededsr()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
SpriteBatchpair — 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_SPRITEScap with silent overflow drop,beginFrame/add/flushlifecycle, premultiplied-alpha math at insertion time, and theflush('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/reuploadAtlashooks 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.
Shapesregistry — name → polygon list for player, XP canister, coin, genesis station, the four base enemy archetypes (orb / charger / shooter / mortar), plus extras; consumed bydraw.tspolygon fallback and by the enemy-orbit hull cache in vfx.Camerashake / zoom-pulse latches (_shakeAmp/_shakeT/_zoomPulseFactor/_zoomPulseT/_zoomPulseEasing) and the per-frame_shakeLastX/Y/_zoomPulseLastDeltacarry-over fields that let next frame’s lerp anchor on the true (unshaken) camera position.PostProcessingpipeline — 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 andVIGNETTE_MARGINoverdraw, 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 bywindow.__vignetteOpacityfor the simpler always-on darkness pass (distinct from the post-processing preset-gated vignette). PostFxStatestore — 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);useSyncExternalStorehooks for React subscribers and asubscribePostFxraw 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
LiveShaderRuntimefor 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/drawWhiteGlowCanvas-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_glowsbatch, plus a Canvas-2D fallback that converts world→screen viaCamera.toSand writes directly to the main ctx inlighterblend. - 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/sygetters consumed bydraw.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,
paintCardGradienthelper 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 →
CinematicModulemap populated at module-load time (level_up/weapon_box/artifact_box/shooting_star), per-frameCinematicContextsnapshot shape,onStart/onUpdate/onStateTransition/drawBackdrop/drawCardOverride/drawOverlay/onEndhook 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 bydepth, 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/core—W/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_ENEMYconfig constant, render-diag pass timers, smoothed-fps reader.engine/core/device-capabilities—isMobile()for star-density, post-processing-quality, and per-recipe perf-tier gating.engine/core/types—WorldState,ShipRarity,EnemyEntityshapes.engine/world/leveling—XP_THRESHOLDSfor 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-puddles—WarpPuddleGroupshape for the puddle render adapter.engine/player/states—hasExclusiveStatefor 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/juice—ShipRecoilcombined sx/sy for the ship draw transform.engine/affixes— affix VFX color lookup via the enemy-orbit pass.data/weapons—WEAPON_MAP,resolveWeaponRarity,DamageTagtype, weapon-emoji map.data/weapon-icons— icon presence + path lookup.data/modifiers—MODIFIER_TYPE_MAPfor HUD modifier slot rendering.data/artifacts—ARTIFACT_MAP+ tier color table for the HUD artifact column.data/bosses—BOSS_DEFSfor boss-bar display-name / bar-color,BossBackgroundLoopshape for the bg-loop module.data/kill-streaks—STREAK_MILESTONESfor HUD killstreak escalation.data/planet-config—PostProcessingModetype wired to the post-processing preset switch.data/nebula-archetypes— archetype list, star-color / star-type / surface-mode tables, planet archetype index lookup, plusArchetypetype 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-presetstable 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 viactx.save/ draw ops /ctx.restore, plusctx.drawImageblits 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: trueso the nextdrawImageto 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
_listenersset — 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
reuploadAtlason 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
CinematicContextand 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._rawDtas 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
Rendererclass —new Renderer(canvas)constructor builds the layer registry and initializes both WebGL sprite batches,beginFrame/endFramefor the per-frame lifecycle,render(...)for the top-level orchestrator that sequences background → world → particles → effects → optional lighting → HUD,resizefor viewport changes,getCanvas/getContext/getDimensionsfor harness wiring,worldToScreen/screenToWorld/isVisible/getVisibleBoundsprojection helpers,setViewportClip/restoreViewportfor explicit clipping,createLayer/getLayer/addToLayer/clearLayersfor the (mostly-unused) layer registry,webglReadyflag.safeRadialGradient— defensivecreateRadialGradientwrapper 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.drawplus the parallax / palette / nebula barrel exports surfaced throughindex.ts.- Sprite-batch barrel —
initSpriteBatch/getSpriteBatch/getGlowBatchplus theSpriteBatchclass 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.tsexports —setActiveShipRarity/setActiveHullClassplus the per-entitydrawShip/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.LiveShaderRuntime—init/setUniforms/render/getCanvas/resizeplus thecone-beam-rendergetConeBeamRuntime/resizeConeBeamRuntimeadapter.WarpPuddlerender —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 bywindow.__vignetteOpacity.- Post-FX store —
getPostFxState/getPostFxValue/setPostFxValue/getPostFxIdentity/subscribePostFx/usePostFxState/usePostFxValue. - Glow primitives —
drawGlow/drawWhiteGlowplus the additive helpersaddGlow/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, theCinematicContext/CinematicModule/RewardStatetypes, andplayCinematicTone. - Palette system —
setActivePaletteByPresetId/setActivePaletteForBiome/resolvePaletteSlot/getActivePalette/getPaletteRevision. - Parallax system —
setBiome/setPerfTier/drawSlot/disposeParallax/getActiveBiomeId; silhouette-stamp disposer for the cache. - Gameplay-grid —
drawGameplayUnderlayinvoked by the bridge after parallaxfgand 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
Shapesregistry 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
reserveAtlasRegion→patchAtlasRegion→reuploadAtlas. 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
drawImagethereafter. 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 * durand|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, nocreateRadialGradient, noshadowBlur, noglobalCompositeOperationchanges outside the reward-cinematic overlays. The HUD uses axis-alignedfillRectplus a bounded count ofctx.arccalls 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. ThePostFxStatestore 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:backafter the nebula but before terrain,midbefore terrain,nearafter terrain but before sprites,fgafter sprites but before HUD. Layers inside a slot sort bydepth. 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
CinematicModuleat module-load time; HUD calls the module’sonStart/onUpdate/onStateTransition/drawBackdrop/drawCardOverride/drawOverlay/onEndhooks 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
drawImageafter that engine’s flush — the WebGL2 canvases are created withpreserveDrawingBuffer: trueso 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.arcwindow; 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 throughCamera.toSrather than re-deriving the transform. (3) The HUD pass clears any transform back to identity (drawVignetteis 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
drawImageof the pre-baked sticker. Modifier upgrade icons share the same pipeline with amod_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 externalgetTopArtifactSlotPoshit 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
useSyncExternalStorehook and a rawsubscribePostFxcallback 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 (
setLanternFillis 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 inengine/vfx. The renderer hosts the bg-loop because it’s a<video>element +drawImageblit; 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
PALtable — used exclusively for the boss bar and adjacent “medical chrome” surfaces. Don’t reuse it for general HUD chrome.