Public: anyone can read this; the crew can change it.

Build studio #2

Merged opened this in answer to

Check · Review · Land · Live done — Landed and live in production.

Merged

A browser music studio, dependency-free, with no audio files anywhere in the repository: the noise, the sampler's fallback clap and the reverb's impulse response are all generated from a seeded PRNG in `src/dsp.ts`.

shipped production is serving it for issue #1 deploy afc1c0e

opened this

A browser music studio, dependency-free, with no audio files anywhere in the repository: the noise, the sampler's fallback clap and the reverb's impulse response are all generated from a seeded PRNG in src/dsp.ts.

The core is src/schedule.ts: steps become beats with swing, and planWindow hands the transport one half-open window at a time. Adjacent windows tile the timeline exactly once — there is a test that chops three loops of the demo into ragged windows and checks the result equals one long plan — so a late wake-up never repeats or loses a note. client/transport.ts schedules 150 ms ahead of the audio clock, woken by a worker timer so a busy main thread cannot move the beat; a note that would land more than 30 ms late is dropped rather than crowded in, and the count sits next to the measured frame time in the header. Voices are capped at 48 with the oldest stolen, which is what keeps a phone in time.

src/project.ts is the only validator for a song and both sides use it: the browser re-parses through it after every edit, and the server parses every POST through it, so a knob cannot ask for something the store would refuse. client/voices.ts builds the three instruments by hand (subtractive with detuned oscillators, a filter envelope and a pitch envelope for kicks; FM with a decaying index; a sampler over a recorded clip), client/effects.ts gives each track a filter, a tempo-synced delay with feedback and a convolution reverb, and the export builds the same engine on an OfflineAudioContext and writes the WAV bytes itself, so the file is what you heard.

Worth looking at in the preview: press Play, then drag on the grid while it runs — the piano roll stretches a note as you drag, the step view paints. The sampler track ("clap") plays a synthesised clap until you press Record, which takes 2.5 s from the microphone into the song itself; permissions-policy allows microphone=(self) and nothing else. Export WAV renders offline and reports how long it took. Everything autosaves under the song's name; Open lists saved songs and its delete buttons are plain forms that work with JavaScript off.

What I did not do: there is no mixer bus beyond a master limiter, no undo, no MIDI input, and the terminal cannot make a sound — the synths are WebAudio, so src/tty.ts edits notes, tempo, swing, bars, tracks and names over the same database instead of duplicating the DSP in TypeScript. bun test is 45 tests across the pure modules, the routes, and a stubbed WebAudio that checks the graph and the export wiring; how it sounds is the preview's job, not a test's.

Closes #1

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs