W-07 · Case study
nexus
TouchDesigner is extraordinarily capable and intimidating in equal measure. nexus wraps it rather than replacing it.
- In development
- TouchDesigner 2023.11+, macOS 12+ or Windows 10+
- frontend/ · backend/ · shared/ · td/
- No IPC channel without a shared type first
- Electron · TypeScript · React · Zustand · WebSocket · TouchDesigner · pnpm workspaces
Thesis
Wrapping a professional tool beats reimplementing it.
§ 01
The problem
For someone who wants a reactive visual for a clip or a set, node graphs, operators and shaders are out of proportion to the goal. The capability exists; the interface prices people out.
§ 02
The system
An Electron app with a strict split. The renderer (React and Zustand) owns the control panel, template selector and export button. The main process owns the TouchDesigner engine client, IPC handlers and a capture manager. They meet only through a preload bridge. On the TouchDesigner side, a WebSocket DAT in server mode receives parameter updates — so the integration is a protocol, not an embedding.
Diagram 01
Three runtimes, one contract
The user changes a template parameter.
Sliders, pickers and toggles mapped from the template's parameters.
Every stage, in words
- 01Control (renderer · React + Zustand) — Sliders, pickers and toggles mapped from the template's parameters.
- 02Bridge (preload · contextBridge) — The only path between renderer and main; channels typed in shared/.
- 03Engine client (main · TDEngine) — WebSocket client managing the TouchDesigner session.
- 04TouchDesigner (WebSocket DAT · .tox) — Receives parameter updates, renders, reports state back.
- 05Capture (main · CaptureManager) — Exports the result as a short clip.
§ 03
The decision that keeps it honest
Every IPC channel name and every WebSocket message shape lives in a shared package, and no channel may be added without updating it first. With three runtimes — renderer, main process and TouchDesigner — untyped messages would turn every protocol change into a silent break.