Plain-text music notation
Plainsong is notation you type in any editor, diff in git, and compile to MIDI. This page is the compiler — it runs in your browser, offline, with nothing installed and nothing sent anywhere. Everything below is live: edit it, drag it, break it.
Type notation, hear it play. It recompiles as you type, and each token lights up while the note it wrote is sounding.
You never write durations in Plainsong. You write how many things happen in a bar, and the bar shares itself out between them. Drag the slider and watch the same bar hold a different number of events — the bar never changes length.
The idea the whole system rests on. A score says “beat one” without saying where that happens. Plainsong reads a written beat as the moment sound should reach a listener, then solves backwards for when each player must physically move. Drag the players. Drag the ear. Watch what changes.
Drag the ear onto the organ. The spread stops being zero — because the organ now reaches you before the timpani does, and the score was never compensated for your chair.
Everyone plays exactly on the written beat, and the sound smears by the width of the stage. That is what a score without a stage actually asks for.
Every player hears a different performance, and each is right about what they hear. The eyes carry the intention; the ears only carry history.
Rows of different kinds sound together. Two rows of the same kind run one after the other. Toggle the second melody row between the two readings.
This page is one file with no dependencies, no build step and no network calls.
Save this HTML anywhere and open it. It works from file:// — offline,
on a plane, on a machine with nothing installed.
python3 -m http.server 8000
Any static host works: GitHub Pages, Netlify, a Raspberry Pi on your desk. There is no backend to run.
pip install plainsong plainsong serve
Adds the stage solver, soundfont rendering, 3,800 chord charts, an agent, and an MCP server so several agents can write one score at once.
The browser engine is checked against the reference compiler by running both over the
same notation and comparing the pitch, start and length of every note — triplets, 3/4,
6/8, chord qualities, stacks, sustains, rests and repeated rows
(tools/demo_differential.py). That check exists because it found a real
one: . and - were read here as rests rather than as holds, so
every sustained note was cut to a single subdivision while the note counts
stayed right. Where this engine is smaller, it is smaller on purpose — it does not solve
stages, only draws them.
Oscillators with an envelope. The installed version renders through a soundfont, which is the difference between a sketch and something you would play to somebody.
Checking for a built-in model…