Files
guitar-practice-tool/README.md
T
immortaly007 083744203b Phase 4: drone + T6/E-series + polish + deploy + README
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>
2026-07-22 00:36:34 +02:00

137 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
## Develop
```bash
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 with
`VITE_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):
```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://<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.