# 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`](https://github.com/tonaljs/tonal). Timing uses a Web Audio lookahead scheduler so the metronome never drifts. ## Where this routine comes from The drill set began with the YouTube video [**“How to get SO GOOD at guitar it feels ILLEGAL (Using Neuroscience)”**](https://www.youtube.com/watch?v=7SNcy_GprQU). Its practice principles were turned into a concrete, coded routine — the R/F/T/L/E drills kept in my Obsidian wiki — in [this conversation](https://claude.ai/share/dfded9f7-5a26-4ea7-b265-fab118e32ed7). This app then gives each of those drill codes an interactive helper so the routine is actually runnable next to the guitar. The routine is built on a handful of evidence-based practice ideas from the video: - **Interleaving + active recall** — mix skills within a session and test yourself cold, instead of mindless blocked repetition of the same lick. - **A practice journal** as the engine that makes the rest work. - **Short, frequent sessions** over long grinds. - **The additive approach** — build a passage up in tiny increments. - **Deep practice / myelin** — effortful reps right at the edge of your ability. - **Mental practice** away from the instrument. Several of these are baked into the app: the “clean? +2” tempo bump enforces additive, edge-of-ability progression (only speed up once a rep is clean); per-drill BPM persistence supports short daily sessions and a journal-like record of where you left off; and the mixed drill categories on the index page nudge you toward interleaving rather than grinding one skill. ## Develop ```bash 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): ```bash 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: ```nginx location / { try_files $uri $uri/ /index.html; } ``` ## Deep-link URL format The whole point — link to any drill directly from the wiki: ``` https:///drill/ ``` Optional query parameters override the stored/default settings: | Param | Applies to | Example | |------------|-----------------------|----------------| | `bpm` | click-track drills, T1 | `?bpm=66` | | `string` | T1 (1 = high e … 6 = low E) | `?string=6` | | `variant` | drills with variants | `?variant=2-3-2` | | `key` | T2, T4 | `?key=D` | | `root` | T3, T5, T6 | `?root=A` | | `interval` | T5 | `?interval=P5` | | `mode` | T6 | `?mode=dorian` | | `note` | L4 | `?note=C` | | `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 `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.