A mobile-first web app that helps a gym beginner figure out what to do: pick a muscle group from a list and get beginner-friendly exercises that train it — each with sets/reps/rest guidance, form cues, common mistakes to avoid, and an embedded YouTube demo of proper form that plays right in the page.
Built for the author's girlfriend, who's a lifelong athlete (soccer — strong legs, strong core) but new to the gym, so she can feel confident choosing exercises.
The app is index.html plus a small assets/ folder of
images (the body-map artwork, added in Phase 4). No build step, no dependencies, no
framework, no backend — still a plain static site you can open in any browser or host
anywhere (GitHub Pages, Netlify, etc.).
Everything below has been built and verified working (muscle picker, equipment filter, exercise cards with sets/reps/cues/mistakes, inline video players, saving to favorites, routines, and tips).
- Refactored from "tap a body diagram" into a 4-tab app. The clickable SVG body figure was removed entirely — the muscle picker is now a region-grouped list of chips (Upper body / Core / Lower body), which is more reliable on a phone.
- Richer exercise cards. Each exercise now shows sets · reps · rest, a short list of form cues, common mistakes, the gear it needs, and the secondary muscles it works — not just a name + description.
- Equipment filter. Toggle which equipment is available (Bodyweight, Dumbbells, Cables, Machines, Barbell) and the exercise list filters to match.
- Save / favorites. A ♡ Save button on every exercise persists to
localStorageand shows up under the Saved tab. Degrades gracefully in private/incognito mode (shows a heads-up banner instead of silently failing). - Routines tab. Three hand-curated starter programs (Full body 3×/week, Glute focus, Upper/lower split). They reference exercises by key, so they stay in sync with the exercise data automatically.
- Tips tab. Short gym-confidence tips (etiquette, resting, starting light).
It's a static file, so any of these work:
- Locally: double-click
index.html, or run a static server in the folder:python3 -m http.server 4173 # then open http://localhost:4173 - GitHub Pages (how the author plans to share it): push the folder to a repo,
go to Settings → Pages, set source to the
mainbranch root. The app will be served athttps://<user>.github.io/<repo>/. - Or drop it on Netlify / any static host.
.claude/launch.jsonis configured to serve the folder for the Claude Code preview tool. Harmless to keep or delete.
Custom domain note: an earlier attempt to point
www.hannahsworkout.comat GitHub Pages failed only because the domain is not registered yet (it has no DNS records). Buy the domain first, then add aCNAMEDNS record (www→<user>.github.io) and aCNAMEfile in the repo containing the domain.
The app is five tabs, switched from a fixed bottom nav bar:
- Exercises — the main screen.
- Body map at the top — a clickable anatomical figure with Female/Male and Front/Back toggles. Tap a muscle to see its exercises; the map highlights the selection and auto-flips front/back to follow whatever you pick (even from the chips or search). It's the visual entry point Hannah wanted.
- Search — a global lookup over every exercise by name, muscle, gear, or equipment ("romanian deadlift", "glutes", "dumbbell"). Ignores the equipment filter so it always finds everything (and also matches machines).
- Equipment filter grouped the way a beginner thinks: Free weights (dumbbells + barbell), Machines & cables, Bodyweight. All on by default; toggling a category re-filters the shown group instantly.
- Muscle picker chips grouped by region — Upper body (chest, back, shoulders, biceps, triceps, traps, forearms), Core (abs, lower back), Lower body (glutes, quads, hamstrings, calves). A reliable fallback to the map.
- Tapping a group renders its exercise cards below (filtered by equipment).
- Focus sub-filter: some groups have a second chip row to target a region of the muscle — Glutes (Overall / Upper & side, i.e. gluteus medius), Shoulders (Front / Side / Rear), Back (Lats / Mid-back), Abs (Upper / Lower / Obliques). "All" is the default; the focus chips stack with the equipment filter.
- Routines — build your own and use pre-made ones:
- Routine builder ("Your routines"): tap + Routine on any exercise to
collect it, then in this tab name it, reorder (▲▼) or remove (✕) exercises, and
Save. Saved custom routines persist in
localStorageand can be edited or deleted. The draft survives a reload too. - Starter routines: three expandable pre-built programs. Each lists its exercises with sets × reps and a quick ▶ video, plus "Save a copy" to drop an editable copy into your own routines.
- Routine builder ("Your routines"): tap + Routine on any exercise to
collect it, then in this tab name it, reorder (▲▼) or remove (✕) exercises, and
Save. Saved custom routines persist in
- Machines — a guide to common gym machines. Each card shows what the machine trains, how to set it up (beginner steps), a demo video (its YouTube thumbnail is the still image), and a "See [muscle] exercises" link. Search finds machines too.
- Saved — favorited exercises (full cards), persisted in
localStorage. - Tips — short gym-confidence tips.
Each exercise shows: name, a Beginner / Intermediate badge, a one-line description, a video thumbnail (the YouTube poster image — tap it to expand the inline player; tap again to close), a sets · reps · rest row, form cues, common mistakes, a gear pill, a secondary muscles pill, a ♡ Save button, and a + Routine button (adds it to the routine builder). (The thumbnail is the play control — Hannah is a visual learner, so cards lead with the image rather than a text button.)
chest, back, shoulders, biceps, triceps, traps, forearms (upper); abs, lower back (core); glutes, quads, hamstrings, calves (lower). Weighted toward machines / dumbbells / cables / bodyweight that are easy to learn. (Lower-body selection leans a little more advanced — the user is a strong-legged ex-athlete.) The glutes group includes a full set of gluteus-medius (upper/side glute) exercises behind the Upper & side focus.
index.html+ a smallassets/image folder, no build tooling — chosen for trivial sharing and GitHub Pages compatibility. Don't introduce a framework/build step unless the user wants ongoing feature growth.- Body map = supplied anatomical images + invisible SVG hotspots (not a
hand-drawn figure). The original hand-drawn SVG was removed in v3; Phase 4 brought
the body picker back as licensed line-art images (
assets/<female|male>-<front|back>.png) with transparent clickable<polygon>s on a 500×900 overlay. The region chips stay as the mobile fallback. Don't freehand an anatomical SVG. - YouTube "Watch" buttons embed a curated, hard-coded video per exercise.
Each exercise in
DATAhas aytfield holding an 11-character YouTube video ID, rendered as ayoutube-nocookie.com/embed/<id>iframe.- Why curated and not the live YouTube API: to keep the single-file, zero-setup, zero-API-key design (a "fetch the highest-view video live" approach would need a Google API key exposed in the public file).
- Dead-link safety: every player keeps a fallback link that opens a YouTube search, so if a hard-coded video is ever removed the user can still find a replacement.
- Saving degrades gracefully.
localStorageis wrapped in try/catch; if it's unavailable (private mode), the Saved tab shows a banner rather than failing silently. Don't assumelocalStoragealways works. - Routines reference exercises by key, never by duplicating content. A routine
item is a
"group:key"string looked up in the flatALLmap. Fix an exercise once and it's fixed everywhere it appears. - Major muscle groups only, but groups can have an optional focus
sub-filter for the regions a beginner actually cares about (e.g. upper/side
glutes). Configured in
FOCUS/FOCUS_MAP, not by splitting the group. Don't add individual-head groups to the muscle picker — use a focus instead. - Dark theme,
--accentis the red/coral brand color. Focus chips use--good(teal) to distinguish them from the red muscle chips.
<style>— all CSS. Theme variables are at the top under:root. Layout is mobile-first with a fixed bottom.tabbar. Key classes:.view(one per tab,.on= visible),.card(an exercise card),.srr(the sets/reps/rest row),.cues/.mistakes(the bullet lists),.pill(gear / secondary-muscle chips),.player(.player.openreveals the inline iframe),.routine/.rrow(routines),.tip,.banner.<script>:DATA— the single source of truth for content. An object keyed by group id; each entry has atitleand anexarray. Each exercise is:{ key, name, level, yt, equip:[...], gear, sets, reps, rest, desc, cues:[...], mistakes:[...], secondary:[...] }
keyis unique within its group; routines address exercises as"group:key".equipdrives the equipment filter (subset ofEQUIP).ytis a real, oEmbed-verified YouTube ID. Edit this object to add/remove/change exercises or swap a video.
REGIONS— controls how muscle groups are grouped/ordered in the picker.EQUIP— the five canonical equipment tags (used on exercises and in filtering).EQUIP_GROUPS— how they're presented to the user (Free weights / Machines & cables / Bodyweight); a category chip toggles its underlying tags.FOCUS/FOCUS_MAP— the optional per-group focus sub-filter.FOCUS[group]is the ordered[key, label]chips to show (e.g. glutes →[["overall","Overall"],["medius","Upper & side"]]);FOCUS_MAP[group][exKey]assigns each exercise to one focus. Focus is not stored on theDATAobject — it's attached whenALLis built. A group absent fromFOCUSsimply shows no focus row. To add a focus to a group, add it to bothFOCUSandFOCUS_MAP.ROUTINES— array of built-in starter programs; each hasdays[], each day a list of"group:key"item strings.MACHINES— machine-guide entries{ name, trains, how:[...], yt, group? }.ytreuses (mostly) an exercise's validated demo of that machine; the card image is the video's YouTube thumbnail, not an/assetsphoto.grouplinks to that muscle's exercises viagoToGroup. Rendered bymachineCardHTML/renderMachines, and also matched byrunSearch.MYROUTINES/DRAFT(runtime state, not config) — the user's saved custom routines and the in-progress builder draft. Persist tolocalStorage(wf_routines/wf_draft), gated bycanSave.DRAFT.itemsand each saved routine'sitemsare filtered toALLat load so the builder's index-based move/remove stays aligned.TIPS— array of gym-confidence tip strings.HOTSPOTS— body-map click targets,HOTSPOTS[female|male][front|back]= list of{ g, p }(muscle group + SVG polygonpoints) overlaid on theassets/<type>-<view>.pngimages on a shared 500×900 canvas. Rendered byrenderBodyMap; per gender+view because proportions differ.ALL— a flat map built at load time:ALL["group:key"]→ the exercise object (plusgroup/groupTitle). Used by routines, saving, and rendering.cardHTML(id)/rowHTML(id)— render a full exercise card / a compact routine row from a"group:key"id.showGroup(group, focus)— renders the filtered exercise list for a muscle group, plus the focus chip row if the group has one.focusis optional (defaults to the current/"all" focus); the focus chips call it with an explicit focus key.runSearch(raw)— global text lookup overALL(name/muscle/gear/ equipment); renders matching cards. Ignores the equipment filter. Empty input returns to the "pick a group" placeholder.toggleEquipGroup(key)— toggle an equipment category, then re-render.renderBodyMap()/setBodyType()/setBodyView()/syncBodyMap(group)— the body map.renderBodyMapswaps theassets/image + draws the hotspot polygons;syncBodyMaphighlights the selected muscle and auto-switches front/back so chip/search selections stay visible.showGroupcallssyncBodyMap.renderRoutinesTab()— renders the whole Routines tab:renderBuilder()(the draft),renderMyRoutines()(saved customs),renderRoutines()(built-ins).toggleDraft(btn)— add/remove an exercise from the draft (the + Routine button).moveDraft/removeDraft/setDraftName/saveDraft/clearDraftdrive the builder;editRoutine/deleteRoutinemanage saved customs;saveCopyOfBuiltin(i)flattens a built-in into an editable copy.toggleRoutine(uid)— expand/collapse a routine;uidis"b"+ifor built-ins or the custom routine'sid.toggleVid(btn)— expands/collapses the inline player (one open at a time); works for both full cards and routine rows.toggleSave(btn)/renderSaved()/persistSaved()— favorites +localStorage(with the private-mode fallback).renderRoutines()/toggleRoutine(i),renderTips()— routines/tips.showTab(t)— switches the active tab.
Find the group in DATA and add an object to its ex array. Give it a key
unique within that group and fill in every field:
{ key:"inclinepushup", name:"Incline Push-up", level:"Beginner", yt:"VIDEO_ID",
equip:["Bodyweight"], gear:"A bench", sets:3, reps:"10–12", rest:"60s",
desc:"Hands on a bench, easier than the floor.",
cues:["...","...","..."], mistakes:["...","..."], secondary:["Chest","Triceps"] }Rendering, the embed, the equipment filter, and saving are all automatic.
- Find a good "proper form" tutorial on YouTube; the ID is the
v=part ofhttps://www.youtube.com/watch?v=VIDEO_ID. - Verify it's embeddable before trusting it — a
200with JSON means it exists and is embeddable:curl -s "https://www.youtube.com/oembed?format=json&url=https://www.youtube.com/watch?v=VIDEO_ID" - Paste the ID into the exercise's
ytfield.
⚠️ Never invent video IDs. Everyytvalue must be a real ID you verified with the oEmbed check above — a wrong ID silently embeds the wrong video or a dead player. (All current IDs were validated this way.)
- Add an entry to
DATAwith a new group id (with all fields on each exercise). - Add the group id to the right region in
REGIONS.
Add an object to ROUTINES with name, meta, note, and days (each day a
list of "group:key" item strings that must exist in DATA).
All four planned phases are built. Smaller ideas still open: workout-of-the-day / randomized session; mark-as-done session logging; personalize (her name in the title).
- Phase 4 — full-body clickable muscle map: female/male + front/back anatomical
line-art (user-supplied, in
assets/, normalized to 500×900 with transparent backgrounds) with an invisible SVG hotspot overlay per muscle, as the visual entry point at the top of the Exercises tab. Chips remain as the mobile fallback. This is why the app now allows a smallassets/folder. - Phase 3: machine guide (12 common machines) with how-to steps, demo videos, and a link to each machine's exercises; search now finds machines too. (Machine images are the demo videos' YouTube thumbnails.)
- Phase 2: custom routine builder — build/name/reorder/save your own routines (localStorage), edit/delete them, and "Save a copy" of a built-in to edit.
- Phase 1: global search, free-weights equipment grouping + presets, video thumbnails on cards (visual-first).
- v3: equipment filter, sets/reps/cues/mistakes, focus sub-filters, save/favorites, routines, tips.
- v2: embedded curated demo videos.