PURPOSE

VFX tab inside the Ship Playground. Live-edits darkness/lighting, fog layers (below + above ships), particle dust + bokeh, post-processing knobs, and biome/level-design VFX (4-color palette, spoke style, hub style, illumination + lantern preset). Pushes config to the running mission in real time and supports save/load of planet presets.

OWNS

  • Local section open/close flags (biomeOpen, darknessOpen, fogOpen, particleOpen, paletteOpen, spokeStyleOpen, hubStyleOpen, illuminationOpen).
  • Local biome + nebula selection (biome, nebulaIdx).
  • Local FPS sampler (rAF counter + 1s interval to update fps).
  • Local clipboard-confirmation flag (copied).
  • Internal Toggle and ColorPicker subcomponents.
  • Static option lists: ALL_ARCHETYPE_INDICES, BIOMES, SPOKE_STYLES, HUB_STYLES, HUB_PARTICLES, LANTERN_PRESETS.

READS FROM

  • Props (TabProps): missionRef, vfxState (aliased vs), setVfxState (setVs), levelConfig (lc), setLevelConfig (setLc).
  • DEFAULT_VFX_STATE and DEFAULT_LEVEL_CONFIG as fallbacks when state is undefined.
  • ARCHETYPES from ../../data/nebula-archetypes (length + per-index name).
  • Shared styles BTN_STYLE, LABEL_STYLE and shared Panel + StatRow from ./PlaygroundShared.
  • LevelConfig type and id types SpokeStyleId, HubStyleId, HubParticleType, LanternPresetId from ../../data/level-config.
  • pushStats from ../../services/playgroundPush for preset save.

PUSHES TO

  • VFX state via setVs(prev => ({ ...prev, ...patch })) (helper set).
  • Level config via setLc(prev => ({ ...prev, ...patch })) (helper setC).
  • Mission handle methods on missionRef.current:
    • sandboxRegenerateWorld(biomeId) on biome change and on REGENERATE TERRAIN.
    • sandboxSetNebula(index) on nebula cycle.
    • sandboxSetLighting({ enabled, darkOpacity, ambientLight, baseRadius, dimWidth, enemyLights, bulletLights }).
    • sandboxSetPostProcessing({ bloomAlphaMult, contrastBoost, saturationShift }).
    • sandboxSetFog({ fogBelow, fogAbove, fogBelowAlpha, fogAboveAlpha, dustEnabled, bokehEnabled, fogBelowTex, fogAboveTex, fogBelowTintR/G/B, fogAboveTintR/G/B, fogShipCutout }).
  • navigator.clipboard.writeText(...) for export and preset save fallback.
  • pushStats({ target: 'vfx-preset', id: 'current', patch: s }) to persist current VFX stack.
  • window.prompt / window.alert for the import JSON flow.

DOES NOT

  • Does not own VFX state or level config (lifted to ShipPlaygroundScreen).
  • Does not render the mission canvas or any gameplay surface.
  • Does not implement darkness, fog, dust, bokeh, lighting, or post-processing — only forwards parameters via the mission handle.
  • Does not write to Supabase, telemetry, or any persistent store besides the playgroundPush dev endpoint and the system clipboard.
  • Does not validate JSON imports beyond a try/catch around JSON.parse.
  • Does not include the previously planned bloom/contrast/vignette post-processing UI (cut, per trailing comment).

Signals

  • Biome <select> calls changeBiome(id), which resets nebulaIdx to 0 and triggers sandboxRegenerateWorld.
  • Nebula chevrons call cycleNebula(±1) or cycleNebula(±10), wrapping modulo ARCHETYPES.length and pushing sandboxSetNebula.
  • Three useEffects push lighting, post-processing, and fog/dust to the mission whenever their dependency slice of s changes.
  • Fog hex tints are parsed to { r, g, b } integer triplets before being sent (fallback values 255/255/255 if the hex is malformed).
  • SAVE PLANET PRESET attempts pushStats first, falls back to clipboard; LOAD reads a pasted JSON string via prompt and merges it into VFX state.
  • FPS sampler runs a requestAnimationFrame loop counting frames; once per second a setInterval divides by elapsed seconds and updates the displayed FPS (cleaned up on unmount).
  • FPS color: green at >= 100, amber at >= 50, red below 50.
  • select and similar inputs stop key propagation so playground hotkeys do not fire while typing.

Entry points

  • Default-exported VfxTab(props: TabProps) React component. Rendered by ShipPlaygroundScreen when the active tab is 'vfx'.

Pattern notes

  • State-lift pattern: VFX and level config live on the parent so values survive tab switches; section open flags stay local because resetting them on switch is acceptable.
  • set and setC callbacks use functional updates (prev => ({ ...prev, ...patch })) to avoid stale-closure issues across the many handlers.
  • Level Design controls are wrapped in an IIFE that destructures lc ?? DEFAULT_LEVEL_CONFIG once, keeping cfg and setC local to that sub-tree.
  • Inline styling throughout (no CSS module). Color accents per panel: purple #a855f7 for biome/hub, indigo #6366f1 for below-fog, violet #8b5cf6 for above-fog, blue #60a5fa for spoke, amber #fbbf24 for lantern.
  • Toggle visual state communicated via tinted background and matching border color derived from the prop color.
  • Trailing comment marks the post-processing UI as intentionally removed; the underlying sandboxSetPostProcessing call still fires from state and stays at default values.