A personal fitness PWA where an AI coach named Tawfiq learns your workout history, builds your next workout from a deterministic algorithm, and coaches you through chat — grounded in your actual training data, not generic advice.
Live beta ending July 10
The Problem
Solo — UX, Product Strategy, and Full-Stack Development
React · TypeScript · Vite (frontend, Vercel) · Node.js + Express (backend, Railway) · Supabase (PostgreSQL + Auth) · Groq (Llama 4 Scout 17B)
Core product live · iterating on real user feedback
17+ across all flows, manual-logging flow rebuilt twice based on gym testing
Logging workouts at the gym is friction-heavy. Spreadsheets are slow. Most apps require you to know the exercise name before you can log it — and when you're between sets, the last thing you want to do is type.
TawfiqFit removes that barrier two ways: Tawfiq builds your workout for you based on your training history (no decision fatigue), and logging is reduced to 1-2 taps per exercise via a pre-filled bottom sheet. Tawfiq acts as a coach throughout — learning your history and goals to give guidance that's grounded in your actual data, not generic advice.
A deterministic algorithm reads your training history and picks today's session — Push/Pull/Legs rotation, recovery-aware, progressive overload. AI only writes the explanation.
Ask anything. Tawfiq knows your history, your goals, and what you said last week.
Strength, volume, streaks — compared week over week. Bar chart built in pure CSS, no charting library.
The Process
Every screen went from sketch to shipped. Left side shows the wireframe intent; right side shows the built result. Click any image to zoom.
Wireframe
Built
Before and after redesign
Wireframe
Built
Personalisation complete — Tawfiq calibrated before a single account detail is entered.
Wireframe
Built
Empty home prompts the first action — Tawfiq speaks, zero stats don't
Before
After
"How it works" redesigned — camera permission removed, "Build me a plan" added as step 1, camera demoted to step 4 marked "Soon"
Wireframe
Built
Coach chat — interactive, context-aware, AI-ready
Wireframe
Built
Each Progress has different metrics based on user goal "Lose Weight, Stay Consistent or Build Muscle
Wireframe
Built
Profile — your plan first, app settings second
Visual Identity
Designed before a single component was built. Every colour, typeface, and mark is intentional — and lives in one file.
tokens.css
:root {
/* Surfaces */
--tf-paper: #FAF9F7;
--tf-bone: #E5DFD2;
--tf-char: #1A1714;
--tf-ink: #14110F;
--tf-stone: #6B6660;
/* Brand */
--tf-clay: #6B2417;
--tf-clay-2: #8C3A2B;
--tf-ochre: #B8924A;
}
Fonts & stacks
--tf-font-display: "Newsreader", Georgia, serif;
--tf-font-body: "Geist", ui-sans-serif, sans-serif;
--tf-font-mono: "Geist Mono", ui-monospace, Menlo, monospace;
Delivered files
assets/
tokens.css
wordmark-dark.svg
wordmark-light.svg
mark.svg
mark-dark.svg
favicon-32.png
favicon-16.png
Design Thinking
Eleven decisions that shaped the product — pivots, real-world testing, infrastructure tradeoffs, and the moments where the obvious path was wrong.
Mentor feedback reshaped the entire product hierarchy before a single backend line was written for the new direction
The original concept was camera-first: point your phone at a gym machine, AI identifies it, log the set. Onboarding, home screen, and navigation were all built around this promise.
A mentor reviewed the product and gave one piece of feedback that restructured everything: "Reframe AI-suggested workouts as the primary feature, with camera logging as complementary."
This single sentence changed: the home screen's primary CTA (camera → "Build me a plan"), the onboarding narrative (camera demo → meeting Tawfiq as a coach), the three feature pills (Photo Log/AI Coach/Progress → AI Plans/Smart Coach/Real Progress), and the entire backend roadmap (camera/vision API moved to "Coming soon," AI suggestion algorithm became priority #1 backend feature).
Before — Home
Camera was the primary CTA — "Take a photo" in clay red
After — Home
Tawfiq speaks first. "Build me a plan" is the primary action
Before — Onboarding
Onboarding sold the camera feature — three empty placeholder cards
After — Onboarding
Tawfiq introduced via animated chat. Pills: AI Plans / Smart Coach / Real Progress
Before — Onboarding 2
Camera permission screen
After — Onboarding 2
Camera demoted to "Soon", "Build me a plan" added
Before — HomeMidSession
Camera CTA still present mid-session
After — HomeMidSession
Camera CTA removed mid-session too — one logging component end to end
Why: Camera-first made sense intuitively — it's the most "wow" demo. But it required the most infrastructure (Vision API, image storage, identification accuracy) for a feature that's genuinely complementary to the core value: knowing what to train and tracking progress. Leading with AI-suggested workouts meant the MVP could ship and be tested with real users weeks earlier, while camera remains visible on the roadmap as "Coming soon" — not abandoned, just sequenced correctly.
The AI workout suggestion is ~95% deterministic code and ~5% AI-written prose
Every part of "Build me a plan" that could cause harm if wrong — which exercises, how many sets, what weight — is decided by code, not by a language model. Groq (Llama 4 Scout 17B) receives only the result of the algorithm's decision and writes a 2-sentence human explanation.
The algorithm, step by step
Goal-based parameters
Build muscle
3 sets · 8 reps · 4 exercises
Compound first ✓
Lose weight
3 sets · 12 reps · 4 exercises
Compound first ✓
Improve endurance
3 sets · 15 reps · 4 exercises
No compound priority
Stay consistent
2 sets · 10 reps · 3 exercises
No compound priority
| Goal | Sets | Reps | Exercises | Compound first |
|---|---|---|---|---|
| Build muscle | 3 | 8 | 4 | ✓ |
| Lose weight | 3 | 12 | 4 | ✓ |
| Improve endurance | 3 | 15 | 4 | — |
| Stay consistent | 2 | 10 | 3 | — |
10 edge case scenarios tested with seeded data
Normal PPL rotation
✅Long gap (10 days) — reduced sets + recovery note
✅Same session type 3× in a row — forced change
✅Zero sessions — beginner full-body
✅All muscle groups trained within 48h
✅ after fix"Use dumbbells" modifier
✅Goal changes reps (muscle vs endurance)
✅"Add an exercise" caps at 5
✅Full-body trainer detection + rotation
✅Progressive overload trigger (+2.5kg)
⚠️ Deferred v2| Scenario | Result |
|---|---|
| Normal PPL rotation | ✅ |
| Long gap (10 days) — reduced sets + recovery note | ✅ |
| Same session type 3× in a row — forced change | ✅ |
| Zero sessions — beginner full-body | ✅ |
| All muscle groups trained within 48h | ✅ after fix |
| "Use dumbbells" modifier | ✅ |
| Goal changes reps (muscle vs endurance) | ✅ |
| "Add an exercise" caps at 5 | ✅ |
| Full-body trainer detection + rotation | ✅ |
| Progressive overload trigger (+2.5kg) | ⚠️ Deferred v2 |
The AI-written part is only the italic explanation at the top — everything else is algorithm output
Why: A language model choosing exercise names risks hallucination — suggesting "Cable Lat Pulldown Machine" when the actual database entry is "Lat Pulldown Machine" silently breaks logging. By constraining the AI to writing prose ABOUT a pre-computed result, the suggestion is always logically sound and always references real exercises — while still feeling personal and conversational.
The most valuable feedback came from abandoning the app mid-workout
After the AI suggestion flow was built and tested at a desk, it was taken to an actual gym. The result: the app was abandoned mid-session. The friction wasn't in the AI — it was in logging sets.
What happened: browsing to find an exercise required digging through a category with no search. Logging 3 sets meant tapping into a reps field and a weight field SIX times (2 fields × 3 sets), each requiring the keyboard to open. After saving, a toast notification took a few seconds to confirm the save — long enough to feel like nothing happened. A notes field, after saving, visually disappeared (it had saved correctly, but looked broken).
Friction ranked by impact vs. effort
"Same as last time" pre-fill
Optimistic save (instant confirmation)
Search in category view
Notes confirmation chip
| Fix | Effort | Impact |
|---|---|---|
| "Same as last time" pre-fill | Medium | 🔥 Highest |
| Optimistic save (instant confirmation) | Small | High |
| Search in category view | Small | Medium |
| Notes confirmation chip | Small | Medium |
Before
6 separate tap-and-type interactions per exercise
After
Bottom sheet, pre-filled. One tap on "Done" logs the whole exercise.
Why: No amount of desk-testing surfaces friction the way actually using the product mid-workout does. The abandonment wasn't a minor annoyance — it was the exact failure mode that makes people quit fitness apps after a week. This single piece of evidence justified rebuilding the entire manual-logging flow — twice, as the next decision shows.
Chips → horizontal ruler → iOS-style wheel picker, with real bugs at every stage
Replacing 6 taps with fewer taps required a new way to input reps and weight. Three approaches were built and tested, in order:
Attempt 1 — Chips
Set 1 — Reps
Worked for typical values — broke for a 100kg first-time lift, which needed 16 taps on "+" from a 50kg default.
Attempt 2 — Ruler
Set 1 — Weight (kg)
50 kg
↔ Swipe to adjust
Solved the range problem — but precise values (e.g. 105.5kg) required careful, deliberate swiping with no margin for error. Also had no desktop mouse support.
Attempt 3 — Wheel ✓
Two-column wheel (Reps | Weight), pre-scrolled to last values. Reached after fixing height-collapse and z-index ghosting bugs in the library integration.
Why: Building custom scroll/gesture physics by hand (Attempt 2) hit the exact category of edge cases — boundaries, momentum, cross-device input — that a mature library has already solved. The lesson: for generic interaction patterns, research a library FIRST; for app-specific logic (the bottom sheet, history pre-fill, save/sync logic), hand-build because no library fits anyway.
Manual logging no longer needs a dedicated "session in progress" screen
With the new LogSetSheet, LogManual and ExerciseList became capable of showing session progress directly — so they became the session view themselves, for the manual-logging flow.
Before — Exercise list
Plain text rows, no images, no search
After — Exercise list
Image grid, search bar, equipment filters
Category badges
Red "1 ✓" badge — progress at a glance
Logged checkmarks
Tapping a checked exercise opens edit mode, not a duplicate
End session summary
Full set detail before confirming end
Why: A separate "session in progress" screen duplicated information that LogManual/ExerciseList could show natively via badges and checkmarks — and required navigating back and forth between "where am I in my workout" and "let me log this exercise." Collapsing these into one view removes a navigation layer entirely.
The app logged what you did. It never asked how it felt.
Mentor session flagged missing session quality data. The workout was captured — sets, reps, weight — but nothing about the experience behind it.
A lightweight check-in now appears automatically when a session ends. Three optional fields:
Saves to a session_feedback table linked to the session record.
Appears automatically after ending a session — no navigation required
Why: Immediate post-session capture is the most accurate — energy and feel are already fading by the next morning. Automatic appearance after "End session" means it gets answered before the user leaves the app. All fields are optional to avoid friction. Over time this gives Tawfiq context that sets and reps alone can't provide.
Every "smart" feature was audited: is this actually AI, or just a query?
Workout suggestion
✓ Real AIWhat the user sees: Personalised workout plan with explanation
What actually runs: Deterministic PPL algorithm decides exercises/sets/weight; Groq (Llama 4 Scout 17B) writes only the explanation prose
Tawfiq coach chat
✓ Real AIWhat the user sees: Personalised coaching advice
What actually runs: Groq (Llama 4 Scout 17B) — prompt includes session history, goals, last 3 sessions' full detail
Exercise history recall
DB queryWhat the user sees: "You did 3×12 last week"
What actually runs: Database query + string template
Progress stats
SQLWhat the user sees: Streak, volume, weekly delta
What actually runs: SQL aggregates
Rest timer
JS timerWhat the user sees: Countdown between sets
What actually runs: setTimeout()
| Feature | What the user sees | What actually runs | Real AI? |
|---|---|---|---|
| Workout suggestion | Personalised workout plan with explanation | Deterministic PPL algorithm decides exercises/sets/weight; Groq (Llama 4 Scout 17B) writes only the explanation prose | ✓ Real AI |
| Tawfiq coach chat | Personalised coaching advice | Groq (Llama 4 Scout 17B) — prompt includes session history, goals, last 3 sessions' full detail | ✓ Real AI |
| Exercise history recall | "You did 3×12 last week" | Database query + string template | DB query |
| Progress stats | Streak, volume, weekly delta | SQL aggregates | SQL |
| Rest timer | Countdown between sets | setTimeout() | JS timer |
Why: Labelling a database query as "AI" is dishonest and creates wrong expectations. If a deterministic function handles a feature well, AI is the wrong tool. This audit also keeps API costs near zero — only the 2 genuinely intelligent features ever hit the model.
The wearables feature is shown as 'coming soon' — and asks users which to build first
Why: Device sync (Fitbit, Apple Watch, Garmin) is a v1.1 feature, not built yet. Rather than hide it or fake it, the Connect screen presents it honestly as 'coming soon' and asks 'which matters most to you?' — turning a placeholder into a way to learn which integration users actually want first. Commit to the vision, but let real demand decide the roadmap.
Every visual decision lives in one file
Why: No Tailwind, no Bootstrap — every color, radius, and spacing is a CSS custom property. Global theme changes are one-line edits. Each component's styles reference tokens directly via .module.css files.
The AI behind the coach can be upgraded anytime — the user never notices
Why: AI services change fast — they get better, cheaper, or occasionally go down. The coach was built so a better AI can be swapped in at any time, and the user never notices a thing: same chat, just smarter answers over time. The user gets continuity; the product keeps improving without a rebuild.
Visual Polish
A full contrast audit was run across the app for dark mode — every screen below was checked against WCAG contrast guidelines and fixed where needed.
Post-session feedback — emoji rating, body-feel selector, optional notes.
Exercise detail — before/after reference images, stats grid, Tawfiq's contextual note.
Coach chat — Tawfiq references specific weights and exercise history from real data (e.g. "You did Cable Lat Pulldown with 52.5kg recently").
Under the Hood
users
id, name, goal, activity_level, created_at, terms_accepted_at, terms_version
sessions
id, user_id, name, started_at, ended_at, duration_minutes
exercises
id, user_id, session_id, name, muscle_group, logged_at, entry_method
sets
exercise_id, user_id, set_number, reps, weight_kg
coach_messages
id, user_id, role, content, created_at
exercise_library
id, name, primary_muscle, secondary_muscle, equipment_type, difficulty, gif_url
session_feedback
session_energy, body_feel, notes, skip true or false
The exercise_library table — 83 exercises, single source of truth the suggestion algorithm draws from.
Tawfiq runs on Groq (Llama 4 Scout 17B, OpenAI-compatible API). Each request's system prompt includes a JSON summary built from 4 Supabase queries:
Conversation history (last 10 messages) is included for context. Users get 8 messages/day (UTC midnight reset) — checked before the expensive summary query runs, so over-limit requests fail fast.
Validation
The coach went through two model iterations on Groq's API. Both were tested against the same 30-question framework across 6 categories — each question had a known expected answer based on seeded data. The goal wasn't just accuracy. It was finding what breaks before a real user does.
Data Accuracy
Does the coach correctly read session count, averages, top lifts, dates?
Goal-Specific
Does advice adapt to the user's goal — lose weight, build muscle, etc.?
Planning
Can it suggest today's workout, a weekly plan, or which muscles to prioritize?
Historical
Can it recall past sessions, progression arcs, and strongest muscle groups?
Edge Cases
How does it handle questions it can't answer — body fat %, calories burned?
Emotional
Does it handle "I don't feel like training" with empathy before data?
| Category | Questions | What it tested |
|---|---|---|
| 1 — Data Accuracy | 7 | Does the coach correctly read session count, averages, top lifts, and dates from the database? |
| 2 — Goal-Specific | 5 | Does the coach adapt its advice to the user's stated goal? |
| 3 — Planning | 5 | Can it suggest today's workout, a weekly plan, or which muscles to prioritize? |
| 4 — Historical | 4 | Can it recall past sessions, progression arcs, and identify strongest muscle groups? |
| 5 — Edge Cases | 5 | How does it handle questions it can't answer — body fat %, calories burned, week comparisons? |
| 6 — Emotional | 4 | Does it handle "I don't feel like training" with empathy before data? |
Model 1 — Tested June 6, 2026
20
Passing ✅
6
Partial ⚠️
1
Failing ❌
30
Total
Category 1 — Data Accuracy
7/7 · 100% ✅▸ "How am I doing?" → 13 sessions, 2.6/week average, 59 min avg session returned correctly ✅
▸ "Best squat ever?" → 72.5kg on June 2 returned correctly ✅
▸ "Muscle groups this week?" → Correct + flagged unworked groups ✅
Category 2 — Goal-Specific
5/5 · 100% ✅▸ "Am I making progress?" → Redirected from scale to consistency metrics (correct for "lose weight" goal) ✅
▸ "Am I eating enough?" → Correctly acknowledged no food tracking data ✅
Category 3 — Planning
5/5 · 100% ✅▸ "What should I do today?" → Correctly suggested Push day after a Pull day ✅
Category 4 — Historical
2/4 passing · 2/4 partial ⚠️▸ "How has my bench press improved?" → Only saw last 2 sessions, missed the full 50→62.5kg arc ⚠️
Category 5 — Edge Cases
4/5 passing · 1/5 failing ❌▸ "Compare this week to last week?" → Completely incoherent response ❌
Category 6 — Emotional
2/4 passing · 2/4 partial ⚠️▸ "I don't feel like training today" → "Skipping can set you back" — too harsh ⚠️
▸ "I missed a week, should I give up?" → Used real data to show no full week was actually missed — excellent ✅
Grouped by what type of thinking they required, not just what changed in the code.
UX & Coaching Quality
Data Quality (what the model could see)
Model 2 — Tested June 11, 2026 · Final
Switched for one primary reason: token efficiency on the free tier. Spot-checked with 8 questions after all fixes were already in place.
"How was last week?"
✅"What do you suggest I workout today?" → Push/Legs with specific exercises and weights
✅"Give me a plan I can do now" → 3 exercises with sets, reps, and rest times
✅"Tell me more about myself" → goal, sessions, recent workouts, top lifts all correct
✅"I don't want to go today" → "That's okay, it's normal" — empathetic tone
✅"Do you know the list of exercises the app offers?" → Correctly said no, offered history instead
✅"How can I achieve 100kg bench press?" → Progressive overload from real current weight (55kg)
✅"But my hand hurts" → Suggested rest, empathetic tone
✅Daily message limit (8 messages) triggered correctly after 8 messages ✅
Input tokens per request
Output tokens per request
Daily token limit (free tier)
Max users at 8 msgs/day
| Metric | Llama 3.3 70B | Llama 4 Scout 17B |
|---|---|---|
| Input tokens per request | ~2,440 | ~1,767 (28% more efficient) |
| Output tokens per request | ~120 | ~43 (64% more efficient) |
| Daily token limit (free tier) | 100,000 | 500,000 (5× more) |
| Max users at 8 msgs/day | ~5 users | ~35 users |
| Response quality | Excellent, longer | Good, slightly shorter |
| Empathy handling | Good (after fix) | Good |
| Data accuracy | Excellent | Excellent |
Decision: Llama 4 Scout 17B chosen for beta — 5× more users supported on the free tier, token usage 28–64% more efficient, response quality adequate for coaching use. The slightly shorter responses were an acceptable tradeoff for a beta that needs to support multiple real users without hitting limits.
Roadmap
Core product is live and deployed. Auth, AI coach, suggestion algorithm, manual logging (rebuilt with bottom-sheet + wheel picker), and legal pages are all shipped. Camera identification, calendar view, and a visual design refresh are next.
users
id, name, goal, activity_level, created_at, terms_accepted_at, terms_version
sessions
id, user_id, name, started_at, ended_at, duration_minutes
exercises
id, user_id, session_id, name, muscle_group, logged_at, entry_method
sets
exercise_id, user_id, set_number, reps, weight_kg
coach_messages
id, user_id, role, content, created_at
exercise_library
id, name, primary_muscle, secondary_muscle, equipment_type, difficulty, gif_url
session_feedback
session_energy, body_feel, notes, skip true or false
Current status
Auth, AI suggestion algorithm, AI coach, manual logging (rebuilt with bottom-sheet + wheel picker), session-state badges, and legal pages are all live and deployed. Camera identification, calendar view, and a visual design refresh are next.