▶ Live site: https://brona90.github.io/twelve-keys/
A growing single-file-per-exercise practice library for jazz piano:
shell and rootless voicings, tritone subs, turnarounds, diatonic 7ths,
two-handed comping, walking bass, drop-2, minor ii–V–i, stride, quartal
(McCoy), the blues in a dozen treatments, upper-structure triads,
Coltrane changes, the modes and melodic/harmonic-minor scales, bebop
lines, modal vamps, and interactive chord-scale tools — all rendered
with notation, playback, and guitar comping diagrams, in all twelve
keys, offline. The site is built and deployed to GitHub Pages
automatically on every push to main.
twelve-keys/
├── build.sh # builds index.html + per-exercise HTML
├── fetch-fonts.sh # one-time: encodes RealBook, fetches Special Elite
├── src/
│ ├── render.py # shared template, playback, fullscreen, metronome
│ ├── build_index.py # landing page
│ ├── build_scales.py # scales.html reference tool
│ ├── build_chord_scales.py # chord_scales.html reference tool
│ ├── rhythm.ly # shared LilyPond example
│ ├── style_*.css # exercise / index / scales / chord-scales stylesheets
│ ├── lilyjazz/ # vendored LilyJAZZ music font (otf + svg)
│ ├── realbook-webfont.* # RealBook chord/text font (ttf + woff)
│ ├── font_*.b64 # gitignored (regenerated by fetch-fonts.sh)
│ └── exercises/<slug>/exercise.py
└── build/ # intermediate artifacts (gitignored)
Built outputs (all gitignored): index.html, <slug>.html, scales.html,
chord_scales.html, sw.js, manifest.webmanifest, icon files.
./fetch-fonts.sh # once: base64-encodes the local RealBook woff, curls Special Elite
./build.sh # produces ./index.html and ./<slug>.html per exercise
./build.sh -j 4 # limit parallelism; --html-only skips LilyPond
xdg-open index.htmlRequires lilypond (>= 2.26, for the property-defaults.fonts syntax the
engraving uses), python3, base64, and curl. Tested on Debian WSL;
CI pins LilyPond 2.26.0.
- Landing page lists every exercise, grouped by section, with search and category filters; plus two reference tools (scale library and an interactive chord→scale map).
- Each exercise page is a 12-key grid with notation and guitar fret diagrams above each chord. Click any card to view it fullscreen. ← / → step through keys; ESC closes.
- Playback on every card and in the fullscreen modal — a WebAudio synth follows the metronome tempo, with a moving playhead over the notation and a keyboard widget that lights the sounding keys.
- Floating metronome in the bottom-right corner of every exercise page. Tap-tempo, subdivision toggle, persists across pages and reloads. Doesn't interfere with scrolling or printing.
- Topbar switcher to jump between exercises; "← All exercises" returns to the landing page.
- Offline / PWA: a service worker precaches the whole site, so it works offline and as an iOS home-screen app.
- Print stylesheet preserves the 3-column grid for the music stand.
mkdir src/exercises/<slug>- Create
src/exercises/<slug>/exercise.pywith anEXERCISEdict (seesrc/exercises/shells/exercise.pyas the canonical example). ./build.sh— the new exercise is picked up automatically and appears on the landing page.
Open src/exercises/<slug>/exercise.py. Each row of the keys table is:
(n, name, quality, chord_mode_str, melody_str, label)chord_mode_str uses LilyPond chord-mode syntax (e.g. d2:m7 g:7 c1:maj7)
and drives the chord names + guitar fret diagrams. melody_str uses
LilyPond absolute notation for the voicings (bass clef by default, or
treble when the dict sets clef: "treble"). The two must stay
synchronised in chord quality but not in voicing — the melody can be
voiced however you like.
Two-handed exercises set staves: "double" and use a 7-tuple row with
separate left- and right-hand strings:
(n, name, quality, chord_mode_str, lh_str, rh_str, label) — LH on bass
clef, RH on treble, grouped in a PianoStaff.
After edits: ./build.sh.
Open src/render.py. The HTML template, fullscreen modal, and
metronome live inline (CSS, JS, HTML strings).
Each built .html is fully self-contained (embedded fonts +
inline SVG), so it runs on any static host. This repo deploys to
GitHub Pages automatically: .github/workflows/deploy.yml installs
LilyPond, builds the site, and publishes it on every push to main —
live at https://brona90.github.io/twelve-keys/. The built HTML is
gitignored; only the sources are committed, and CI rebuilds them.