fit-lab: Designing a Fitness Assessment That Has to Show Its Work
A free, evidence-based body and movement assessment built for India
Most fitness onboarding asks what body part you want to train, gives you a BMI label, and turns a few preferences into a confident-looking plan. fit-lab starts from the opposite position. It asks only for measurements that change an answer, screens for reasons a generic plan may be unsafe, checks whether the goal and timeline agree, then prescribes a complete movement foundation with its evidence and limits attached.


What the product actually does
fit-lab is a free, mobile-first fitness assessment for India. It combines height, weight, waist, neck, age, sex at birth and, where applicable, hip circumference into a body-fat range and practical risk context rather than pretending BMI is the whole answer. It runs a PAR-Q+-informed readiness screen and a SCOFF-informed food-risk screen before discussing a goal. Then it checks whether the requested weight change and timeline are plausible.
- Treat BMI as the assessment
- Let the user pick muscles
- Turn a seeded slider into an answer
- Hide caveats in generic fine print
- Use the measurements that change the read
- Prescribe complete movement coverage
- Require an explicit answer or interaction
- Put evidence and limits beside the claim
My role
I designed and built the experiment end to end: assessment logic, safety flow, anthropometric deformation, male and female body assets, anatomy coverage, exercise catalogue, dosing, responsive interaction, visual system, source registry, validators, tests and GitHub Pages deployment. It was a solo build, and the honest story is not a straight line from idea to polish. Several of the most important decisions came from admitting that an implementation was wrong and replacing it.
The architecture is an evidence chain
The assessment is deterministic. Measurements and explicit answers become a typed profile. Screening decides whether the product should stop, caution or proceed. Goal logic checks feasibility. The foundation engine selects complete movement capacities for the available environment. The dose engine assigns goal- and training-age-specific sets, reps, RIR and progression. Validators then refuse to ship a broken link between the recommendation, anatomy and source.
| Layer | What it decides | How it stays honest |
|---|---|---|
| Body | Body-fat range, waist context, percentiles and the visible figure | Measured inputs, sex-aware formulae, uncertainty bands and mesh limit notes |
| Safety | Whether to stop, caution or proceed | Explicit group closure; untouched means unanswered, never no |
| Goal | Whether a target and timeline agree | Rate models use goal type and training age instead of motivational copy |
| Foundation | Which movement capacities and variants are required | Automatic full-body coverage, anatomy validation and verified external guides |
| Dose | Sets, reps, effort, progression and weekly distribution | Deterministic rules with goal-specific tests and overlap caps |
The body model had to be rebuilt, not tuned


The first deformation system scaled whole horizontal slices of the mesh. That was mathematically convenient and visually dishonest. Moving a waist measurement could pull the arms and hands with it. Shoulder changes could leak into the skull. Weight could make unrelated regions swell because the algorithm knew height bands, not anatomy.
I replaced it with region-aware deformation. Waist, shoulder, weight, neck and other measurements now act through per-region masks with controlled falloff, so a local measurement stays local. The rebuild also added honest limit notes. If a combination of measurements pushes beyond what the base mesh can represent cleanly, the product says so instead of presenting a distorted render as measurement truth.
| Failure | Why patching was not enough | Replacement |
|---|---|---|
| Waist changed arms and hands | Horizontal slices had no concept of anatomical ownership | Waist and torso masks with bounded falloff |
| Shoulder edits reached the skull | Vertices were grouped by height rather than body region | Shoulder, neck and head regions separated explicitly |
| Extreme inputs looked precise | The renderer had no language for the asset's representational limit | Profile validation plus visible limit notes |
| One base body stood in for everybody | Ratios cannot turn a male topology into a female body | Sex-aware mesh and profile loading |
One redesign failed hard enough to freeze and roll back
A large in-flight redesign and deformation rewrite became tangled. The live preview was badly broken, and each attempted rescue mixed new visual work with uncertain body logic. The responsible move was not to keep polishing the branch until it looked plausible. I froze the work as a git stash, rolled back to a known-good commit, and restarted the deformation work with smaller boundaries and explicit validators.
The onboarding got smaller because most of it did no work
The original flow was dense: character customization, a personality inventory, redundant sliders and questions that did not change the result. I cut it to three visual data moments - body, safety and goal. Every remaining field now has to move a formula, threshold, safety state, mesh or exercise variant.


The cuts required rules, not just fewer screens. A seeded slider value is not an answer until the person touches it. Selecting female adds the hip measurement because the formula uses it; selecting male removes it. Changing sex invalidates safety answers when the applicable question set changes, so a stale 'none apply' cannot silently answer a newly visible pregnancy question.
- Twenty-eight asks to use nine answers
- Character details mistaken for assessment data
- Prefilled controls treated as user intent
- Safety state survived a changed question set
- Body, safety and goal only
- Every field changes a visible output
- Untouched values remain unanswered
- Inapplicable safety state is invalidated
Users do not pick muscles anymore
The exercise system began as a muscle picker. That sounds empowering, but it produces incomplete plans and lets preference masquerade as evidence. Someone can select chest and arms while omitting squat, hinge, pull, carry, aerobic base, balance or mobility capacities. I replaced that interaction with an automatic full-body foundation.
The foundation covers strength, aerobic base, balance, mobility, yoga and calisthenics, with optional running, boxing and outdoors emphasis. The user's training environment selects viable variants; it does not decide whether a capacity matters. A custom anatomy-coverage validator now fails the build if a recommended capacity cannot be represented in the 3D anatomy scene.


