Capstone report

Project documentation

How the AI Fitness Assistant turns a webcam feed into counted reps and form feedback: the problem it tackles, the architecture and maths behind every tracker, how it was tested, and where it still falls short.

31 trackers MediaPipe Pose WebGL · WebAssembly 100% on-device
01 — Summary

Abstract

The AI Fitness Assistant is a browser-based computer vision system that tracks resistance-training exercises in real time from an ordinary webcam or phone camera. Google’s MediaPipe Pose model locates 33 body landmarks in every frame; the application converts them into joint angles and body positions, smooths the signal, and runs a per-exercise state machine that only counts a repetition once its full range of motion is complete.

The Exercise Lab ships 31 trackers: 29 repetition counters and 2 timed holds. Form checks turn the on-screen skeleton red and explain the fault in plain words. All inference runs on the user’s device, so no video or body data ever leaves the browser. In automated testing every tracker passes all 100 scripted scenarios on both 16:9 and 4:3 cameras, including 33 movements that must not count as a rep.

02 — Context

Problem statement & motivation

Resistance exercise provides proven cardiovascular, metabolic and musculoskeletal health benefits. However, inexperienced lifters face substantial risks of joint strain and acute injury when they train with poor posture or a compromised range of motion, and most train without a coach watching.

Wearable inertial measurement units (IMUs) and motion-capture suits can measure movement precisely, but they need specialised, expensive hardware. This project investigates whether a consumer webcam and edge machine learning can deliver useful biomechanical feedback directly in a web browser.

The aim is not to replace a coach. It is to answer, immediately and objectively, the two questions every lifter asks: did that rep count? and was my position safe?

Person training while a camera-based system tracks their movement
03 — Scope

Objectives & requirements

Functional

  • Track the body in real time from a single RGB camera (webcam or phone).
  • Count a repetition only when the full range of motion is reached, ignoring half-reps and bounces.
  • Time isometric holds (Plank, Wall Sit) only while the position is actually held.
  • Detect common form faults and explain them in words.
  • Cover upper body, lower body, core and full-body movements.

Non-functional

  • Private: all processing on-device; no account and no uploads.
  • Zero install: a static website that runs in any modern browser.
  • Camera-independent: the same movement measures the same on any camera shape and at any distance.
  • Accessible: keyboard-operable controls, visible focus, and cues announced to screen readers.
04 — Design

System architecture

Every tracker runs the same six-step pipeline on each camera frame. Only step 5, the exercise’s own rules, differs between trackers.

  1. 1CameraFrames from getUserMedia
  2. 2Pose modelMediaPipe Pose finds 33 landmarks
  3. 3GeometryJoint angles & body ratios
  4. 4Smoothing~160 ms moving average
  5. 5State machineThe exercise’s rep rules
  6. 6FeedbackSkeleton, meter, cues, count

Components

