Add drone synth + DronePlayer, wire T6 (mode over drone) and E1-E3 info pages (E3 carries a drone). Screen Wake Lock while any click/drone plays. Vitest suite for scheduler timing (no drift over 5 min) and music-layer correctness (triads, CAGED, diatonic, fretboard). Deploy artifacts: public/_redirects, multi-stage Dockerfile + nginx.conf (SPA fallback, immutable asset caching, gzip), .dockerignore. README covering build, Cloudflare Pages / nginx / Docker deploy, and the deep-link URL format. prefers-reduced-motion honored throughout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.7 KiB
Guitar Practice Helper
A static web app that turns the drill codes from my Obsidian practice wiki (R1, F2, T3, …) into interactive helper pages: click tracks, pattern grids, chord & fretboard diagrams, and a drone. Open a drill link from the wiki on phone or desktop, press play, practise for ~5 minutes. No accounts, no backend, no analytics.
Built with Svelte 5 + Vite as a fully static SPA. Music theory is powered by
tonal. Timing uses a Web Audio lookahead
scheduler so the metronome never drifts.
Develop
npm install
npm run dev # local dev server
npm run check # svelte-check (typecheck)
npm test # vitest (scheduler timing + music-theory correctness)
npm run build # typecheck + production build to dist/
npm run preview # serve the built dist/ locally
Deploy
The build is fully static (dist/). Because the app uses path routing
(clean URLs like /drill/R3), the host must serve index.html for unknown paths
(SPA fallback), and the base path is baked in at build time.
Base path
- Dedicated domain / served at the root: default,
base = '/'. - Subpath (e.g.
https://example.com/practice/): build withVITE_BASE=/practice/ npm run build.
Cloudflare Pages
Framework preset: none. Build command npm run build, output directory dist.
public/_redirects (already included) provides the SPA fallback:
/* /index.html 200
For a subpath, set the VITE_BASE environment variable in the Pages build settings.
Docker + nginx
A multi-stage Dockerfile (Node build → nginx:alpine) with the correct config
(nginx/default.conf: SPA fallback via try_files, immutable caching for hashed
assets, no-cache for the HTML shell, gzip):
docker build -t guitar-practice . # root deploy
docker build --build-arg VITE_BASE=/practice/ -t guitar-practice . # subpath
docker run -p 8080:80 guitar-practice # http://localhost:8080
Existing nginx server
Serve dist/ and add the SPA fallback to your location block:
location / {
try_files $uri $uri/ /index.html;
}
Deep-link URL format
The whole point — link to any drill directly from the wiki:
https://<host>/<base>drill/<CODE>
Optional query parameters override the stored/default settings:
| Param | Applies to | Example |
|---|---|---|
bpm |
click-track drills | ?bpm=66 |
variant |
drills with variants | ?variant=2-3-2 |
key |
T2, T4 | ?key=D |
root |
T3, T5, T6, L4 | ?root=A |
interval |
T5 | ?interval=P5 |
mode |
T6 | ?mode=dorian |
pair |
R4 | ?pair=g-c |
Examples:
https://example.com/practice/drill/R3?bpm=66&variant=2-3-2
https://example.com/practice/drill/T6?root=A&mode=dorian
https://example.com/practice/drill/R4?pair=f-bm
Unknown codes resolve to a friendly “unknown drill” page linking back to the index
(/ shows all drills grouped by category).
Drills
| Code | Name | Helper |
|---|---|---|
| R1 | 16th-note strum, mute the off-beats | metronome + strum grid |
| R2 | Accent patterns | metronome + strum grid |
| R3 | 7/8 loop (drop to 4/4 live) | metronome + strum grid |
| R4 | One-minute chord changes | countdown + tap counter + chord diagrams |
| R5 | Palm-mute chugging | metronome + strum grid |
| F1 | Travis picking | metronome + picking lane + chords |
| F2 | PIMA arpeggio over I–V–vi–IV | metronome + picking lane + chords |
| F3 | Thumb-independent bass + melody | metronome + picking lane |
| T1 | Name every note on one string | metronome prompter + fretboard |
| T2 | CAGED shapes | fretboard, prev/next |
| T3 | Triads on a 3-string set | chord diagrams, inversions |
| T4 | Diatonic chords of a key | chord diagram row |
| T5 | Intervals from a root | fretboard |
| T6 | Mode over a drone | drone + fretboard |
| L1 | Alternate picking builder | metronome + picking lane |
| L2 | Legato runs (triplets) | metronome + picking lane |
| L3 | String skipping | metronome + picking lane |
| L4 | Bends and vibrato | reference tone |
| E1 | Interval recognition | info |
| E2 | Chord quality recognition | info |
| E3 | Play along by ear | info + drone |
Keyboard (desktop)
- Space — play / pause
- ↑ / ↓ — BPM ±2
Notes
- Audio unlocks on the first tap/keypress (mobile autoplay policy).
- Per-drill settings (BPM, variant, key, best scores) persist to
localStorageunder thegph:namespace. - Screen Wake Lock keeps the phone awake while a click or drone is playing.
- Respects
prefers-reduced-motion: the beat still advances, without the glow/swing.