diff --git a/docs/superpowers/specs/2026-06-29-matrix-editor-refactor-design.md b/docs/superpowers/specs/2026-06-29-matrix-editor-refactor-design.md new file mode 100644 index 0000000..e269c36 --- /dev/null +++ b/docs/superpowers/specs/2026-06-29-matrix-editor-refactor-design.md @@ -0,0 +1,188 @@ +# Matrix Instrument + Colour Editor Refactor — Design + +Date: 2026-06-29 +Scope: `web_realart/public/control/` (front) + a small `sound_algo/data_only/matrix.scd` engine extension. + +## Context & Goals + +The matrix control surface (`control.js`, 1151 lines, vanilla JS, no build step) drives the +SuperCollider data-only engine over a WebSocket-wrapped OSC bridge. The per-voice +instrument + per-(voice,colour) variation editor has grown tangled. Three goals, +validated with the user: + +1. **Unify instrument + colour editing** into one coherent per-voice editor. +2. **Better live UX** on **desktop** (mouse/keyboard, large screen) — keep the grid visible. +3. **Maintainability** — split the monolith into ES modules, single source of truth. + +Two feature additions requested during design: + +4. **Adaptive step sequencer** — note pitch (piano-roll) for melodic voices, drum-machine + (on/off + accent) for percussion, with a per-voice toggle. +5. **A SYNTH/FX tab** to edit the instrument's parameters and effects (full scope: + cutoff, resonance, reverb send, pan, amp, octave, stretch — requires a small engine + extension since `res`/`rev` are not yet driven statically). + +## Current State (frictions, from code audit) + +- **Instrument chosen in two places**: the `.minst` dropdown in each grid row (per-voice, + `matInst`) AND the per-colour `inst` field inside the modal — no shared widget. +- **`colordef-modal` is a full-screen overlay** (`position:fixed; inset:0`) → it hides the + grid while editing. +- **Six-colour palette triplicated**: CSS `.mcell.m1..m6`, CSS `.cd-row/.seq-row`, and JS + `COLORS` array — must diverge eventually. +- **Monolithic OSC inbound handler** (~250 lines, l.34-286) mutates every state global and + calls every renderer — the central coupling hub. +- **Two voice-addressing schemes**: grid/colordef use index `vi`, mixer uses voice-name. +- **Engine params**: SynthDefs accept `cutoff, res, pan, rev` (reverb send); the matrix only + pushes `cutoff`/`pan` statically (gated by `~matModTargets`), `rev` only via modulation, + `res` not at all. + +## Design + +### A. Voice editor — inline accordion with tabs + +Each grid voice row becomes: + +``` +[label] [instrument ▾] [▷] ■·■·■·■·… (64 colour cells) +``` + +- **One instrument dropdown per voice** (removes the grid/modal duplication — the unification). +- Clicking **▷** (or the label) **expands an editor row directly under the voice**, in the grid. + **Only one voice expanded at a time** (opening another collapses the previous) so height stays + bounded with 22 voices. The grid stays visible — no full-screen modal. +- The `colordef-modal` overlay is **removed**. +- The expanded editor is **tabbed**: + - **STEPS** — the adaptive step sequencer (section B), for the currently selected colour. + - **SYNTH/FX** — instrument params + effects (section C), for the currently selected colour. + - **MOD / POSE** — capture-modulation (source→target→depth) and pose bindings + (poseId→action), as today, grouped in one tab. +- A **colour selector ①–⑥** sits above the tabs; it sets the active colour all tabs edit. +- **AUDITION** button moves from the modal head into the editor row. +- **Advanced (collapsed by default): per-colour instrument override.** The engine supports + `spec.inst`; expose it as an optional "advanced" disclosure, default "= voice instrument". + +### B. Step sequencer — adaptive Pitch / Drum + +The step model is unchanged: `steps[s] = {degree, vel}` or `null`, 16 steps, sent via +`/matrix/step vi colour step degree vel`. This is purely a better UI over the same data. + +- **Pitch mode (piano-roll)** for melodic voices: vertical axis = scale degrees (default ~2 + octaves, vertically scrollable for very low `sub` / high `bells`), horizontal = 16 steps. + Click places/moves a note at (step, degree); velocity via wheel or a per-step level. Shows + the actual melody contour. +- **Drum mode (beat box)** for percussion: 16 on/off pads + accent/velocity (no pitch; + `degree` written as 0). +- **Per-voice toggle Pitch/Drum** with a smart default by voice class (section below). + `perc`/`tom` default to Drum but can switch to Pitch (the engine treats them as pitched). + +### C. SYNTH/FX tab (+ small engine extension) + +Per **(voice, colour)** the tab edits: **cutoff, resonance (res), reverb send (rev), pan, +amp, octave, stretch**. `cutoff/res/rev/pan` are shown only for voices whose SynthDef accepts +them (gated, see classification). + +Engine extension (`matrix.scd`) — minimal, the SynthDefs already accept `res`/`rev`: + +1. `~matDefaultColorDefs`: add `res: nil, rev: nil` to each colour def. +2. `~matVariationOverlay` and `~matColorStepPattern`: push `\res` and `\rev` to the Pbind when + non-nil and the voice supports them (same pattern as the existing `\cutoff`/`\pan` push). +3. `~matLoadFile`: add `\res, \rev` to the per-colour field copy list (currently + `[\stretch,\octave,\amp,\inst,\cutoff,\pan]`). +4. OSC: `/matrix/colordefs` push and `/matrix/colordef vi colour field value` set already carry + arbitrary fields — extend the bulk-push arg layout to include res/rev; `setCD` works as-is. + +No new FX buses (delay/distortion were considered and deferred — see Non-goals). + +### D. Code architecture — ES modules + +`index.html` switches `control.js` to `