← All docs
Ideas

v0.5 backlog (ordered)

This is the canonical priority order for the next working session. Everything else in docs/ideas/ is supporting analysis. Do these in order. Skipping ahead is fine if a task is blocked, but please update this file when you do.

Effort key: XS (≤30 min) · S (≤2 h) · M (≤half day) · L (≥half day) · XL (multi-day).

1. Dispatch lineage + max-depth guard — S ✅ shipped 2026-04-29

Added causedBy: { nodeId, depth }? to InputFiredEvent. The mapping router drops depth > 8 events with a warn log. New src/features/inputs/dispatchTracker.ts records each resolume-out write so the matching parameter_update rebroadcast can attach causedBy and the depth cap terminates the Resolume echo loop. fireResolumeOutput threads depth through.

Files: src/features/inputs/inputBus.ts, src/features/inputs/dispatchTracker.ts (new), src/features/inputs/useResolumeSubscriptions.ts, src/features/mapping/useMappingRouter.ts.

2. Resolume echo-suppression on resolume-in — M ✅ shipped 2026-04-29

Reuses the dispatch tracker from #1. Per-node echoGuard?: boolean (default true) on ResolumeInNodeData. When a parameter_update matches a recent write within 200 ms, the useResolumeSubscriptions handler drops the emission entirely (instead of just attaching causedBy). Inspector exposes a toggle on the resolume-in node with explanatory copy.

Files: src/store/store.ts, src/features/inputs/useResolumeSubscriptions.ts, src/features/inspector/ResolumeInInspector.tsx.

3. Inbound OSC listener (osc-in node) — M ✅ shipped 2026-04-29

New electron/osc-listener.ts opens a refcounted UDP node-osc Server per (bindHost, port) and broadcasts decoded packets on a new osc:incoming IPC channel. New renderer hook useOscInputs.ts reconciles the listeners with the present osc-in nodes and emits matching packets onto the input bus. Supports and ? address patterns. New node + inspector + topbar button. Default bind is 127.0.0.1 with an explicit "LAN visible" warning when flipping to 0.0.0.0.

Files: electron/osc-listener.ts (new), electron/main.ts, electron/preload.ts, electron/shared/types.ts, electron/types/node-osc.d.ts, src/types/api.ts, src/store/store.ts, src/features/inputs/useOscInputs.ts (new), src/features/mapping/{OscInNode,GraphCanvas}.tsx, src/features/inspector/{Inspector,OscInInspector}.tsx, src/components/Topbar.tsx, src/App.tsx, docs/osc/inbound.md (new).

4. Math / logic nodes — L ✅ shipped 2026-04-29

New transform node kind shipped with the full op set: clamp, scale, invert, curve, hold-timer (tap / hold), double-tap, threshold (high / low), latch, edge (rise / fall / both). Multi-output ops expose multiple right-side handles; MappingEdge.sourceHandle / targetHandle carry the routing. Stateful ops keyed by (transformNodeId, sourceId) reset on hydration. Router rewritten for multi-hop traversal with MAX_DISPATCH_DEPTH = 8 cycle bound. Topbar button + node body + full inspector.

Files: src/store/store.ts, src/features/mapping/{TransformNode,GraphCanvas,useMappingRouter,transformOps,ColorMixEdge}.{ts,tsx}, src/features/inspector/TransformInspector.tsx, src/components/Topbar.tsx, docs/mapping/transform-nodes.md.

5. Dispatch-mode per edge — M ✅ shipped 2026-04-29

MappingEdge.dispatch?: { mode: parallel | sequential | after-prev, delayMs?, requirePrevOk?, order? }. Router groups outgoing edges by source, sorts by order, awaits sequential / after-prev steps, fires-and-forgets parallel ones. New EdgeInspector + edge selection state in the store. ColorMixEdge renders dashed strokes + an order/mode badge for non-default edges.

Files: src/store/store.ts, src/features/mapping/{useMappingRouter,ColorMixEdge,GraphCanvas}.tsx, src/features/inspector/{EdgeInspector,Inspector}.tsx, docs/mapping/dispatch-modes.md.

