bd sn hh sn| Syntax | Meaning | Example |
|---|---|---|
| a b c | Sequence — items share the cycle equally | bd sn hh sn |
| [ a b ] | Group — subdivides one time slot | [bd bd] sn |
| a*n | Repeat — fires n times within its slot | hh*8 |
| a/n | Slow — plays only every n cycles | cy/4 |
| a(k,n) | Euclidean — k hits spread across n steps | bd(3,8) |
| ~ | Rest — silence for that time slot | bd ~ sn ~ |
Syntax can be combined freely. Nesting is supported:
bd(3,8) spreads 3 hits as evenly
as possible across 8 steps — the same algorithm that generates the clave, tresillo,
and other rhythms found in world music.
All sounds are synthesised from scratch using FM (frequency modulation) synthesis — no samples.
| Name(s) | Sound | FM Character |
|---|---|---|
| bd kick | Kick drum | Pitch envelope 160→42 Hz, high mod index |
| sn snare | Snare drum | FM body + noise burst |
| hh hat | Closed hihat | √2 ratio (inharmonic) → metallic |
| oh | Open hihat | Same as hh, longer release |
| cp clap | Clap | 3 staggered FM bursts + noise |
| cy ride | Cymbal / ride | √2 ratio, long FM index decay |
| tom | Tom | Pitch envelope 140→80 Hz |
| bell | FM bell | 3.5:1 ratio — the DX7 algorithm |
| bass | FM bass | 0.5:1 ratio, sub-heavy |
| lead | FM lead | 2:1 ratio, bright attack |
| pad | FM pad | Dual detuned operators, slow attack |
| c d e f g a b | Note names | FM tone at pitch, octave optional |
c, e4, g#3, bb5 etc.
Default octave is 4. Sharps = #, flats = b.
Basic drum machine:
Polyrhythmic:
Melodic:
Dense / layered:
The settings bar below the header controls global parameters for all patterns. Changes are staged (held in reserve) until you press [ SHIP ].
| Control | Description |
|---|---|
| KEY | Root note for scale quantization. Click to cycle through all 12 chromatic roots (C → C# → D … → B). |
| SCALE | Scale mode. Click to cycle: Major, Minor, Dorian, Phrygian, Lydian, Mixolydian, Locrian, Pentatonic, Diminished, Whole Tone, and more. Note names are snapped to the nearest in-scale degree. |
| BEATS | Beats per cycle — 2, 4, 8, or 16. Controls how many steps a cycle is divided into. 4 = common time, 8 = double-time feel. |
| VOL | Master volume (0–100%). Staged — takes effect on SHIP. |
| SWING | Swing percentage (0–50%). Delays every off-beat 8th note. 0% = straight, 33% = triplet feel, 50% = maximum shuffle. Also adjustable by dragging the canvas centre zone. |
| REVERB | Synthetic reverb mix (0–100%). Uses an exponentially-decaying noise impulse response — no external files. |
| SWAP | Quantisation boundary for thread swaps (1, 2, 4, 8, or 16 cycles). When you evaluate a new pattern while playing, the change queues until the next multiple of this value. See Staged Parameters below. |
Tidal Waves separates editing from applying. This lets you dial in a complete new state — new key, new scale, adjusted volumes — and then cut to it at exactly the right moment.
[ SHIP 3 ].▶4 counting down.
When the swap fires, the active indicator flashes.
Each pattern row has an expandable control panel. Click [ CTRL ] on any row to open it.
| Control | Description |
|---|---|
| GAIN | Per-pattern volume (0–100%). Independent of the master VOL. Staged — requires SHIP to apply. The readout shows the live value; if pending, it shows ← 75% (the current live value) beside the staged value. |
| CHANCE | Probability gate (0–100%). At 80%, each event has a 20% chance of being silenced. At 100%, every event fires. Creates live rhythmic variation without editing the pattern. |
# gain 0.5 param applies per-event and takes effect
immediately on the next evaluate.
Press [ PRE ] to open the substrate selector — 12 pre-composed algorithmic patterns ready to play. Each substrate loads a full configuration: BPM, key, scale, swing, reverb, and up to 5 pattern rows. After loading, all patterns are fully editable.
| Substrate | Character |
|---|---|
| NEURAL DRIFT | 76 BPM · A dorian · Heavy reverb · Evolving bell lines |
| GRID RUNNER | 134 BPM · C minor · Straight · Fast industrial grid |
| GHOST PROTOCOL | 112 BPM · D phrygian · Swing 22% · 8 beats · Polyrhythmic |
| NEON RAIN | 100 BPM · D minor · Euclidean kick/snare · Melodic pad |
| FLATLINE | 95 BPM · G pentatonic · Swing 38% · Sparse groove |
| DATA CASCADE | 122 BPM · F mixolydian · Heavy Euclidean polyrhythm |
| SECTOR ZERO | 70 BPM · B locrian · Reverb 58% · Dark ambient pulse |
| PHOSPHOR BLOOM | 88 BPM · E lydian · 8 beats · Reverb 80% · Ethereal bell |
| CHROME RITUAL | 84 BPM · C whole-tone · Reverb 72% · Ritualistic layers |
| SUBTERRANEAN | 92 BPM · A diminished · Swing 16% · Deep sub textures |
| UPLINK | 128 BPM · G dorian · Hard techno grid · No reverb |
| DEAD CHANNEL | 60 BPM · E phrygian · Reverb 90% · Sparse decay |
Append # key val to any pattern to control per-event synthesis parameters.
Multiple values cycle across events. Multiple params chain with additional # separators.
| Parameter | Range | Effect |
|---|---|---|
| # gain 0.5 | 0 – 2 | Amplitude multiplier for this pattern |
| # pitch 1.5 | 0.1 – 4 | Frequency multiplier (1 = normal, 2 = octave up) |
| # pan -0.5 | -1 – 1 | Stereo position (-1 = left, 0 = centre, 1 = right) |
| # decay 0.5 | 0.01 – 4 | Duration multiplier (shorter/longer notes) |
| # index 2 | 0 – 8 | FM modulation index multiplier (brightness/timbre) |
# pitch 1 1.5 2 — event 1 gets 1, event 2 gets 1.5,
event 3 gets 2, event 4 wraps back to 1. Works with any number of values.
Append ~op (or ~op value) after the pattern to make it evolve automatically over cycles.
The pattern lives — it changes itself while you watch.
| Operator | Effect |
|---|---|
| ~drift | Rotates the pattern by 1 step each cycle (events shift position over time) |
| ~mut 0.1 | Each event has N% chance of being silenced or swapped each cycle. Default 10%. |
| ~rev | Reverses the pattern direction every 4 cycles (or every N cycles if specified) |
| ~alt | Alternates between forward and reversed on odd/even cycles |
| ~mk | Markov chain: rewrites the sequence each cycle using token bigram probabilities. Optional arg (0–1) controls randomness. |
# params.
Put ~ops before the # section, or after the pattern before the #.
Three global effects are available in the settings bar, all built using Web Audio API — no plugins, no libraries.
| Effect | Control | Description |
|---|---|---|
| DLY | Toggle + wet % | Stereo delay with feedback. Slider controls wet mix (how loud the echo is). |
| FLT | Toggle + cutoff Hz | Resonant lowpass filter. Slider sweeps cutoff from 200 Hz to 18 kHz. |
| DST | Toggle + drive % | Waveshaper distortion. Slider controls both drive amount and wet level. |
Press S (or [ STG ]) to switch to full-screen performance view. The code editor disappears. The cycle wheel fills the screen.
Press [ DRAW ] to enter Draw mode — the cycle wheel becomes an instrument you can edit directly.
| Ctrl + Enter | Evaluate the focused pattern row |
| S | Toggle Stage mode (full-screen visualizer) |
| Escape | Exit Stage mode |
| Scroll wheel on canvas | Adjust BPM ±1 |
| Drag canvas centre | Adjust swing (0–50%) |
| Right-click canvas | SHIP pending changes |
| Click ring on canvas | Select that pattern row |
# params are resolved per-event using cyclic indexing — no changes to the parser or scheduler
Additional # parameters added to the synthesis engine:
| Parameter | Range | Effect |
|---|---|---|
| # index 2 | 0 – 8 | FM modulation index multiplier (1 = default, 2 = double brightness, 0 = pure tone) |
The ~mk operator uses a Markov chain to rewrite the pattern each cycle.
Transition probabilities are built from the token bigrams of the original pattern —
so the machine follows the pattern's own grammar.
| Syntax | Effect |
|---|---|
| ~mk | Markov rewrite with strength 0.7 (default) |
| ~mk 0.3 | Lower strength = more random (0 = fully random, 1 = fully Markov) |
| ~mk 0.95 | High strength = closely follows original transitions |
Every session is automatically encoded in the URL hash after you evaluate or ship a pattern. Copy the URL from the address bar and share it — anyone who opens it hears exactly what you built.
Save named snapshots of your session to browser storage. Recall them from the preset overlay.
[ SAVE ] in the header opens a name input — type a name and press Enter[ PRE ] overlay, click the SAVED tab to see your sessions
Save individual patterns with names and reference them using @name syntax.
| Action | How |
|---|---|
| Save a pattern | Click [ LIB ] on the row, enter a name when prompted |
| Load a pattern | Type @name anywhere in a row's pattern field |
| Combine with params | @kick-groove # gain 0.8 |
Render N cycles to a WAV file using OfflineAudioContext — no recording, no screen capture.
[ WAV ] in the headertidal-waves-{timestamp}.wav| Feature | How |
|---|---|
| Instrument label | Hover over any dot on the cycle wheel to see the instrument name |
| Ring radius = gain | Drag a ring inward to lower its volume, outward to raise it. Change is immediate (no SHIP needed). |
| Density heatmap | In Stage Mode, rings show a white glow that brightens in regions where events are clustered |
The fm instrument uses a 4-operator FM engine with selectable routing algorithms.
Use the [ ALG ] button in a row's [ CTRL ] panel to cycle between algorithms.
| Algorithm | Character |
|---|---|
| PARALLEL | All three modulators hit the carrier simultaneously — complex, dense timbres |
| SERIAL | Modulators chain in series — deep, evolving harmonic stacks |
| FEEDBACK | Op3 feeds back into itself before modulating the carrier — metallic, screaming |
The filter LFO rate can follow a rhythm pattern. In the FLT section of the global bar, the text input next to the cutoff slider accepts a mini-notation pattern string.
FLT:ON) for the LFO to be audibleLive-code together in real time. One person hosts a session; others join and hear every pattern change as it's shipped. Uses a Cloudflare Worker for server-side clock sync and shared state — no WebSockets, no WebRTC.
| Action | How |
|---|---|
| Host a session | Click [ NET ] → [ HOST ]. The current session state uploads and a join URL appears. |
| Share the link | Click [ COPY ] next to the join URL. Anyone who opens it auto-joins the session. |
| Join a session | Paste a session ID or full join URL into the join field and press [ JOIN ]. |
| Claim a row | Non-owners: click any unclaimed pattern row to take ownership of it. Claimed rows lock out other clients. |
| Spectator mode | Open a join URL with ?spectate=1 appended. All controls are hidden — pure audience view. |
| Leave a session | Click [ LEAVE ] in the NET overlay. Your rows become unclaimed. |
| End a session | Host only: [ DELETE SESSION ]. All connected clients are disconnected. |
[MINE] for yours, a short client ID for someone else's. Remote-owned rows are dimmed
and read-only until you claim them or the owner leaves.
URL parameters:
| Parameter | Effect |
|---|---|
?session=ID | Auto-joins the given session on page load |
?spectate=1 | Opens in spectator mode — controls hidden, canvas full-screen |
Multi-user sessions are implemented but not yet enabled on this deployment. The infrastructure uses a Cloudflare Worker for shared state and server-side clock sync.
node scripts/multiuser.js enable and redeploy.
See DEPLOY.md for full instructions.