PURPOSE
Playground tab UI for the three-layer post-FX pipeline. Surfaces curated full-game art-style presets, optional layer FX (BG stars, color sparkles, shooting stars), and a final-pass polish stage (blur, HSL, anim speed). All controls are sliders/toggles that map directly to entries in the post-FX store; changes apply to every nebula-rendered pixel via the FS1/FS2 fragment shaders.
OWNS
- Default export
FxTab— the tab component rendered inside aPaneltitled “POST-FX”. - Internal helpers
SliderRowandCheckboxRowfor the two control row variants. SliderEntryinterface plus theART_STYLESandPOLISH_KNOBSarrays — the curated tables that drive the Art Styles and Polish sections.- Local style constants (
ROW_STYLE,LABEL_STYLE,VALUE_STYLE,DESC_STYLE,SECTION_HEADER_STYLE). - Local UI state
layerOpen(the Layer FX foldout open/closed flag) viauseState. - Per-slider visible-to-internal value mapping: visible range 0..100 integer, stored as 0..1 float.
READS FROM
usePostFxStatehook — subscribes to the fullPostFxStateobject fromengine/rendering/post-fx-store. Each row reads the matching key offstate(e.g.state.borderlands,state.polishBlur,state.layerBgStarsOn,state.layerShootingStarsAngle).getPostFxIdentity— fetches the per-key identity/reset value used for polish knobs and the shooting-stars angle reset target.PostFxStatetype — constrainsSliderEntry.keyso only valid store keys appear in the curated tables.Panelfrom./PlaygroundSharedfor the outer collapsible container shell.
PUSHES TO
setPostFxValue(key, value)— single mutation entry point for every slider, toggle, and reset button. Used to:- Drive art-style slider values (0..1) and reset them to
0. - Toggle
layerBgStarsOn/layerColorSparklesOn/layerShootingStarsOnbetween0and1using a>= 0.5threshold. - Drive each layer’s intensity/density/angle/variance sliders and reset them (intensity-style knobs reset to
0.5, variance to0, angle to its identity). - Drive each polish knob and reset it to its
getPostFxIdentityvalue.
- Drive art-style slider values (0..1) and reset them to
DOES NOT
- Does not own or mutate post-FX defaults — the store owns identity values and the schema.
- Does not render any nebula, sparkle, or shader output itself; it only edits store state that downstream shaders read.
- Does not persist preset state outside the store (no localStorage, no URL params, no telemetry).
- Does not gate on layer toggles in any way other than visually — toggling a layer just writes 0/1 to the store.
- Does not handle keyboard input, drag interactions, or touch-specific affordances beyond the native
<input type="range">element. - Does not import or read any other tabs; rendering of the FX tab elsewhere is the playground shell’s responsibility.
Signals
- Layer foldout caret swaps between
▾(open) and▸(closed) based onlayerOpen. - Layer toggle buttons render
☑ ON(amber#fbbf24background, dark text) when active,☐ OFF(translucent white background, dim text) when inactive. Active state is read asstate.layer<X>On >= 0.5. - Each slider displays a right-aligned 0..100 integer readout in amber monospace.
- Reset button (
↺) appears whenever a row passes anonResetcallback; itstitleattribute shows the integer reset target derived fromidentityValue. - Section dividers come from
SECTION_HEADER_STYLE(uppercase letterspaced label with a top border): “Art Styles”, “Layer FX”, “Polish”. - Per-style descriptions render in a dim monospace block immediately under the slider for
ART_STYLESentries andPOLISH_KNOBSentries that supplydesc. - Shooting Stars block adds an extra footnote describing how Variance interpolates between parallel rain (0) and fully random per-streak angles (1).
Entry points
- Default export
FxTab— the only entry point; mounted by the Playground screen as one of its tabs. SliderRow/CheckboxRow/SliderEntryare file-local; they are not exported and have no callers outside this module.
Pattern notes
- Curated-table pattern: art styles and polish knobs are declared as
SliderEntry[]constants and rendered with.map(...); adding a new preset is a one-row edit plus a new key onPostFxState. - Three-section layout corresponds 1:1 to the pipeline stages described in the file header: Art Styles (preset full looks), Layer FX (additive extras, foldout), Polish (final pass).
- Visible-vs-internal split: the UI consistently shows 0..100 while the store stores 0..1. Conversion happens at the
SliderRowboundary (pct = Math.round(value * 100)for display,parseInt / 100for write). - Boolean-as-float convention: layer toggles are stored as numeric 0/1 values and interpreted via
>= 0.5. This keeps every store entry uniformly typed as a number even when the semantic is binary. - Reset semantics differ by knob type. Art-style sliders reset to 0 (no preset applied). Layer intensity/density sliders reset to 0.5. Layer variance resets to 0. Polish knobs and shooting-stars angle reset to
getPostFxIdentity(key)so the store decides identity. - Typed cast
as neveris used at everysetPostFxValuecall site becausekeyis a wide union overPostFxState; the call site has already constrained the value type via the slider range. - The outer
Panelis rendered withopen={true}and a no-oponToggle, i.e. the FX panel is non-collapsible within the tab — the shell-level tab switcher gates visibility instead. - Styling is inline-only via
React.CSSPropertiesconstants; no CSS module, no Tailwind. Accent color#fbbf24(amber) is used consistently for active state, readout, and slider thumb.