A fast, mobile-first web app for logging your gym workouts and tracking your strength trends over time. Built as an installable Progressive Web App — add it to your phone's home screen and it works offline, right there at the rack. Also looks great on your computer.
No accounts, no servers, no tracking. Your data is stored locally on your device, with one-tap backup/transfer via an export file.
- Quick logging built for the gym — big touch targets, numeric keypads, exercise name autocomplete from your history, and "carry over" placeholders so logging your next set is one tap.
- Sessions by day — flip between days, edit anything, add session notes.
- Trends — per-exercise charts for estimated 1‑rep‑max (Epley), top-set weight, and total volume, plus automatic personal records.
- Body-weight tracking with its own trend chart.
- History with totals (sessions, exercises, sets, volume).
- lb / kg display toggle.
- Backup & transfer — export your data to a JSON file and import it on another device (merge or replace).
- Program-aware — load any day from your 86-week training plan with its set/rep/tempo targets, instead of retyping exercise names.
- Offline-first PWA — installable, works with no signal.
This repo's Markdown plan (Workout Tracker - Block #N ... .md, 86 weeks) is
parsed into data/program.json so you can pull a prescribed day straight into
the logger:
- On the Log tab, tap 📋 Load a day from your program.
- Pick the week and session (e.g. Monday – 5x10 – Day 1).
- The day's exercises are added with their set/rep/tempo targets, and each shows what you lifted last time — just fill in weight and reps.
Exercise-name autocomplete also draws from the program, and the empty tables in the Markdown are templates only (no weights are stored there).
This is automatic: a GitHub Action
(.github/workflows/rebuild-program.yml) regenerates data/program.json and
commits it back whenever you push a change to a Workout Tracker - Block*.md
file (or to the parser) on main — so the deployed app's program picker always
matches your plan. You can also trigger it manually from the repo's Actions
tab ("Run workflow").
To regenerate it locally instead:
python3 tools/import_program.py # rewrites data/program.json- Push this repo to GitHub (already done if you're reading this there).
- In the repo, go to Settings → Pages.
- Under Build and deployment, set Source = Deploy from a branch,
choose the branch (e.g.
main) and folder/ (root), then Save. - Wait a minute, then open the URL it gives you, e.g.
https://philquist.github.io/workouts/.
The included
.nojekyllfile makes sure GitHub Pages serves the app as-is.
It's all static files, so any static server works:
# Python
python3 -m http.server 8000
# then open http://localhost:8000
# or Node
npx serve .Open the site in your phone's browser, then:
- iOS (Safari): Share → Add to Home Screen.
- Android (Chrome): menu ⋮ → Install app (or tap the Install button in the app's top bar).
It then launches full-screen like a native app and works offline.
Everything lives in your browser's localStorage on the device you're using —
nothing leaves your phone or computer. That means:
- Data is per-device. To move it, use Data → Export on one device and Import on the other.
- Back up regularly with Export. Clearing your browser data / site data will erase the log.
Want automatic sync across devices later? The data layer (
js/db.js) is isolated behind a smallDBAPI specifically so a synced backend can be added without rewriting the UI.
index.html app shell + bottom tab navigation
css/styles.css mobile-first dark theme
js/db.js data layer (localStorage) + analytics ← swap for sync later
js/charts.js tiny dependency-free SVG line chart
js/app.js views + hash router (Log / History / Trends / Data)
manifest.webmanifest PWA manifest
sw.js service worker (offline caching)
icons/ app icons (generated)
data/program.json training program parsed from the Markdown logs
tools/import_program.py rebuilds data/program.json from the Markdown plan
tools/make_icons.py regenerates the icon set (pure Python, no deps)
The app itself has no build step and no dependencies — just open
index.html via a static server. The tools/ scripts (standard-library
Python only) are needed only to regenerate the program data or icons.
- Estimated 1RM uses the Epley formula:
weight × (1 + reps / 30)(a single rep reports the weight itself). - Volume is
Σ (weight × reps)across all sets. - The lb/kg setting is a display label only — it doesn't convert your stored numbers, so pick one and stay consistent.