Files
guitar-practice-tool/README.md
T
2026-07-23 10:40:33 +02:00

4.7 KiB
Raw Blame History

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

pnpm install
pnpm dev           # local dev server
pnpm check         # svelte-check (typecheck)
pnpm test          # vitest (scheduler timing + music-theory correctness)
pnpm build         # typecheck + production build to dist/
pnpm 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 with VITE_BASE=/practice/ pnpm build.

Cloudflare Pages

Framework preset: none. Build command pnpm 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;
}

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 IVviIV 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 localStorage under the gph: 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.