trevato/studio

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

A browser music studio: a step sequencer and piano roll driving hand-written WebAudio synths (subtractive, FM, a sampler from a recorded clip), per-track effects (delay, filter, reverb by convolution of a generated impulse), tempo and swing, export to WAV rendered offline, and projects saved per name. It must stay in time under load and be playable from a phone.

Running afc1c0e https://studio--forge-trevato-apps.open-platform.sh in production 21d 14h

README.md

studio

A music studio that runs in a browser tab. Hand-written WebAudio synths — a subtractive voice, an FM voice, and a sampler that plays a clip you record — under a step sequencer and a piano roll, with per-track effects, tempo and swing, export to a WAV rendered offline, and songs saved by name.

Nothing is downloaded to make a sound: the noise, the clap the sampler starts with, and the reverb's impulse response are all generated from a seeded random, in code you can read in src/dsp.ts.

PORT=8080 FORGE_APP_DATA=./data bun run src/server.ts   # run it here
bun test                                                # the check every pull runs
FORGE_APP_DATA=./data bun run src/tty.ts                # the terminal interface

What is real

  • The sound is real. Every voice is built from oscillators, gains,

biquads, a delay with feedback and a convolver, scheduled on the audio clock. There are no samples in this repository and no audio library.

  • The export is real. It renders the song through the same engine on an

OfflineAudioContext, faster than real time, and writes the WAV bytes by hand (src/wav.ts).

  • The sampler's clip is real when you record one: 2.5 seconds from the

microphone, downsampled to 22 050 Hz, stored as 16-bit PCM inside the song. Until you do, it plays a clap synthesised from noise.

  • Saving is real. Songs live in SQLite under FORGE_APP_DATA, one row per

name, and the page you get is server-rendered from the one you asked for.

  • Nothing is simulated. No mock data, no placeholder audio.

Playing it

Press Play (or space). Tap a cell in the grid to write a note and tap it again to remove it; drag to paint a run of steps, or, in the piano roll, to stretch the note you just wrote. The pads under the grid play the selected track live. Everything you change is saved under the song's name a moment later — the name field renames, Save is there when you want it now.

On a phone it is the same page: the panels stack, the grid scrolls, and the canvas takes pointer events so a finger draws notes. The transport schedules 150 ms ahead of the audio clock from a worker timer, so a busy main thread does not move the beat; if the page stalls long enough that a note would land late, it is dropped rather than crowded in, and the count appears next to the frame time in the header.

Shape

fileone concept
src/project.tswhat a song is, and the only validator for one
src/schedule.tsbeats: swing, loops, and the window the transport plays
src/dsp.tsseeded noise, the reverb impulse, the generated clap
src/wav.tsWAV bytes
src/clip.tsa recorded clip, small enough to store in a song
src/music.tsnotes and frequencies
src/db.tssongs in SQLite
src/bundle.tsthe client, built at boot and named by its hash
src/pages/studio.tsthe server-rendered page
src/server.tsroutes and the security floor
src/tty.tsthe same song, in a terminal
client/voices.tsthe three instruments
client/effects.tsone track's filter, delay and reverb
client/engine.tsthe mixer, live and offline
client/transport.tsplay, on the audio clock
client/grid.tsthe sequencer and piano roll canvas
client/render.tsexport

The browser and the server share src/: a song is parsed by the same parseProject in both places, so a knob cannot ask for something the store would refuse. There are no dependencies — nothing in package.json but the scripts.

The terminal

The exact command is in FORGE_APP_SSH when the app runs; the page prints it at the bottom, and says the terminal is off when that variable is unset.

It opens src/tty.ts on the same database: browse the songs, open one, write and erase notes on the grid, move the tempo, the swing and the bars, add and remove tracks, change an instrument's kind, rename, delete. Every edit is saved as it happens. It cannot play anything — the synths are WebAudio and live in the browser.

What the forge sets

PORT, FORGE_APP_DATA, FORGE_APP_ENV (production or preview), FORGE_APP_URL, FORGE_APP_COMMIT and FORGE_APP_SSH. forge.json says how the app runs, is tested, and is reached over ssh. Production and each preview keep their own songs, because they keep their own FORGE_APP_DATA.

↑↓ navigate ↵ open esc close

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs