No description
  • Python 51.1%
  • CSS 18.2%
  • HTML 14.4%
  • JavaScript 11.7%
  • Nix 4.6%
Find a file
Ellie 0ab61122b4 Add Stave Friend, an FSRS trainer for reading piano music
Notes, intervals, key signatures, and notes-in-a-key on a grand staff,
introduced landmark-first and explained relative to alto clef. Answers
come from MIDI, the on-screen piano, or letter keys; the page grades
and times them and suggests a rating. Built on Hanzi Friend's stack.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 23:44:43 -07:00
static Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
templates Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
tests Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
.envrc Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
.gitignore Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
app.py Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
auth.py Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
curriculum.py Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
db.py Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
exercises.py Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
flake.lock Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
flake.nix Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
music.py Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
pow.py Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
quiz.py Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
README.md Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
score.py Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
serve.py Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00
stats.py Add Stave Friend, an FSRS trainer for reading piano music 2026-10-04 23:44:43 -07:00

Stave Friend

A spaced repetition app for learning to read piano music, built on the same bones as Hanzi Friend (Flask, htmx, FSRS, SQLite). It's tuned for someone who already sight-reads alto clef and needs treble and bass.

What it teaches

Concepts are introduced in a fixed order (curriculum.py), one card each:

  • Notes on the grand staff, starting from landmarks (treble G, bass F, middle C, the Cs an octave out) and filling in by step, treble and bass alternating. Ledger lines and black keys come later.
  • Intervals (2nd to octave), read by shape: odd intervals stay line-to-line or space-to-space, even ones switch.
  • Key signatures, then notes in a key, where the signature, not an accidental, makes the note sharp or flat.

Every note intro and answer explains the spot in terms of alto clef (treble reads one letter down from alto, bass one letter up). When a wrong answer is the alto reading of that spot, the app says so.

The Drill page plays short melodies from notes you know. Drills aren't scheduled; they're for fluency once the notes are in.

Answering

  • Play the key on a MIDI keyboard (Web MIDI; Chromium-based browsers work out of the box, Firefox asks for a site permission).
  • Click the on-screen keyboard.
  • Type the letter, with Shift for the black key above. Typed answers check the letter, not the octave.

The page grades the answer and times it, then suggests an FSRS rating: wrong is Again, right letter in the wrong octave is Hard, and a right answer is Easy, Good, or Hard by speed (under 2.5s, under 6s, slower). Space accepts the suggestion; 1-4 picks a rating yourself. Interval and key questions answer by number key.

Configuration

Environment variables (a .env file works for development):

  • FLASK_SECRET -- session and proof-of-work signing key
  • INVITE_CODE -- enables registration; leave unset to keep it closed
  • POW_DIFFICULTY -- leading zero bits for the login proof-of-work (default 15)
  • STAVE_DATA_DIR -- where stave.db lives (default .)

Running

Development: nix develop, then python app.py (port 8090). Tests: pytest tests, or nix flake check.

Production: nix run, or on NixOS import nixosModules.default and set:

services.stave-friend = {
    enable = true;
    port = 8090;
    environmentFile = "/run/secrets/stave-friend.env";
};

The service listens on localhost; front it with your reverse proxy. Web MIDI needs a secure context, so serve it over HTTPS (localhost is fine for development).

Credits

Notation is drawn with Bravura (SIL Open Font License, see static/fonts/Bravura-OFL.txt).