Files
2026-05-27 23:29:53 +01:00

4.6 KiB

@suaveplan/react-control-surface — 2D atomic controls + runtime

  • Target path: packages/ui/react/react-control-surface/
  • Tier: browser/react
  • Date: 2026-05-23
  • Status: PROPOSED

Why

@suaveplan/control-surface-schema defines the JSON; @suaveplan/audio-fx-plugin-base defines the parameter machinery; @suaveplan/react-audio provides useParameter for chain-driven automation. What's missing is the actual atomic React components — <Knob>, <Slider>, <Button>, <Meter>, etc. — plus a runtime that walks a ControlSurface document and renders the live DOM tree, wiring every control's onChange to its bound consumer (plugin parameter, MIDI CC, transport state, DMX channel).

This is the 2D renderer. The 3D R3F sibling (@suaveplan/r3f-control-surface) reads the same schema; both stay in lock-step via the shared BoundControlProps contract.

What changes

  1. NEW package @suaveplan/react-control-surface at packages/ui/react/react-control-surface/.
  2. BoundControlProps — the shape every control component implements: { value: number; onChange: (v: number) => void; min: number; max: number; step?: number; default?: number; disabled?: boolean; label?: string; ariaLabel?: string; renderHint?: "auto" | "2d" }. Pure value-in / value-out — no audio knowledge.
  3. Atomic controls (each is a feature directory with .tsx + .types.ts + .test.tsx + .stories.tsx + .md):
    • <Knob> — rotary, drag-vertical-to-change, double-click-resets, keyboard-arrow-step, fine-mode (Shift), aria-slider, range mapping via curve (linear/log/exp/custom)
    • <SliderV>, <SliderH> — vertical/horizontal sliders with detents
    • <ButtonMomentary>, <ButtonLatching> — press-and-hold / toggle behaviours
    • <Switch2>, <Switch3> — two- and three-position toggles
    • <LED> — output indicator (boolean or 0..1 brightness)
    • <DigitalDisplay> — 7-segment or LCD-style readout for numeric / text / scrolling
    • <MeterVU>, <MeterPeak> — single-channel level meters with optional peak-hold + ballistic curves
    • <MeterSpectral> — multi-band spectrum visualisation (consumes @suaveplan/audio-analysis-spectrum data)
    • <XYPad> — 2-axis touch surface
    • <JogWheel> — endless rotary with velocity
    • <PianoKey> — single piano key with state (idle/pressed/highlighted)
    • <DrumPad> — velocity-sensitive pad with optional aftertouch via pointer pressure
    • <TextLabel>, <Image> — static decorative elements
  4. Grouping primitives — <Group>, <Rack>, <Panel>. Provide layout (grid/absolute/stack) and aria-labelling.
  5. Runtime — <Surface schema={ControlSurface} bindings={BindingRegistry} />. Walks the schema, renders each node via the atomic component for its kind, and wires each control's onChange to its binding consumer via the registry.
  6. BindingRegistry — discriminated dispatcher consumers register handlers with:
    • pluginParam → calls useParameter from @suaveplan/react-audio
    • transport → calls transport hooks
    • midiOut → calls useMidiOutput from @suaveplan/react-midi
    • midiIn → subscribes via useMidiInput, drives the control's value back
    • dmx → calls into a @suaveplan/react-dmx hook (deferred — gated by feature flag)
    • compound → fans out to multiple handlers
  7. Skin contract — <SurfaceSkinProvider id="native-instruments"> injects CSS variables consumed by every atomic control. Skin packages (control-surface-skin-*) supply the variables.
  8. Accessibility — every control is keyboard-operable, ARIA-labelled, supports prefers-reduced-motion, exposes role="slider" / role="switch" / etc. with proper aria-valuenow / aria-valuetext.
  9. Error codes — REACT_CONTROL_SURFACE.{UNKNOWN_KIND, BINDING_HANDLER_MISSING, INVALID_SCHEMA} via defineCodes.
  10. OTel — counter control_surface_interactions_total{kind}, histogram control_surface_render_duration_ms.

Impact

  • New browser-tier React package; depends on @suaveplan/control-surface-schema@workspace:^, @suaveplan/react-audio@workspace:^, @suaveplan/react-midi@workspace:^, React 19 peer.
  • Unblocks: react-control-surface-editor, r3f-control-surface (mirrors this surface 1:1 in R3F), every downstream DAW/instrument UI.
  • The 3D sibling reuses BoundControlProps, BindingRegistry, and the schema — only the rendering layer differs.

Out of scope

  • 3D atomic controls — r3f-control-surface (Wave 2b).
  • Drag-and-drop layout editor — react-control-surface-editor (Wave 4).
  • Skin packages — separate per-device families.
  • Network replication — network-control-surface (Wave 3).