Live-codeable synths and mini-notation patterns, in the browser. You write two kinds of code. Synths are functions that wire oscillators, filters and envelopes into a sound. Patterns are mini-notation sequences that trigger those synths in time. A custom AudioWorklet DSP engine runs it all; nothing is sampled unless you load a sample.
This repository is a fork of vijaypemmaraju/rondocode, created and maintained by Vijay Pemmaraju.
The pattern engine, DSP, editor, mini-notation, MIDI importer, agent bridge, and the design of the system are Vijay’s work. This fork builds on that foundation — thank you, Vijay.
Upstream: github.com/vijaypemmaraju/rondocode
This fork: github.com/luke-hurd/rondocode
Everything below was added after forking. Upstream already shipped the core live-coding system (synth DSL, mini-notation, AudioWorklet engine, editor, examples, MCP bridge, offline render tools, etc.). For open ideas and status, see ENHANCEMENTS.md.
| Feature | What it does |
|---|---|
| In-app MIDI import | Project library → midi — pick a .mid, get an editable project (synths, patterns, setCps). Same converter as the CLI (midiToRondocode). |
| Offline WAV export | Header export → bounce the current program offline to a 16-bit WAV. |
| Live master recording | Header export → rec / stop — capture the master bus to a downloadable WAV. |
| Web MIDI in | Header MIDI control + connected controllers trigger noteOn/noteOff on a chosen live synth (fail-open if unsupported). |
| Web MIDI out | Pattern-scheduled notes are forwarded to MIDI outputs (not MIDI-in echoes). Optional MIDI clock API on the out handle. |
| Sample packs | Built-in procedural packs in the samples popover: core (vox, pad, riser) and kit (kick, snare, hat, clap), with preview + insert. |
| Mixer bus faders | Shared bus() send amounts + bus gain appear in the mixer and edit the code (same rewrite path as inline widgets). |
| Mobile header overflow | Narrow viewports collapse secondary header actions into an overflow menu. |
| Agent → editor sync | Successful MCP eval_code rewrites the CodeMirror buffer to match what’s playing; get_code also returns editorDoc. |
| Jam rooms | ?room= live collab via PartyKit + Yjs + y-codemirror.next — shared buffer, presence, driver-mode Run, local audio per peer. See docs/jam.md. |
| Shared transport | Driver play/stop + cps with best-effort cycle-aligned start across peers. |
| Spectator view | ?spectate=1 — read-only projection of a jam room. |
| Stem mute masks | Per-synth mute shared in the room control plane (local gain). |
| Sample CDN hooks | Room can publish {name,url} packs; peers fetch into the Worklet. |
| GitHub auth + cloud library | Optional Supabase Auth (GitHub OAuth) + cloud projects / room ownership (env-gated). |
| Feature | What it does |
|---|---|
squeezeBind |
For each event, play an inner pattern time-stretched into that event’s slot. |
chop(n) |
Subdivide each event into n equal pieces (rhythmic chop). |
.striate(n) |
Chop into n slices and set begin/end (0..1) so sample() plays successive buffer regions (Tidal/Strudel-style). |
Mini a*[2 3] / a/[2 1] |
Pattern-valued speed-up / slow-down factors via squeeze-bind. |
Mini 0 .. 7 |
Inclusive integer ranges (also 0..7); max 128 steps. |
| Richer scales | Extra tables for .scale(...): blues, majorBlues, harmonicMinor, melodicMinor, hungarianMinor, wholeTone, diminished, augmented, enigmatic, … |
density / hurry |
Aliases of fast (Tidal/Strudel naming). |
mask / inside / outside / zoom |
Bool gating, apply-in-sped-up-time, apply-in-slowed-time, cycle-window magnify. |
binary(n) |
Boolean rhythm from binary digits (handy with struct / mask). |
.echo / .ping |
Tempo-synced musical delay (layered copies); ping-pong stereo taps. |
| Feature | What it does |
|---|---|
Sample-accurate setParam |
Protocol + realtime queue support atFrame so patterned params land with the note, not ~one lookahead early. |
note.midi |
Discrete MIDI note signal alongside note.freq (handy for wavetable position / selects). |
feedback(fn, time, opts?) |
Delayed feedback loop combinator (Karplus-style / external echoes). |
Sample begin / end |
sample() auto-wires begin/end params (0..1) for region play; override with opts. Powers .striate(). |
| Stereo samples | Loaded stereo files keep L/R (no host downmix); imaging preserved through gain-only paths (mul/adsr). |
| Shared send buses | bus('space', fx, { pad: 0.4, pluck: 0.25 }, { gain: 0.8 }) — one FX chain, many synths feed; mixer faders rewrite the send literals. |
truePeak |
Inter-sample peak in offline analysis. |
melBands + melDistance |
32-band log-mel mean spectrum; cosine distance in compare_renders. |
| Integrated LUFS | BS.1770-style K-weight + dual gating on analyze(); lufs in render-tool readings and compare deltas. |
| Feature | What it does |
|---|---|
CI vite build |
GitHub Actions builds the app in addition to typecheck + tests. |
| Biome lint | pnpm lint + CI lint step (starter rules; intentionally permissive). |
/llms.txt |
Docs guide + reference as Markdown for LLMs (also a copy button in the docs UI). |
scripts/render-code.ts |
Headlessly bounce curated demo programs to WAV. |
| ENHANCEMENTS.md | Fork backlog / what’s done vs still open. |
| Docs / agent-guide | DSL reference + MCP agent guide updated for editor sync, new combinators, and LUFS/mel analysis. |
Flok / Estuary-style multiplayer: share a URL, edit one buffer together, each browser renders audio locally. Sync the code and control plane, not PCM.
pnpm jam # PartyKit on :1999
pnpm dev # app on :6060
# open http://localhost:6060/?room=demo in two browsers| Piece | Behavior |
|---|---|
| Shared buffer | Yjs CRDT over PartyKit (y-codemirror.next) — everyone types in the same program. |
| Driver | One peer owns Run / play-stop; others edit. Claim drive in the jam bar to hand off. |
| Presence | Nickname + color; see who’s in and who drives. |
| Transport | Shared cps + play/stop with a short aligned start across peers. |
| Spectator | ?spectate=1 — read-only projection view. |
| Stems / samples | Optional per-synth mute masks; room can publish {name,url} sample packs. |
| Saving | You’re co-writing a shared score. Header export bounces that program on your machine (same as solo). Local IndexedDB copies and optional Supabase cloud projects are per-user. |
| Accounts | Optional GitHub OAuth via Supabase (env-gated). Anonymous rooms work with no sign-in. |
Solo / offline still works with zero network — rooms are opt-in. The MCP bridge supersede path is unchanged (agents attach to the driver’s tab, not PartyKit).
Full notes: docs/jam.md.
pnpm workspace, TypeScript throughout. Packages import each other by name
(@rondocode/pattern, resolved to src/ via workspace symlinks).
| Package | What it is |
|---|---|
@rondocode/pattern |
Pure pattern engine: Pattern/Hap/TimeSpan/Fraction, mini-notation parser, combinators, scales, chords, the scheduler, and the MIDI importer (src/midi.ts). No audio, no DOM. |
@rondocode/engine |
The DSP: oscillators, filters, envelopes, effects, the synth() builder, offline render, WAV encode. |
@rondocode/app |
The browser app: CodeMirror editor, the live audio session, the docs panel, the built-in examples (src/examples/index.ts). |
@rondocode/server |
Headless/bridge tooling and dev scripts. |
pnpm install
pnpm dev # vite dev server on http://localhost:6060
pnpm jam # PartyKit jam rooms on :1999 (optional)
pnpm test # the whole vitest suite
pnpm test:watch # watch mode
pnpm lint # Biome (this fork)Optional env vars: see .env.example (VITE_PARTYKIT_HOST,
Supabase keys for cloud auth).
Type-check with pnpm --filter @rondocode/app exec tsc --noEmit (or per package).
Do not run tsc -b in this repo: it emits .js into src/ and vite then
loads the stale .js over the .ts. Always use tsc --noEmit.
Everything you can write in an example is documented in-app (the docs panel) and
in packages/app/src/docs/:
dsl-docs.ts, the reference: every scope global,Patternmethod, synth-ctx member,Sigmethod and mini-notation operator. It is coverage-pinned:test/docs.test.tschecks it bidirectionally against the live objects.content.ts, the hand-written guide: short sections that each end in a complete, playable program.
pnpm tsx packages/server/scripts/render-example.ts "veldt (full)" 52 out.wavAgents can also use MCP render_code / render_synth / compare_renders
(analysis includes lufs, truePeak, melBands, etc.).
In the app: project library → midi. From the CLI:
pnpm tsx packages/server/scripts/midi-to-rondocode.ts song.mid "my song" out.txtmidiToRondocode in packages/app/src/midi/import.ts is the in-app converter.
The low-level importer lives in packages/pattern/src/midi.ts.
rondocode’s pattern model follows in the lineage of
TidalCycles and Strudel. The
pattern engine, DSP, and editor are written from scratch (Vijay’s upstream),
with no Tidal or @strudel/* dependency.
Contributions are welcome. See CONTRIBUTING.md and CODE_OF_CONDUCT.md. Example tunes must be original compositions (no transcriptions of copyrighted songs).
For fork-specific ideas and status, see ENHANCEMENTS.md.
MIT © Vijay Pemmaraju.
This fork remains under the same MIT license; copyright for the original work belongs to Vijay Pemmaraju.