Every exercise has to earn a live, specific technique guide
An exercise cannot ship with an invented tutorial, a dead link or a generic channel homepage. Every recommendation requires an exact external technique guide URL. The build checks that the URL is live; when the source is YouTube, it verifies the video identity through oEmbed. If the guide cannot be verified, the exercise does not ship.
| Evidence surface | Build rule | What the user can inspect |
|---|---|---|
| Technique guide | Exact URL, live response and verified identity where applicable | The external guide attached to the prescribed exercise |
| Formula or threshold | Registered source and evidence kind | Why the number exists and where it came from |
| Exercise evidence | Catalogue entry linked to the source registry | Guideline, meta-analysis, study or editorial basis |
| Dataset | Source, transformation and limitation recorded | Population context behind percentiles or ranges |
| 3D asset | Licence file and profile validation required | Mesh and anatomy provenance |
The dose engine had a real hypertrophy bug
The first goal-specific engine gave beginners who selected build muscle only four sets per muscle per week. That number resembles a bare maintenance floor, not a useful hypertrophy prescription. The output looked tidy and the code was deterministic, but the product advice was wrong.
I caught it late and retuned weekly volume by training age into evidence-backed hypertrophy ranges, while preserving overlap caps so compound exercises do not create absurd totals. Sets, reps, RIR, progression and weekly scheduling are now generated together and tested as a system rather than copied into cards independently.
Research does not count until it reaches the shipped catalogue
A late systematic audit found a different kind of failure. Useful research on yoga, calisthenics, a strong sit-to-stand and mortality finding, and neck, arm and lumbar capacities had been gathered but never connected to the shipped data. The notes existed; the product did not know them.
I audited the research set against the exercise and capacity catalogues, added the missing material, and kept the same gate: no verified guide, no shipped exercise. The source-registry validator now catches orphaned claims and broken references so research cannot look complete merely because it exists in a document.
The design-system migration exposed a token collision
I adopted shadcn/ui and Radix primitives to build a proper light/dark system around the orange action accent. The migration surfaced a subtle failure: shadcn's --muted and --radius names collided with pre-existing fit-lab variables using the same names for different purposes. Most secondary text became nearly invisible in light mode without producing a compile error.
The bug was caught by before-and-after visual comparison, not by assuming a component library's defaults were safe. I separated the legacy and shadcn token roles, then rechecked the assessment in both themes. The lesson was not to avoid the library. It was to treat token adoption like an API migration with namespace and contrast consequences.
Accessibility changed the implementation
The release sweep combined axe-core with a manual keyboard and screen-reader walk across every stage, both themes and two viewport sizes. It found contrast pairs that technically failed WCAG AA, a generated accordion without a visible focus treatment, and an ARIA state mismatch introduced while replacing hand-rolled toggles with Radix primitives.
WebGL is also not required to understand the body. The Three.js and React Three Fiber scene has an accessible flat-SVG fallback for unavailable WebGL, while labels describe the entered measurements rather than asking assistive technology to interpret a canvas. The goal was not to claim clinical or assistive-technology certification. It was to make the tested path operable and honest about what the visual can and cannot communicate.


Publishing failed for an operational reason
The first Pages push was blocked by a 403. The environment's default GitHub credential was read-only for the repository. The fix was not a deployment workaround or a copied token in a script; it was authenticating as the actual repository-owner account for push and Pages operations, then keeping deployment in GitHub Actions as a static Vite build.
What is under the interface
The app uses React, TypeScript, Vite and Tailwind. Three.js and React Three Fiber render the body and anatomy scene, with a flat SVG fallback. shadcn/ui and Radix provide interaction primitives, Recharts draws the gauges, and Framer Motion handles transitions. Deterministic evidence, dose, goal, screening, capacity and schedule engines are covered by more than 270 tests.
Custom build validators inspect body-mesh assets, anatomy coverage, technique-guide link integrity and source-registry integrity. GitHub Actions builds the static site and deploys it to GitHub Pages. The operational shape is intentionally simple; the product rules are where the complexity belongs.
What I can and cannot claim
fit-lab is a working v1, not a finished clinical product. It can show that the assessment logic, evidence links, body assets, anatomy coverage, responsive flow, themes and tested accessibility paths work as designed. It cannot diagnose, replace a clinician or prove health outcomes. There is no usage analytics yet, so I cannot honestly claim completion rates or behavior change.
What I would fix next
The clearest product gap is language. The product is India-first, but the verified technique-guide catalogue is English-only. That excludes people the product is supposedly designed for. The next version should add verified guides in major Indian languages without relaxing the identity and link-integrity checks. I would also add privacy-respecting usage analytics, broaden assistive-technology testing, and test the assessment with clinicians and people using it over time.