A small web app that turns a choir score into practice material: it reads the notes off the sheet music, splits the voices onto their own staves, puts the words under them and publishes the score to the stemmanauhat site, which draws it and plays it back part by part. It runs on your own machine and works from a phone as well as a desktop.
You bring the sheet music as a PDF (or a score file you already have). The app walks one song at a time through a fixed set of stages, remembers where each song is, and keeps the printed page on screen next to whatever you are doing.
Each song moves left to right along the stage list. You can go back to an earlier stage at any time; redoing one clears only what depended on it.
- Start — name the song and give it a PDF, a score file, or both. A PDF on its own starts at Scan; a score file skips scanning and starts at Clean. Choose whether it is a men's, women's or mixed choir, which decides how the parts are named.
- Scan — read the notes off the PDF. Mark where each printed system (one line of music across all the staves) sits on the page — Find systems proposes them and you drag the edges to fix them — then press Scan the score. Each system is read separately by homr, an optical music reader. A bar it was unsure of is marked for the Fix stage, and a system that came out wrong can be read again on its own.
- Clean — split staves that carry two voices into one staff per voice and name the parts (S1, A1, T1, B1, …). When the staves change parts from one system to the next, you fill in a small grid saying who sings on which staff. A part split in two is named with a b: S1 and S1b, S2 and S2b, A1b, T1b. Never S2-2 or S1-1/S1-2: the site knows S1b as the second half of S1, so a singer whose part is S1b hears S1 in songs that have no S1b, and a part left out of a system takes its base part's notes there.
- Fix — a health check lists bars that look damaged: a voice that does not
fill its bar, an extra voice, a bar longer than its time signature. Fix them in
MuseScore — on this machine, or download the score, fix it anywhere and upload
it back. Recorded fixes (for example a slur the scan missed) are kept in
fixes.jsonand applied again every time the song is cleaned. - Lyrics — either paste lyrics from your own AI chat (copy the prompt, give it the PDF, paste the answer back; no API key needed) or type them one printed system at a time, each staff of the page with its parts' boxes under it, verse by verse. The app shows where the words and the notes do not match up.
- Review — one place that says whether the score is ready: health, lyrics, whether MuseScore 3 will open the file. Approve it to move on. That last check needs MuseScore 3: with MuseScore 4 it says "not checked", and a bar MuseScore would call corrupted is not found or reset for you.
- Publish — send the score to the stemmanauhat site for one choir. Set the opening tempo (for a score that prints none), the bars where the tempo changes, and the composer, lyricist and arranger the site shows, then press Publish. The site gets the score, its MIDI and a timing file; publishing again replaces the song there. Part names follow the same rule as Clean: S2b, not S2-2.
The right-hand viewer switches between the original PDF, single printed systems, the scanned score and the cleaned score (with and without lyrics), and can split into two panes to compare any two of them.
Below tablet width the stage panel and the score are shown one at a time, with a bar at the bottom to switch between them. The stage list slides in from the ☰ in the header, and the score zooms with a pinch or its − / + / Fit buttons. The app can be installed to the home screen (see docs/PWA.md).
./song.py # starts the server and opens the browser
./song.py --no-browser # just the server
./song.py --port 8123 # prefer a port (the next free one is used if it is taken)To set up your own copy, run scripts/setup.sh and then ./song.py --check;
SETUP.md walks through it — Python, MuseScore 3 (or 4), poppler, the
optional homr install, and publishing to your own copy of the
stemmanauhat site. song.py uses the
project's virtualenv (.venv/bin/python song.py if .venv is not active).
Each song is a folder under songs/ with its state in songs/<song>/.song.json.
songs/ is not part of this repository, since the songs are usually sheet music
under copyright; keep it in a private repository of your own.
- CHANGELOG.md — what changed, and when.
- TOOLS.md — the command-line tools the app is built on
(
clean_score.py,lyric_txt.py) and the MuseScore 3 plugins inplugins/. - DESIGN.md — why the app is shaped the way it is.
- SETUP.md — setting up a new machine.
- CLAUDE.md — detailed notes for whoever works on the code.