6. Per-edge throttle override — S ✅ shipped 2026-04-29 (v0.5.1)

MappingEdge.throttleMs? plumbed through fireSink → fireOscOutput / fireResolumeOutput. Edge inspector exposes Toggle + Slider (1..500 ms); default off (engine ~25 ms applies).

7. Cycle detection / warning UI — S ✅ shipped 2026-04-29 (v0.5.2)

detectCycleEdges walks combined static + virtual (resolume parameter equality on the same host:port) edges and marks any static edge whose source is reachable from its target. ColorMixEdge renders amber stroke + ⟳ loop pill when the flag is set. Non-blocking; runtime depth cap + echo guard remain authoritative.

Files: src/store/cycleDetect.ts (new), src/features/mapping/{GraphCanvas,ColorMixEdge}.tsx.

8. Per-sink event-rate gauge — S ✅ shipped 2026-04-29 (v0.5.2)

sinkRateBus side-channel publishes (sinkId, ts) from every fireSink. New useSinkEventRate(nodeId) hook keeps a 1-second ring buffer and refreshes at ~4 Hz. <SinkRateGauge /> pill turns amber > 60/s, red > 200/s. Hidden at 0.

Files: src/features/mapping/{sinkRateBus,SinkRateGauge,useMappingRouter,Output,ResolumeOut}Node.{ts,tsx}.

9. Toggle state cleanup on rebind — XS ✅ shipped 2026-04-29 (v0.5.1)

useMappingRouter now subscribes to graph changes, fingerprints each sink's incoming-edge set + triggerMode, and drops toggleState[sinkId] when the fingerprint shifts. Stale entries for deleted sinks are also dropped.

10. Save debug log to file — XS ✅ shipped 2026-04-29 (v0.5.1)

New log:save IPC handler in main: opens dialog.showSaveDialog, writes JSONL. DebugPanel exposes a Save button next to Copy.

11. Resolume swagger discovery on bridge open — S ✅ shipped 2026-04-29 (v0.5.2)

useResolumeComposition.refresh now fetches /api/v1/docs/swagger.json alongside the composition tree (best-effort; failure is silent). Swagger paths under /composition/... that aren't already represented are emitted as synthetic kind: "param" items so the picker autocompletes effects / dynamic params that the composition tree hasn't hydrated yet.

12. Global hotkeys — S ✅ shipped 2026-04-29 (v0.5.2)

New electron/global-hotkeys.ts reconciles electron.globalShortcut registrations against a renderer-supplied set. Renderer hook useGlobalHotkeys walks input nodes for globalHotkey: true && descriptor.kind === "keyboard", converts each descriptor's code + modifiers to an Electron accelerator, and IPCs the desired set on every change. globalShortcut fires once per press; main synthesizes a release ~120 ms later. InputInspector exposes a Toggle on keyboard-bound inputs.

Files: electron/global-hotkeys.ts (new), electron/main.ts, electron/preload.ts, src/types/api.ts, src/store/store.ts, src/features/inputs/useGlobalHotkeys.ts (new), src/features/inspector/InputInspector.tsx, src/App.tsx.

13. Custom theme editor (in-app) — M ✅ shipped 2026-04-29 (v0.5.3)

src/features/settings/CustomThemeEditor.tsx lives below the built-in
ThemePicker grid. Six seed inputs (bg, surface, accent, accent-2, text, border)
each as a TextInput + clickable swatch (OS color picker). buildCustomTheme
in src/theme/buildCustomTheme.ts derives the full 18-var theme via
chroma-js: surface-2/3 brightened/darkened off surface, border-strong nudged
off border, text-dim/mute mixed toward bg in oklab, glows as rgba off accent.
Mode (light/dark) auto-detects from bg luminance. Every change live-applies
through the existing ThemeProvider. Seeds persist on Theme.seeds? so the
editor re-hydrates on reopen.

14. mDNS / network auto-discovery — M ✅ shipped 2026-04-29 (v0.5.3)

