songcode

From prompt to pattern

What happens between the moment you press generate and the moment you hear a kick drum.

under the hood5 min read

A songcode track goes through three machines: ours, a model provider’s, and yours. Only yours makes sound.

1. Your prompt meets a written doctrine

When you press generate, your browser sends the prompt, the genre, the length and any advanced settings to a small server program (a Cloudflare Worker). The Worker assembles what the model will read.

Most of that is a document we call the doctrine. With its list of sounds it runs to about 4,000 words, and it has four parts.

An output contract and a song format. Line one is a title. Everything after it is runnable code, no prose. A song is a tempo, a form, shared material and stems, and each instrument is one named stem: kick:, bass:, stab:.

A reference. Every function, drum machine and sound the model may use. This part matters more than it looks, and the next section explains why.

Mixing and groove rules. The things a producer learns in the first few years, written as instructions. A few, verbatim:

One sub at a time. Only the kick and the bass live below 120 Hz.

Never let every stem be busy at once. Density is a dial you move across the song.

Silence is an instrument. A beat of nothing before a drop does more than any riser.

A mode. Loops and full tracks get different instructions. A loop has to sound finished in bar one and survive ten minutes of repetition. A full track needs an intro a DJ can mix, a breakdown where the kick leaves, and one element held back until the peak.

On top of the doctrine sits a brief for your genre: a tempo range, a drum kit, a palette of sounds, the move that defines the style. Then your settings. Then your prompt.

2. Why the reference is checked by hand

Language models invent function names. In most programming that is a loud failure: the code throws an error and you fix it.

Music code fails quietly. Ask Strudel to arpeggiate with .arp("up") and nothing complains. Nothing plays either. A drum machine name that is one character off gives you a bar of silence where the snare should be.

So the model is told to use only what the reference names, and the reference lists the traps by name. Its line on arpeggios ends: “A mode name like .arp("up") is silent.”

3. A language model writes the track

The Worker sends the assembled prompt to Anthropic or OpenAI, using the API key you supplied, and streams the reply back as it is written. You watch the code arrive line by line.

The models are general-purpose language models. songcode offers three tiers per provider: fast, balanced and best. A bigger model takes longer and tends to write better arrangements.

The key is yours. It lives in your browser’s storage, rides along as a header on each request, and is forwarded to the provider. The Worker does not store it or log it.

4. Your browser tests it before you hear it

A written rule is a request. The test is what holds. When the last line arrives, the engine in your browser compiles the track and walks every stem through the whole song without making a sound. It is looking for three things: a method that does not exist, a sound that does not exist, and a stem that never plays a note.

A clean track goes straight to play, and the status line reports how many stems were checked. If the test finds errors, the track goes back to the model once with the list of problems, and the model fixes its own work. That is a second model call on your key, and it happens only when needed. songcode keeps whichever version has fewer problems.

The test catches broken. It does not catch boring. A track can pass every check and still be dull, and that part is on you and the editor.

5. Your browser plays it

The code is written in Strudel’s pattern language. Each stem compiles to a pattern, and a pattern is a function: give it a span of time, get back the events that happen in it. songcode’s transport asks every stem what happens in the next fraction of a second and schedules the answers on the Web Audio engine built into your browser.

Every stem gets its own channel in a mixer, with its own level, reverb and delay. The channels meet at a master limiter set just under full scale. That is the whole signal chain.

Sound comes from two places:

  • Synthesis. Oscillators, FM, filters with envelopes, a plucked-string model, reverb, delay, distortion. songcode adds its own patches on top of Strudel’s: four drum kits and 53 instruments, from an acid bass to a string machine. All of it is computed on your machine as the track plays.
  • Samples of single sounds. A TR-909 kick, a clave, a piano note, a marimba bar. These are short recordings of instruments. Most ship with Strudel’s sample library. An acoustic drum kit, a kalimba and a marimba come from VCSL, a CC0 sample library.

No recording of the finished track exists until you export one. The export is rendered in your browser too, faster than real time.

What the server never sees

The Worker handles text: your prompt on the way in, code on the way out. It keeps a library of saved tracks, which are also text, and it counts requests per IP address so nobody can use it as an open relay. Audio never touches it, because there is no audio until your browser makes some.

What this design buys

A track is a text request, so it is small and quick to generate. The result is a kilobyte or two. Editing costs nothing: changing a filter cutoff is a keystroke, with no second trip to a model.

It also sets the limits. The engine plays synths and samples, so that is what songcode sounds like. The limits get their own post.

Describe a song.
Get it in code.

open songcode