Main parts of the application
PartLocationResponsibility
Tracker pages (23)exercises/*_web.htmlCamera session, landmark geometry, the rep state machine and cues. Each page is self-contained.
Exercise Labdashboard.htmlCatalogue of every tracker and tool, grouped by muscle group.
Shared stylesheetassets/css/common.cssDesign tokens, the lab layout, camera stage and rep progress meter.
Site behaviourassets/js/ui.jsMobile navigation, current-page highlighting and scroll reveal.
Routine modeassets/js/routine.jsOptional multi-exercise routines driven by each tracker’s repCompleted event.
Homepage demoassets/js/hero-demo.jsAnimated figure scored by the Squat tracker’s own angle formula and thresholds.
Calculatorspages/onerepmax.html, pages/strength_level.htmlEpley one-rep-max estimate and bodyweight strength standards.
MediaPipe librariesjsDelivr CDNpose, camera_utils and drawing_utils, downloaded once by the browser.

Lifecycle of a set

  1. Start opens the camera and, the first time only, creates the pose model. Later Starts reuse the same model and call reset(): MediaPipe’s WebAssembly runtime cannot be rebuilt on a page, so closing and re-creating it would leave the tracker silent.
  2. Each frame is sent to the model. Its results arrive in onResults, which measures the body, advances the state machine and redraws the skeleton.
  3. Stop ends the set: the camera turns off and the rep count, hold timer, smoothing history and form state all reset to zero. A frame still being processed when Stop is pressed is ignored, so it cannot leave stale state for the next set.
05 — Input

Pose landmarks

MediaPipe Pose, based on the BlazePose network [1], returns 33 landmarks per frame. Each has an x and y position as a fraction of the frame (0–1), a relative depth z, and a visibility score. The trackers run the full model (complexity 1) with landmark smoothing on and detection and tracking confidence set to 0.7.

A landmark is only trusted when its visibility is at least 0.5, and an angle is only measured when all three of its points pass. A limb hidden from the camera therefore drops out instead of producing a wild reading.

Landmarks the trackers use
IndexLandmarkUsed for
0NosePull-up head height; wrists overhead for Shoulder Press and Burpees
7, 8EarsBack rounding (ear against the torso line); head height when the nose isn't visible
11, 12ShouldersAlmost every angle; the shoulder-width scale for Lat Pulldown
13, 14ElbowsElbow angles; elbows-overhead gates for Tricep Extension and the Shoulder Press lockout
15, 16WristsElbow angles; bar height for Pull-ups and Lat Pulldown; the hanging and bar-support gates; hand position for Russian Twists and Jumping Jacks
23, 24HipsHip angles, body line, torso tilt for position gates and lean checks; shoulder height for Shrugs; the centre and width for Russian Twists
25, 26KneesKnee angles
27, 28AnklesKnee angles, body line, split stance and feet-at-hip-height; feet apart for Jumping Jacks; the foot on the box for Step-Ups
29–32Heels, toesHeel lift for Calf Raises
06 — Geometry

Measuring joint angles

A joint angle uses three landmarks: for the elbow, the shoulder (A), elbow (B) and wrist (C). The two-argument arctangent gives the direction of each limb segment, and their difference is the angle at B.

Landmark x is a fraction of the frame’s width and y of its height. On a 16:9 webcam one unit of x is 1.78 times longer than one unit of y, so using the raw numbers skews every angle that isn’t perfectly horizontal or vertical. The trackers rescale x by the video’s aspect ratio first:

const aspect = video.videoWidth / video.videoHeight; let radians = Math.atan2(c.y - b.y, (c.x - b.x) * aspect) - Math.atan2(a.y - b.y, (a.x - b.x) * aspect); let angle = Math.abs(radians * 180 / Math.PI); if (angle > 180) angle = 360 - angle;

Measured effect

The same two photographs were padded into three frame shapes and run through the real pose model. A good measurement should not depend on the frame shape.

Knee angle of the same photo at different frame shapes
PhotoFrameUncorrectedAspect-correctedMediaPipe 3D
Standing4:3178.6°178.1°168.7°
Standing16:9179.9°179.9°168.5°
Standing9:16172.1°175.6°167.6°
Deep squat4:377.1°74.1°68.2°
Deep squat16:963.1°65.8°64.7°
Deep squat9:1662.6°67.9°67.6°

For the squat the spread across frame shapes falls from 14.5° uncorrected to 8.3° corrected. MediaPipe’s 3D world landmarks [3] were the most stable (3.5°) but read a straight leg as about 168°, too close to the 160–165° lockout thresholds. The trackers therefore use aspect-corrected 2D angles, which keep the thresholds’ meaning.

Distance-independent measures

Some movements are judged by position rather than angle. Each is scaled by the body itself, so the result doesn’t change with distance from the camera:

  • Lat Pulldown: wrist height above or below the shoulders, measured in shoulder widths.
  • Calf Raises: heel height above the toes as a fraction of shin length, compared with a resting baseline captured at Start.
  • Pull-ups: head height compared with the hands, which mark the bar.
  • Plank, Burpees, torso-lean checks: torso tilt measured in true pixel proportions.
07 — Signal

Smoothing the signal

Landmarks jitter slightly from frame to frame. Each reading is averaged over the last ~160 ms (at most five frames). A fixed five-frame average, used previously, lags more as the frame rate drops. On a slower phone it can smooth away the bottom of a fast rep before the angle crosses its threshold.

The table shows a simulated squat rep (knee 170° → 85° → 170°) and the lowest smoothed angle each method reaches. The rep counts only if that value drops below the 100° threshold.

Lowest smoothed knee angle for a simulated fast squat
Rep lengthFrame rate5-frame average160 ms window
0.8 s8 fps119.0° · missed95.6° · counted
0.8 s12 fps104.5° · missed87.6° · counted
0.8 s30 fps87.9° · counted87.9° · counted
1.0 s8 fps107.0° · missed91.2° · counted
1.0 s12 fps95.8° · counted87.8° · counted
1.5 s8 fps95.8° · counted87.8° · counted

At 30 fps the two methods are identical; the time window only changes behaviour on slower devices.

08 — Logic

Counting reps

Each tracker is a finite state machine with two thresholds and a gap between them. For a squat, the knee angle dropping below 100° arms the rep (DOWN), and standing back up (knees above 160° and hips above 150°) while armed counts it (UP). The gap stops jitter around a single line from double-counting, and the required order means half-reps and bounces never count.

Angles alone can’t tell exercises apart: a standing curl bends the elbow exactly like a push-up. So every tracker first checks that the body is in the exercise’s position (lying back, hinged forward, hanging from the bar, in a split stance) and only then lets the angles move the state machine. Outside that position the panel shows an amber cue such as “GET INTO A PUSH-UP POSITION”, and nothing counts.

How trackers read the body

Strategies the trackers use
StrategyWhat it doesUsed by
Two-sided averageAverages the left and right angles when both are seen about equally well; otherwise trusts the clearer sideSquats, Bench Press, Deadlifts, Push-Ups, Dips, …
Position gateCounts only while the body is in the exercise’s position; shows a cue otherwisePush-Ups, Bench & Skull Crushers (lying), Rows & Kickback (hinged), Dips (on the bars), Pull Ups (hanging), Situps, Leg Raises & Glute Bridge (on the floor), Russian Twists (seated), Lunges (split stance), Step-Ups (foot on the box)
Torso tiltAngle of the hip-to-shoulder line from vertical: 0° standing, 90° lyingMost trackers, for position gates and lean checks
Feet at hip heightLying or in a plank the ankles sit near hip level; standing, a leg’s length below. Tells a toe touch from a sit-upSitups, Leg Raises, Push-Ups, Glute Bridge
Front legThe more bent knee (Lunges) or the planted leg, whose ankle sits lowest (Bulgarian Split Squat)Lunges, Bulgarian Split Squat
Lead leg on the boxLearns the floor from the lowest the ankles have been; the foot a fifth of a leg above it is on the box. Only a rep finished standing on the box countsStep-Ups
Working armThe more bent elbow; the other arm props straight on the benchSingle-Arm Dumbbell Row
Nearest sideUses the side the camera sees bestBarbell Row, Situps, Tricep Kickback, Burpees, Plank
Both arms requiredNeeds both arms in viewShoulder Press, Lateral Raise, Lat Pulldown
AlternatingOne side then the other counts as one rep: a left curl then a right curl, or a twist to each sideBiceps Curl, Russian Twists
Back roundingHow far the ear drops below the hip-to-shoulder line. Looking ahead lifts it, so only a rounding back is flaggedDeadlifts, Romanian Deadlift, Barbell Row, Single-Arm Row, Kettlebell Swings
Timed holdThe timer runs only while the position is held; Stop resets itPlank, Wall Sit
CalibratedCaptures a resting baseline over the first 15 frames of every setCalf Raises, Dumbbell Shrugs
Multi-phaseEach phase in order: stand → plank → stand → jump with arms overhead (Burpees); open, then closed (Jumping Jacks)Burpees, Jumping Jacks

Every completed rep dispatches a repCompleted browser event with the current count; routine mode listens for it to advance to the next exercise.

09 — Catalogue

Exercise reference

The exact rules each tracker applies. Angles are in degrees; “armed” is the position that must be reached before a rep can count.

Rules for every tracker
ExerciseCameraMeasuresArmedCountsForm check
Chest & Shoulders
Bench PressSide-onElbow, averaged< 90> 160Gate: lying back (torso tilt > 50)
Shoulder Overhead PressFacingElbow, both arms< 100 with forearms pointing up> 150 with the upper arms pointing up and both wrists above the headTorso lean > 20
Lateral RaiseFacingShoulder (hip–shoulder–elbow), both arms< 35> 75Elbow < 130 (bent arms); torso lean > 20
Push-UpsSide-onElbow, averaged< 90> 160Gate: body near horizontal, feet at hip height. Body line < 155 → lift or lower the hips
Front RaiseSide-onShoulder (hip–shoulder–elbow), averaged< 35> 80Elbow < 130; torso (shoulder–hip–knee) < 160 (swinging)
DipsSide-onElbow, averaged< 90> 150Gate: hands below the elbows, torso within 45 of upright
Dumbbell ShrugsFacingShoulder height above the hips, in shoulder widths, vs a baselineLift < 0.04Lift > 0.10Elbow < 150 (bent arms)
Back
Pull UpsFacing or behindElbow + head vs handsDead hang, elbow > 150Head above the handsGate: hands above the shoulders (hanging)
Lat Pull DownFacingWrist height, in shoulder widthsWrists 0.5 above shouldersWrists 0.1 below shouldersTorso lean > 30
Barbell RowSide-onElbow, nearest sideExtended > 150Pulled < 90Gate: hinged (tilt ≥ 30). Back rounding > 35
DeadliftsSide-onHip (shoulder–hip–knee) + knee, averagedHip < 90Hip > 160 and knee > 155 (full lockout)Back rounding > 35
Romanian DeadliftSide-onHip, averaged< 110> 165Back rounding > 35; knee < 130 (squatting)
Single-Arm Dumbbell RowSide-onElbow of the working (more bent) armExtended > 150Pulled < 90 with the upper arm within 55 of the torsoGate: leaning over the bench (tilt ≥ 45). Elbow bent with the upper arm hanging → drive the elbow back; back rounding > 35
Arms
Biceps CurlFacingElbow, each armArm extended > 150Curl < 60; left then right = 1 repUpper arm > 35 from the torso (elbows drifting); torso lean > 20
Overhead Tricep ExtensionSide-onElbow of each raised arm (one or both)< 60> 150Gate: elbow above the shoulder
Tricep KickbackSide-onElbow, arm nearest the camera< 90> 150Gate: hinged (tilt ≥ 30). Upper arm > 50 from the torso
Skull CrushersSide-onElbow, averaged + upper arm from vertical< 80 with the upper arms within 40 of vertical> 150Gate: lying back, elbows pointing up (within 60). Upper arms > 40 from vertical
Core
SitupsSide-onHip (shoulder–hip–knee), nearest sideLying back, hip > 120Sitting < 70Gate: torso within 35 of the floor, feet at hip height. Knee > 140 (legs straight)
PlankSide-onBody line + torso tiltTimer runs while propped on forearms or hands, torso within 35 of level and body line > 150Lift or lower the hips
Lying Leg RaisesSide-onHip (shoulder–hip–ankle), averagedFlat > 160Raised < 110Gate: lying down. Knee < 140 (bent legs)
Russian TwistsFacingHands vs the middle of the hips, in hip widthsHands 0.8 hip widths to one side, then the other = 1 repGate: seated, knees above the hips
Legs
SquatsSide-onKnee (hip–knee–ankle) + hip, averagedKnee < 100Knee > 160 and hip > 150Torso lean > 55 (chest dropping)
LungesSide-onBoth knees: front = more bentFront < 100, back < 135, feet split by half a legFront > 150Torso lean > 30
Bulgarian Split SquatSide-onKnee of the planted (front) leg< 100> 150Torso lean > 45
Wall SitSide-onKnee, averaged + torso tiltTimer runs while the knee is 80–110 and the back within 25 of uprightSlide down, rise up, or back against the wall
Calf RaisesSide-onHeel lift ÷ shin length vs baselineLift < 0.03Lift > 0.08Knee < 150 (bouncing)
Glute BridgeSide-onHip (shoulder–hip–knee), nearest sideHips down, < 140> 165 with the knees still bent (< 140)Gate: lying, feet at hip height. Knee > 140; hips more than 8% above the shoulder–knee line (arching)
Step-UpsSide-onBoth legs: foot height above the floor, knee angles, hipLead foot on the box, knee < 110Standing on the box: lead knee > 160, hip > 160Torso lean > 45
Full Body
BurpeesSide-onTorso tilt, knee, body line, wrists vs headStanding: tilt < 30, knee > 150, body line > 150Plank (tilt > 60, body line > 140), stand, then jump with the wrists above the head—
Jumping JacksFacingWrists vs the head; ankles apart, in shoulder widthsOpen: both hands above the head, feet > 1.2 apartClosed: hands below the shoulders, feet < 0.8 apart—
Kettlebell SwingsSide-onHip + knee angles, arm height (hip–shoulder–elbow)Hinged, hip < 110Hip > 165, knee > 160, arms > 70 from the torsoHip < 130 with knee < 120 (squatting); elbow < 120; back rounding > 35
10 — Experience

Feedback & interface

On the camera view

  • While a set runs, only the skeleton and the rep progress meter are drawn over the video. There are no numbers or letters on the body.
  • The skeleton turns red while a form check fails.
  • The progress meter fills from 0% at the starting position to 100% at the end of the movement. Timed holds don’t show it.
  • The canvas is sized to the camera frame and cropped exactly like the video, so the skeleton sits on the body.

In the side panel

  • The rep count (or hold time in seconds), reset to zero by Stop.
  • The next cue, colour-coded: amber waiting, volt act now, red fix your form.
  • Form messages in red, such as “KEEP YOUR BACK STRAIGHT”, “LIFT YOUR HIPS” or “HINGE AT THE HIPS, NOT THE KNEES”.
  • Position cues in amber when you aren’t set up for the exercise yet, such as “LIE BACK ON THE BENCH”, “HINGE FORWARD, BACK FLAT” or “STEP INTO A LUNGE”. Nothing counts until you are.
  • The count and cues are live regions, so screen readers announce them.

Controls: Start, Stop, Flip Camera (front or rear, with mirroring handled) and Hide/Show Skeleton.

11 — Trust

Privacy & performance

Privacy

Inference runs in the browser on WebGL and WebAssembly. No video, image or landmark data is sent anywhere: the only network requests are for the site itself, its fonts and the MediaPipe model files. There are no accounts and no analytics.

Performance

  • One pose model per page, reused across sets.
  • Only the skeleton is drawn each frame; the camera image is never redrawn onto the canvas.
  • Smoothing adapts to the device’s frame rate.
  • The full model (complexity 1) balances accuracy and speed.
12 — Testing

Evaluation

  1. Synthetic skeletons. An automated browser harness replaces the pose model with skeletons of exactly known joint angles and runs each tracker’s own page code. There are 100 scenarios covering all 31 trackers:
    • 16:9 and 4:3 cameras;
    • a person close to the camera and twice as far;
    • fast reps at about 9 fps;
    • 33 movements that must not count, such as half squats, squats on the lunge and step-up pages, push-ups or rows done standing, curls on the dips, press, pull-up, tricep and single-arm row pages, a press out to a T, a deadlift without knee lockout (or on the kettlebell page), standing toe touches on the sit-up page, a burpee without the jump, half jumping jacks, and sliding the legs out on the glute bridge;
    • form faults with the exact message expected (rounded back, sagging or piked hips, bent arms or legs, drifting elbows, leaning off the wall), and form that must not be flagged (looking ahead during a row, a 25° lean on the lat pulldown);
    • timed holds, and Stop/Start resets.
  2. Real model. The real MediaPipe Pose package ran against a fake webcam feed of photographs. Every tracker went through three Start/Stop sessions.
  3. Measurement study. The angle and smoothing comparisons in sections 06 and 07.
Evaluation results
CheckBefore the fixesNow
First engine suite, 33 scenarios, 16:9 camera20 / 3333 / 33
Technique suite, 73 scenarios, 16:9 camera36 / 7373 / 73
Technique suite, 73 scenarios, 4:3 camera—73 / 73
Wrong movements counted as reps or hold time17 / 200 / 20
Added trackers (8): 27 scenarios, 13 of them wrong movements, 16:9 and 4:3 cameras—27 / 27
Squat reps across 3 Start/Stop sessions (real model)2 / 0 / 22 / 2 / 2
Trackers still tracking after restarts (real model)—31 / 31
Text drawn over the camera viewAngle labelsNone

The 13 scenarios the previous engine failed exposed real bugs. On a 16:9 camera Deadlift, Romanian Deadlift and Wall Sit never registered. Pull-ups could not count from a dead hang, and Shoulder Press counted on the way down. Curls added reps to Shoulder Press and Overhead Tricep Extension, and Lat Pulldown depended on distance from the camera. Fast reps were missed at low frame rates.

A later technique audit found that many trackers checked the right joint but not the right exercise. Run against the previous pages, the 73-scenario technique suite passed 36. Push-ups, bench press, rows and kickbacks counted while standing. Lunges counted squats, and Bulgarian Split Squats never counted because the rear knee stays bent. Dips counted curls, and the press counted arms straightened out to a T. Deadlifts and squats counted without a full lockout, and sit-ups counted standing toe touches. Every tracker now checks the position first and the lockout fully, and each of these cases is a test.

These tests prove each tracker does what its rules say. They do not prove the thresholds suit every body: no study with real athletes has been run yet (see section 13).

13 — Outlook

Limitations & future work

Known limitations

  • Monocular depth ambiguity: a single 2D camera loses accuracy when a limb moves towards or away from it, which is why most trackers ask for a side-on view.
  • Self-occlusion: overlapping limbs lower the model’s confidence, and a hidden joint drops out of the measurement.
  • Lighting and background: dim light or a busy background adds landmark jitter.
  • Heuristic thresholds: the angles come from common coaching cues, not from a measured population, and may need tuning for mobility or body proportions.
  • One person in frame: other people in view can confuse the model.
  • No spine landmarks: the pose model has no points along the back, so back rounding is read from where the ear sits against the hip-to-shoulder line. A rounded lower back with the head held up can pass.
  • Leaning towards the camera: on the trackers you face, a lean forwards or backwards changes little in 2D, so lean checks there catch sideways leaning best.

Future work

  • A validation study with recorded, hand-labelled reps from real athletes.
  • Optional 3D world-landmark angles, with thresholds re-tuned on that dataset.
  • Per-user calibration of range of motion.
  • Temporal Convolutional Networks (TCN) for continuous phase prediction, and dual-camera fusion.
  • Per-set statistics such as tempo and depth, and adaptive filters such as the 1€ filter [5].
Conceptual illustration of future multi-camera pose research
14 — People

Student project contributors

Computer vision lead

MediaPipe & pipeline integration

Biomechanics & maths

Vector angles & FSM rep logic

Front-end engineering

HTML5 canvas & UI architecture

15 — Sources

References

  1. Bazarevsky, V., Grishchenko, I., Raveendran, K., Zhu, T., Zhang, F., & Grundmann, M. (2020). BlazePose: On-device Real-time Body Pose tracking. arXiv preprint arXiv:2006.10204.
  2. Lugaresi, C., et al. (2019). MediaPipe: A Framework for Building Perception Pipelines. Third Workshop on Computer Vision for AR/VR at IEEE CVPR.
  3. Grishchenko, I., Bazarevsky, V., Zanfir, A., et al. (2022). BlazePose GHUM Holistic: Real-time 3D Human Landmarks and Pose Estimation. arXiv preprint arXiv:2206.11678.
  4. Baechle, T. R., & Earle, R. W. (2008). Essentials of Strength Training and Conditioning. National Strength and Conditioning Association (NSCA), Human Kinetics.
  5. Casiez, G., Roussel, N., & Vogel, D. (2012). 1€ Filter: A Simple Speed-based Low-pass Filter for Noisy Input in Interactive Systems. Proceedings of CHI 2012, ACM.
  6. W3C. Media Capture and Streams (the getUserMedia API). https://www.w3.org/TR/mediacapture-streams/