---
kind: finding
date: 2026-04-12
immutable: true
---
# Playground Redesign — 2026 (Design Intent)
> **Purpose of this doc.** Captures design intent of an unfinished playground UI
> redesign that lived as uncommitted changes in a `MEMLNaut-NISPS_rewound`
> working copy. The implementation will not be merged into the current vanilla
> playground — the playground is being rewritten in SolidJS — but the *design*
> shouldn't be lost. Read this as a reference for the SolidJS rewrite, not as
> a description of current code.
>
> **Source snapshot.** `archive/playground-redesign-2026-snapshot` branch in
> this repo. Single commit `287e3a1` on top of `ddb6b77`. Three files:
> `playground/a-immersive.html`, `playground/css/a-immersive.css`,
> `playground/js/a-app.js`. ~1200 lines net of changes.
>
> **Status.** Reference-only, immutable. Do not merge into the vanilla playground — those
> files have moved on independently in `main` and a textual merge would be
> meaningless.
---
## Context
The pre-redesign playground UI was organised around a collapsible **bottom
sheet** containing a sticky **floating bar** (mode toggles, train/randomize
buttons, follow toggle, expand chevron). The sheet would expand upward to show
synth/visual controls. RL thumbs-up/down floating buttons sat just above the
collapsed sheet at `bottom: 92px`.
The redesign moves to a **right-side macOS-style dock + drawer system** and
makes the **top heatmap strip directly interactive**. It also adds **undo** for
RL actions and a **per-parameter override system** that unifies how visual-mode
and synth-mode parameters can be constrained.
There are five themes, summarised here in decreasing order of architectural
weight.
---
## Theme 1 — Right-side dock + drawer system
### What changed
Replaces the bottom-sheet pattern with a vertical dock pinned to the right edge
of the viewport, plus a stack of named drawers that open to the left of the
dock when their dock icon is activated.
- **Removed:** `
`, `
`,
the sheet expand/collapse chevron, sheet-internal sticky mode bar, sheet
status line.
- **Removed JS:** `wireBottomSheet()`, `$bottomSheet`, `$sheetContent`,
`$floatingBar`, `$chevronBtn`, `sheetExpanded` state.
- **Added:** `
` with five icon buttons:
- `data-drawer="training"` — Training (Add Example, Train, Clear Ex, Clear
All, Randomize, plus preset chips: Calm/Chaos, Rainbow, Vortex, Spiral,
Embers; loss-canvas section)
- `data-drawer="mode"` — Input/Output mode toggles (was the floating-bar
pill toggles: Joystick/Hands and Visual/Synth)
- `data-drawer="synth"` — Synth controls
- `data-drawer="params"` — Engine parameters (see Theme 4)
- `data-drawer="help"` — Help
- **Added:** `
` containing one
`
` per icon, each with a
`drawer-header` (title + close ×) and `drawer-body`.
- **Added JS:** `wireDock()` to hook up icon clicks and drawer close buttons.
### Inferred intent
- Reclaim canvas real estate. The bottom-sheet ate vertical space whenever
expanded; a side dock keeps controls flush to one edge and only one drawer
opens at a time.
- Surface mode controls (Visual/Synth, Joystick/Hands) without forcing the
user to expand the sheet. Each function lives behind one click on a labeled
icon.
- macOS-dock visual idiom is a familiar metaphor — users intuit
"click icon → panel appears."
- The action-row inside Training drawer (Add Example / Train / Clear Ex /
Clear All / Randomize) is grouped by purpose rather than by toolbar real
estate constraints, so destructive/dim variants (`.action-btn.dim`) read
as secondary.
### Key UX flow
1. Canvas is fullscreen by default, no chrome over it except top heatmap, RL
buttons, joystick, and the right dock.
2. User clicks dock icon → corresponding drawer slides in from the right
(between dock and canvas), other drawers close.
3. User clicks ✕ on drawer header *or* the same dock icon again → drawer
closes, canvas is unobstructed.
4. Mutually exclusive drawers (only one open at a time) keeps the visual
hierarchy simple.
### Implementation notes for SolidJS rewrite
- This is the largest theme and pulls HTML, CSS, and JS together. In SolidJS
it should naturally become a `` component listing icon → drawer
pairs, plus a single `` rendering whichever is active from a
signal.
- CSS custom properties `--dock-width: 48px` and `--dock-gap: 8px` are
introduced; layout (joystick, hand-pip, dev-panel) is offset by these.
- Old positioning constants — joystick & hand-pip at `bottom: 136px`, RL
buttons at `bottom: 92px` — were tied to the collapsed bottom sheet's
88px height. With no sheet, they shift down (joystick/hand-pip to 100px,
RL buttons to 36px). The rewrite should drive these from layout, not
hardcode.
- `dev-panel` shifts right edge from `right: 16px` to
`right: calc(var(--dock-width) + 24px)` to clear the dock.
---
## Theme 2 — Interactive heatmap strip
### What changed
The top heatmap strip — previously read-only visualization of model outputs —
becomes the primary parameter input surface.
### Two interactions per cell
1. **Drag** (pointermove with motion threshold > 3px) horizontally across a
cell → directly sets that parameter's value to the normalized x-position
within the cell.
2. **Click** (pointerup with no motion) → toggles a per-parameter popup with
override controls (min, max, curve, freeze).
### Tooltip
Existing tooltip extended:
- During hover (no drag): shows `{name}: {value}` with a `▾` indicator hinting
the bar is clickable.
- During drag: stays visible and updates live as user scrubs.
- New CSS: `.heatmap-cell.dragging` with `cursor: ew-resize`,
`.heatmap-cell-bar { pointer-events: none }` so child bar doesn't intercept
parent pointer events.
### Popup contents
Per-parameter popup (`#param-popup` or similar) built on demand for the
clicked param:
- min slider (range 0–1)
- max slider (range 0–1)
- curve slider (range 0–1, see Theme 4)
- freeze toggle — when frozen, dragging the heatmap bar updates a *fixed
value* directly rather than going through the model
- close button / clicks-outside dismissal
- show/hide handled with `popupHideTimer` (300ms grace period to let user
move pointer from heatmap cell into the popup without dismiss-on-leave)
### New JS
- `setHeatmapValue(paramIndex, e, cell)` — converts pointer x to normalized
value, applies override semantics (writes to `ov.fixedValue` if frozen),
routes outputs, syncs UI.
- `showParamPopup(paramIndex, cell)` — positions popup near the cell.
- `hideParamPopup()` — clears active popup state.
- `wireParamPopup()` — wires popup controls to update underlying override.
- Tracking globals: `activePopupParam`, `popupHideTimer`.
### Inferred intent
- Make the heatmap a control surface, not just a readout. Saves a trip to
the bottom-sheet/drawer for common adjustments.
- Per-parameter freeze enables hybrid workflow: freeze a few params at fixed
values while letting the model drive the rest.
- Popup is in-context (anchored to the bar) rather than in a side panel,
reducing eye travel.
### Key UX flow
- **Quick adjust:** point at heatmap bar → drag horizontally → value changes
live, model output reroutes immediately.
- **Override setup:** click bar → popup opens → adjust min/max to constrain
range, or freeze at fixed value, or shape curve (see Theme 4) → click
outside or wait for cell pointer-leave to dismiss.
### Notes
- `.heatmap-cell-frozen` CSS class dims frozen cells visually so user knows
what's manually overridden.
- Drag detection uses 3px motion threshold to disambiguate click-vs-drag.
- Pointer capture (`setPointerCapture`) ensures drag continues even if
pointer leaves the cell.
---
## Theme 3 — Undo
### What changed
Adds an undo button below the RL thumbs-up/down buttons, with a 20-step
history of weight snapshots taken before each RL action.
### Implementation
- New HTML: `