{"id":51157463,"url":"https://github.com/ts95/music-theory","last_synced_at":"2026-06-26T11:30:40.168Z","repository":{"id":361030788,"uuid":"1252797458","full_name":"ts95/music-theory","owner":"ts95","description":"A personal, custom-built music-theory tutor — spaced-repetition études for keys, scales, chords \u0026 progressions. Built with Claude Code.","archived":false,"fork":false,"pushed_at":"2026-06-14T10:25:53.000Z","size":751,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-14T11:11:03.715Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ts95.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-28T22:05:50.000Z","updated_at":"2026-06-14T10:25:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ts95/music-theory","commit_stats":null,"previous_names":["ts95/music-theory"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ts95/music-theory","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ts95%2Fmusic-theory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ts95%2Fmusic-theory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ts95%2Fmusic-theory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ts95%2Fmusic-theory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ts95","download_url":"https://codeload.github.com/ts95/music-theory/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ts95%2Fmusic-theory/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34815669,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-26T02:00:06.560Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-26T11:30:39.356Z","updated_at":"2026-06-26T11:30:40.158Z","avatar_url":"https://github.com/ts95.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Music Theory\n\nA personal, custom-built music-theory tutor — a React web app for teaching myself music theory through\ntailored, interactive exercises. Built with [Claude Code](https://claude.com/claude-code).\n\nIt's a single-user tool that runs entirely in the browser. Sign-in is optional — without it there's\nno backend or account and progress is stored locally on the device; with it your progress syncs across\ndevices. Live at **\u003chttps://ts95.github.io/music-theory/\u003e**.\n\n## Features\n\nLessons are organised into **selectable études**, each its own spaced-repetition session with its own\nprogress, chosen from a table-of-contents home screen. Twelve études today, in three sections:\n\n### 🎹 Keys \u0026 Scales\n\n- **No. 1 — Relative Minors.** Name the relative minor of a major key (\"What is the relative minor of\n  E♭ major?\"); the circle of fifths is shown on reveal. Timed (5 s sudden-death).\n- **No. 2 — Scales.** Spell the major and natural / harmonic / melodic minor scales of every key, across\n  four ABRSM-graded levels (the key range widens from ≤2 sharps/flats at Easy to all 12 keys by Hard).\n  **Expert** adds the Greek modes — Dorian, Phrygian, Lydian, Mixolydian, Locrian.\n- **No. 3 — Play the Scale.** You're given a key and **play the scale ascending** on an interactive\n  keyboard — tap/click, or strike a connected **MIDI keyboard**. Each correct note lights with its\n  RH+LH fingering; a few wrong notes are forgiven (Easy/Medium two, Hard one, Expert none) before the run\n  ends and reveals the whole scale. An optional **show-fingering** hint flashes the whole scale's finger\n  numbers for 3 s (any key hides them) — but peeking grades the exercise as failed. Sudden-death;\n  cumulative ABRSM-grade scope (Easy 1 octave / 15 s, Medium 2 / 20 s, Hard 2 / 18 s, Expert 2 / 12 s).\n- **No. 4 — Key Signatures.** Name the sharps or flats of each key, across the full circle of fifths\n  (15 major keys + relative minors) — **15 s sudden-death**. On reveal, a treble staff shows the key\n  signature with just its sharpened/flattened notes and the keyboard highlights those keys (each\n  labelled); C major / A minor show neither. Four ABRSM-style levels by key range — Easy ≤2 accidentals\n  up to **Expert** at the seven-sharp/flat keys (C♯/C♭ major).\n\n### 🎶 Chords \u0026 Harmony\n\n- **No. 5 — Chords by Degree.** Recall the diatonic chord on a scale degree (\"In C major, what is the\n  IV chord?\" → F), across every major and minor key — the common triads plus V7. Timed (5 s).\n- **No. 6 — Chord Recognition.** Read a chord drawn on the staff (under its key signature) and name it\n  as a symbol, with slash notation for inversions. Timed (10 s, +5 s when the chord is in an inversion).\n- **No. 7 — Spell the Chord.** The inverse of Chord Recognition: read a chord **symbol** and pick its\n  notes (\"Spell the chord Cm7\" → C – E♭ – G – B♭), drawn from the diatonic chords of every key. Choices\n  share the root and differ only in quality. On reveal the chord is shown on a staff **beside** a piano\n  keyboard with RH/LH fingerings. Untimed. Levels add sevenths then ninths and widen the key range.\n- **No. 8 — Progressions.** Map a Roman-numeral progression to concrete chords (\"In G major, spell\n  ii–V–I\" → Am – D – G), including ii–V–I seventh forms. The spelled chords are shown on a treble staff\n  under the key's signature on reveal. Timed (15 s).\n\n### 👂 Ear Training\n\n- **No. 9 — Intervals by Ear.** Hear an interval and name it; the lower note is randomized each time\n  (relative-pitch training). Optional hints — *step up to it* (walks the distance a semitone at a time)\n  and *consonant or dissonant?* — plus a set of **reference songs**: a familiar tune for every interval,\n  notated, that you can play to recognise the leap.\n- **No. 10 — Progressions by Ear.** Hear the tonic, then a progression, and name it in Roman numerals.\n  The chords are **voice-led** — occasionally inverted so the parts connect smoothly rather than leaping.\n- **No. 11 — Melodic Dictation.** Hear a short motif over its tonic and name it in **solfège**. A\n  **hear-scale** hint plays the whole scale with a synced solfège readout (and you can hover a syllable\n  to play just that note); the melody is shown on the staff, in key, on reveal. Miss it and the **whole\n  scale** is shown as a solfège readout with the melody's notes marked in a distinct colour (so you can\n  see where they sit in the scale and learn what each syllable means); it **auto-plays the missed melody**,\n  lighting each note as it sounds, and you can hover any degree to hear that syllable on its own.\n- **No. 12 — Rhythm Dictation.** Hear a one-bar rhythm and pick the matching notation. A\n  research-graded vocabulary that follows the grade bands: sixteenth cells (ti-tika / tika-ti),\n  dotted-eighth and Scotch-snap figures, the **named syncopations** (syncopa, tresillo 3+3+2,\n  Charleston, cinquillo, habanera), ties and anticipation pushes, and the **full triplet family** —\n  eighth, **quarter- and half-note triplets**, sixteenth triplets, shuffled (tied) and gapped triplet\n  cells — plus double dots and the 6/8 hemiola at the top. The metres accumulate with difficulty: Easy\n  is **4/4, 3/4, 2/4**, Medium adds **6/8**, **cut time (₵)** and **12/8**, Hard adds **5/4** and the\n  asymmetric **5/8 (3+2)** and **7/8 (2+2+3)**, and **Expert** pushes the tempo and density; a count-in\n  sets the tempo and metre (uneven clicks in 5/8 \u0026 7/8 — the long beat spans three eighths). A\n  **time-signature picker** lets you narrow practice to any subset of a level's metres (all on by\n  default), remembered per level and synced across devices.\n- **No. 13 — Tap the Rhythm.** The performance flip side of Rhythm Dictation: **read** a one-bar rhythm\n  and **tap-and-hold it in time** (Space, press the screen, or any key of a connected **MIDI keyboard** —\n  the pitch is irrelevant, only the rhythm) over a count-in and a steady metronome click on every beat. The **tempo is adjustable** (a 30–90 BPM slider, remembered per level); a count-in with a\n  beat count (**1·2·3·4**) sets it, then the staff flashes **green** to mark the downbeat where your bar begins.\n  The note head you're about to play **lights up** during the count-in (a silent preview) and on **Hear it** —\n  never while you're tapping — and the **timing bars** below the staff can be hidden (the **Bars** toggle) to\n  practise with less assistance. A **counting guide** sits under the notes (Traditional numbers or Kodály\n  syllables), and below the staff the **full sub-beat count** is always shown (`1 e \u0026 a 2 e \u0026 a …`, on-beats\n  **bold**, off-beats greyed) with each subdivision **lighting up in time** as the count-in and the bar\n  play. You're scored on timing accuracy — a little early or late still counts (a forgiving, flat ~200 ms window)\n  — and each note must be **held for most of its length** (~70%, or ~40% for quick notes — sixteenths, thirty-seconds, triplets)\n  to count, so a note is sustained, not clipped. The result colours each note\n  **on the beat / a little off / missed or too short**, with **Hear it** to compare and **Try again** for a\n  practice run (your first attempt is the one that's graded). On a connected **MIDI keyboard**, middle C\n  begins / advances, B retries, and A plays it back. Same metres and four levels as Rhythm Dictation —\n  including the asymmetric **5/8** and **7/8**, whose count-in beats are uneven, and the multi-beat\n  triplets, whose `1·trip·let` count stretches across their true span in the sub-beat lane.\n\n### Across the études\n\n- 🎚️ **Difficulty levels.** Most études have four bands — **Easy / Medium / Hard / Expert** (remembered\n  per étude), calibrated to the ABRSM grades (≈ 1–3 / 4–5 / 5–6 / 7–8+, adjusted per étude). Levels widen\n  the key range and add harder material (compound intervals, longer/wider melodies, busier rhythms,\n  sevenths/ninths, inversions) — cumulatively, so harder includes easier.\n- 🔊 **Hover to hear it.** Hover any answer to play it on a synthesized piano — scales arpeggiate,\n  chords ring as a block, progressions play chord-by-chord. Toggle with **♪ Sound**.\n- 👆 **Touch-friendly.** Every hover preview also works by touch. **Press** an answer to hear it, **slide**\n  across the options to scrub through them, and **release on one to choose it** — slide off and release to\n  cancel. Single buttons commit on a normal tap. The mouse keeps single-click everywhere.\n- 🎼 **See it on the staff.** Ear-training answers (and progression spellings) are rendered with VexFlow\n  on reveal, under the correct key signature.\n- 🧠 **Learn from misses.** Get one wrong (or let a timer run out) and a **Remember** note explains the\n  rule, pattern, or mnemonic with a worked example. Each étude also has a collapsible **reference box**\n  of the key facts (remembered open/closed).\n- 🤷 **\"I don't know.\"** A fifth option on every question: admit a blank instead of guessing. It reveals\n  the answer and sends the scheduler the strongest \"bring this back soon\" signal.\n- ⏱️ **Timed recall (optional).** Several categories are sudden-death (5–15 s); let the clock run out and it\n  counts as a miss, so the scheduler resurfaces that item sooner. A header toggle (**⏱ Timed / Untimed**)\n  turns the clock off entirely — answer at your own pace — and the choice is remembered.\n- 🪶 **Gentle pacing.** Each étude serves at most **10 due cards per 5-hour window**, so a backlog never\n  feels overwhelming.\n- 📈 **Daily practice time.** Each étude tracks active minutes practiced **today** (it pauses when you\n  switch away, and counts at most 15 s per question so idling on a card doesn't inflate it), with\n  per-section and overall totals on the home screen; resets at midnight, or on demand per étude or\n  globally.\n- 📖 **About page.** A short explainer on how (and why) spaced repetition works.\n- ☁️ **Optional sync.** Sign in with an **email magic link** to sync your SRS progress and practice time\n  across devices. Signed out, everything stays local on the device.\n- 📱 **Installable (PWA).** Add it to your home screen for a full-screen, app-like experience. Installed\n  apps sign in with the **8-digit code** from the email (the magic link would open in the browser, a\n  separate session); in a normal browser tab you still just click the link.\n\n## Tech stack\n\nVite · React · TypeScript (strict) · Tailwind CSS · Tone.js (audio) · VexFlow (notation). Unit tests with\nVitest, browser tests with Playwright.\n\n## Getting started\n\nPrerequisites: **Node.js 20+**.\n\n```bash\nnpm install      # install dependencies\nnpm run dev      # start the dev server, then open the printed localhost URL\n```\n\nOther commands:\n\n```bash\nnpm run build      # production build\nnpm run preview    # preview the production build locally\nnpm run test       # Vitest unit tests (theory/ + srs/ + helpers)\nnpm run test:e2e   # Playwright browser smoke tests (auto-starts the dev server)\n```\n\n## How it works\n\nEvery fact you study — a key relationship, a scale's notes, a fingering, a chord, an interval, a rhythm —\nis a separately scheduled card under an **SM-2-style** spaced-repetition scheduler. Answer well and the\ninterval to the next review grows; miss it (or hit \"I don't know\") and it comes back soon, with the ease\ndropped further the more confidently you blanked.\n\n- **Levels** partition or widen an étude's material into Easy / Medium / Hard / Expert; each level keeps\n  its own scheduling, so progress on one doesn't leak into another.\n- **Timed recall** is sudden-death: Relative Minors and Chords by Degree (5 s), Chord Recognition (10 s,\n  +5 s for inversions; Expert 8 s, 12 s for inversions), Progressions (15 s), plus Play the Scale's\n  per-level clock. Other categories are untimed. The whole timer can be switched off with the\n  **⏱ Timed / Untimed** header toggle.\n- **Pacing** caps each étude at 10 due cards per rolling 5-hour window.\n- **Sync:** progress lives in the browser's `localStorage`. Optionally **sign in** (email magic link,\n  Supabase-backed) to sync progress and practice time across devices; signed out, the app stays fully\n  local.\n\n## Project structure\n\n```\nsrc/\n├── theory/          # Pure music-theory domain (no React/DOM): notes, keys, scales,\n│                    #   chords, recognition, fingerings, MIDI, ear-training realization\n├── srs/             # SM-2-lite scheduler + localStorage / versioned-JSON persistence\n├── supabase/        # Optional sign-in + cross-device sync (client, session, sync side-effects)\n├── audio/           # The only Tone.js consumer (hover/ear playback, lazy-loaded)\n├── questions/       # ETUDES registry + builds MC questions, explanations, distractors\n├── components/      # React UI: review session, question card, staves, keyboard,\n│                    #   circle of fifths, interval-song pages, about page, info box, auth controls\n├── contracts.ts     # Shared domain + question types\n├── intervalSongs.ts # Reference tunes + notes for each ascending interval\n├── levels.ts · prefs.ts · dueCap.ts · time.ts · useEtudeTimer.ts · practiceHistory.ts\n├── rhythm.ts · rhythmCounting.ts · tempos.ts · midi.ts (Web-MIDI input) · touch.ts\n└── App.tsx          # Routing (one path per étude, /about, /interval-songs, /history) + shell\n```\n\nSee [CLAUDE.md](./CLAUDE.md) for the architecture, conventions, and working principles used when\ndeveloping this project.\n\n## Status\n\nEarly and evolving — built incrementally to fit how I actually want to learn.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fts95%2Fmusic-theory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fts95%2Fmusic-theory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fts95%2Fmusic-theory/lists"}