patchtogether.live
Performers patch a shared rack live in their browser. The patch graph is the canonical state — audio (Web Audio + AudioWorklet) and video (WebGL2 fragment shaders) are renderers of that graph. Authoring is collaborative; rendering is local.
184 modules in the registry today (audio + video). Catalog: /docs/modules. Right-click any module on the canvas to open its per-module docs page in a new tab.
Multi-user model
Authoring runs over a Yjs doc accessed through SyncedStore (the patch graph). A Hocuspocus server on Fly.io brokers updates between
rackspace participants and persists snapshots to Neon Postgres. Auth is Clerk; an HMAC-derived
invite code (/r/[id]?invite=...) lets anonymous users join without an account. Cap:
4 concurrent users per rackspace (1 owner + 3 others); the 5th visitor gets a /full page. Anonymous edits persist in the shared graph the same as any signed-in user's.
Domains
Two domains today, with the architecture from day 1 to drop in more:
- Audio. Web Audio + AudioWorklet. Most DSP is Faust 2 → WASM
(
packages/dsp/src/*.dsp). A handful of modules — LFO, Wavetable VCO, TIMELORDE, CHARLOTTE'S ECHOS, DX7 — ship as hand-writtenAudioWorkletProcessors in TS (packages/dsp/src/*.ts) when they need clock arithmetic, lookahead schedulers, or in-JS state. CV cables carry a bipolar −1..+1 signal where ±1 sweeps the target param edge-to-edge; per-portcvScalehints (linear/log/discrete/passthrough) drive the scaling. - Video. WebGL2 fragment shaders. Each video module ships its own GLSL +
a
VideoModuleDeffactory underpackages/web/src/lib/video/modules/. Cable types:image(still RGB),mono-video(1-channel animated),video(RGB animated),keys(1-channel still). Free upcasts handle the obvious widenings.
The graph is a Y.Doc with nodes, edges, and per-user layouts. A PatchEngine reconciler diffs the live graph against
per-domain rendering engines (AudioEngine, VideoEngine) and issues
add / remove / setParam calls. Audio-side cv cables can also terminate on a
video module's CV input — the cross-domain bridge reads the audio CV at frame rate and
pushes it into VideoEngine.setParam.
Persistence
Neon Postgres, one project, three branches (production / autotest / dev). The web tier on Cloudflare Workers can only reach Postgres through Neon's
HTTP neon template tag (pg sockets and the WebSocket Pool both fail in Workers — see deploy notes).
Hocuspocus on Fly runs Node and uses standard pg.Pool over TCP. Rackspace
metadata, owner, member list, and Yjs snapshots all live in Postgres. PICTUREBOX images and
DX7 user banks ride inside the Yjs snapshot — see rackspace persistence.
Deploy topology
Three tiers, fan-out from a single repo:
- prod —
patchtogether.live. Gated on apackage.json:.versionbump in a merge commit. - autotest —
autotest.patchtogether.live. Auto-deploys on every push tomain. Beta-gated (basic-authbeta:robotsonly). - dev —
dev.patchtogether.live. Same as autotest, separate Hocuspocus and Neon branch. Beta-gated (beta:2600hz).
Each tier maps to its own Cloudflare Pages project, Fly Hocuspocus app, and Neon Postgres branch. See deploy for the full topology.
What to read next
- Module catalog — every module, its I/O, params, source link.
- DOOM multiplayer — 4-player co-op: starting a game, joining, late-join, player colors, controls.
- Clip player + monome grid — an 8-instrument-lane, TIMELORDE-locked clip launcher you build, launch, scene, and edit from a monome grid 128 (WebSerial, no helper).
- Rackspace persistence — where patches + assets live, what Save/Load do, what auto-saves.
- Testing — unit / ART / E2E layers + port-surface consistency gates.
- Deploy + ops — the 3-tier flow, Workers↔Postgres caveats.