Main runs bonjour-service browsers for _resolume._tcp, _osc._udp, and
_companion._tcp and pushes diffs on peer:up / peer:down.
src/features/discovery/peerBus.ts keeps the renderer-side snapshot via
useSyncExternalStore so 60Hz mDNS bursts don't trigger app-wide re-renders.
src/components/ui/HostPicker.tsx is the drop-in for inspector host fields:
selecting a peer fills both host and port, filtered by service kind. Wired
into ResolumeOut, ResolumeIn, and OSC Out inspectors. Advertising is off.
See network-discovery.md for the full design.

15. OSC monitor node — S ✅ shipped 2026-04-29 (v0.5.1)

New monitor sink kind. Side-channel monitorBus keeps 60 Hz traffic out of Zustand; the node component subscribes to its own id and renders a 120-point auto-scaling sparkline + last 6 raw samples. Refresh throttled to ~30 Hz so it doesn't peg the UI.

v0.5.4 polish — shipped 2026-04-29

After v0.5.3, the competitor-gap.md audit identified three loose ends not in the original numbered backlog:

  • Serial output node. Closes the Resolume → Arduino LED feedback round trip the competitor-gap doc had as "halfway." New serial-out sink kind: port picker (shares with serial-in on the same path), baud rate, payload template with {v} / {V} / {s} placeholders, configurable line ending. Trigger mode (pulse / toggle / hold) honoured for discrete inputs; continuous inputs stream their normalized value through the placeholders. Files: electron/serial-bridge.ts (added serial:write), src/store/store.ts (new SerialOutNodeData), src/features/mapping/SerialOutNode.tsx, src/features/inspector/SerialOutInspector.tsx, src/features/inputs/useSerialOutputs.ts (port reconciler).
  • Wire pulses on traffic. New edgeFireBus emits per-edge dispatch signals; ColorMixEdge re-mounts a sibling halo <path> keyed on a pulse counter so its CSS animation restarts each fire. Single press → single pulse; 60 Hz axis traffic → wire is continuously lit. Idle wires pay nothing.
  • Serial input parser controls. SerialBindUI exposes a Raw bytes toggle and a Delimiter field (decodes \n / \r\n / \r / \t escapes). Previously parser was API-only.

16. DMX integration (multi-step, scoped behind a feature flag) — XL

Last on the priority list. See dmx-feasibility.md for the full study. Phased plan:

  • 16a (M) — main-process DMX backend via enttec-open-dmx-usb or serialport + custom Pro Mk2 frames. One transmitter per device-path:universe.
  • 16b (M)dmx-out node kind: pick universe, pick fixture (or raw channel), set value. Continuous values map to channel 0..255.
  • 16c (L) — fixture profiles (RGB, RGBA, RGBAW, moving head). Borrowed JSON shape from TagTable. Modal for editing.
  • 16d (L) — sequence/pattern node: priority queue, beat clock, simultaneous-vs-exclusive playback. The biggest unknown.
  • 16e (M)dmx-in (Art-Net listener) so an external lighting console can drive XOSC.
Each sub-step is independently shippable.

Cuts (declined)

  • Portable / zero-install build. The user explicitly doesn't want this.
  • Logo / aesthetic redesign. Already done in v0.4.

Bigger structural calls (not in the backlog yet)

These need a user yes/no before they show up as numbered items:

  • Multi-window canvas — open a second window with the same graph, useful when monitoring on a control surface laptop.
  • Plugin system — allow community-contributed node types loaded from a folder. Probably premature; revisit at v0.7.
  • Cloud/remote sync of configs — out of scope per air-gapped event readiness; skip.

Suggested order for the next session

1. Tackle items 1 → 3 in one go (foundation + the most-asked feature, inbound OSC). That's roughly half a day.
2. Then either 4 (logic nodes) for the biggest user-visible jump, or 5 + 6 for the cleanest dispatch story before logic-nodes lands.
3. Cut a v0.5.0 tag once 1–6 are in. 7–15 trickle in as v0.5.x.
4. DMX is its own milestone (v0.6 or v0.7). Don't blend.