尚未收集儲存庫資訊。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Real-time exercise tracking in the browser. A webcam or phone camera, Google's MediaPipe Pose model and a state machine for each exercise count your reps, time your holds and flag form faults. Every frame is processed on your own device.
Live site · Exercise Lab · Documentation · How it works
A Computer Science & Applied AI senior capstone project, built for educational demonstration. It is not medical or coaching advice.
Angles are in degrees. A rep is armed at the first value and counted at the second. The documentation lists the camera setup for each tracker.
| Group | Exercise | Measures | Rep rule | Position gate · form check |
|---|---|---|---|---|
| Chest & Shoulders | Bench Press | Elbow angle | < 90 → > 160 | Lying back |
| Shoulder Overhead Press | Elbow angle, both arms | < 100, forearms up → > 150, upper arms up and wrists above the head | Torso lean > 20 | |
| Lateral Raise | Shoulder angle (hip–shoulder–elbow) | < 35 → > 75 | Bent arms (elbow < 130); torso lean > 20 | |
| Push-Ups | Elbow angle | < 90 → > 160 | Body horizontal, feet at hip height · hips sagging or piked (body line < 155) | |
| Front Raise | Shoulder angle | < 35 → > 80 | Bent arms; torso swing (< 160) | |
| Dips | Elbow angle | < 90 → > 150 | Hands below the elbows, torso within 45 of upright | |
| Dumbbell Shrugs | Shoulder height above the hips, in shoulder widths, vs a baseline | Lift < 0.04 → > 0.10 | Bent arms (elbow < 150) | |
| Back | Pull Ups | Elbow angle + head vs hands | Dead hang (> 150) → head above hands | Hands above the shoulders |
| Lat Pull Down | Wrist height, in shoulder widths | 0.5 above → 0.1 below the shoulders | Torso lean > 30 | |
| Barbell Row | Elbow angle, nearest arm | > 150 → < 90 | Hinged forward (tilt ≥ 30) · back rounding > 35 | |
| Deadlifts | Hip + knee angle | Hip < 90 → hip > 160 and knee > 155 | Back rounding > 35 | |
| Romanian Deadlift | Hip angle | < 110 → > 165 | Back rounding > 35; knee < 130 (squatting) | |
| Single-Arm Dumbbell Row | Elbow of the working (more bent) arm | > 150 → < 90 with the upper arm back along the torso | Leaning over the bench (tilt ≥ 45) · elbow not driven back; back rounding > 35 | |
| Arms | Biceps Curl | Elbow angle, each arm | > 150 → < 60, left then right | Elbows drifting (> 35); torso lean > 20 |
| Overhead Tricep Extension |
A position gate must hold before a rep can count; until it does, the panel shows an amber cue such as "GET INTO A PUSH-UP POSITION". Back rounding is how far the ear drops below the hip-to-shoulder line, so looking ahead is never flagged.
Every tracker runs the same pipeline on each camera frame:
Camera: frames come from getUserMedia, via MediaPipe's camera_utils.
Pose model: MediaPipe Pose (BlazePose) returns 33 landmarks with a visibility score. A joint is used only when its visibility is at least 0.5.
Geometry: joint angles come from three landmarks, using the two-argument arctangent. Landmark
x/y are fractions of the frame's width and height, so x is rescaled by the 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;
Smoothing: each reading is averaged over the last ~160 ms (at most 5 frames), so slower phones don't lag behind fast reps.
Position gate: the body must be in the exercise's position (lying back, hinged forward, hanging, in a split stance…) before the angles count, so a standing curl can't pass for a push-up.
State machine: the exercise's two thresholds. For a squat, a knee angle below 100° arms the rep
(DOWN); standing tall again, with knees above 160° and hips above 150°, counts it (UP).
Feedback: the skeleton (red on a form fault), the rep progress meter, the cue panel and the rep counter.
The documentation covers the maths, the measurements behind these choices and the evaluation in detail.
localhost: browsers only grant camera access on secure origins.There is no build step: everything is plain HTML, CSS and JavaScript.
git clone https://github.com/21BoaH/AIFA.git
cd AIFA/AIFitnessAsisstant
# Python 3
python3 -m http.server 8080
# or Node
npx serve .
Open http://localhost:8080 for the homepage, or http://localhost:8080/dashboard.html for the Exercise Lab.
| Control | What it does |
|---|---|
| Start / Stop | Begin or end a set. Stop resets the count, timer and smoothing. |
| Flip Camera | Switch between front and rear cameras. |
| Hide / Show Skeleton | Toggle the overlay. Counting carries on either way. |
AIFA/
├── .github/workflows/static.yml GitHub Pages deployment (publishes AIFitnessAsisstant/)
├── README.md
└── AIFitnessAsisstant/
├── index.html Homepage with the animated pose demo
├── dashboard.html Exercise Lab: all 31 trackers + tools
├── exercises/ One self-contained page per tracker (*_web.html)
├── pages/
│ ├── about.html Project documentation (technical report)
│ ├── howitworks.html Methodology explainer
│ ├── onerepmax.html 1RM calculator
│ └── strength_level.html Strength standards
├── assets/
│ ├── css/common.css Design tokens and every shared component
│ ├── js/ui.js Navigation, current-page highlighting, scroll reveal
│ ├── js/hero-demo.js Homepage demo, scored by the squat tracker's own logic
│ ├── js/routine.js Optional routine mode (see below)
│ ├── js/db.js, js/auth.js Unused prototype stubs, not loaded by any page
│ └── images/ Photos (WebP) and README screenshots
├── design-system/ Design direction that common.css implements
└── Presentation_Images/ Images used in the project presentation
Each tracker is a standalone page, so a new exercise starts from the existing tracker closest in shape:
| If the new exercise… | Start from |
|---|---|
| Is judged by one joint angle on both sides | squat_web.html |
| Only counts in a set position (lying, hinged…) | pushUps_web.html or barbellRow_web.html |
| Uses the front leg only | bulgarianSplitSquat_web.html |
| Uses the limb nearest the camera | tricepKickback_web.html |
| Is a timed hold | wallSit_web.html |
| Needs a baseline at Start | calfRaises_web.html |
Then:
exercises/<name>_web.html and update the <title>, the <h1> and the setup hint
(the #progress element).// Initialize variables, the cues in updateUI() and the
landmark indices, thresholds and form checks in onResults(). Reset any new state in stopCamera().visibleSides(), then read smoothed angles with readAngle() (both
sides) or smooth(key, jointAngle(...)) (one side). Both are aspect-corrected.torsoTilt()/readTilt() for position gates and lean checks. While the body isn't in position,
set positionCue (an amber cue) and don't touch stage.stage = 'down' to arm a rep, and stage = 'up' plus counter++ to count it. Require the full
lockout, not just one joint passing a line.repCompleted with the count, so routine mode works.warning to the message in onResults(). setFormWarning() turns the
skeleton red and shows it as a red cue.dashboard.html in the right muscle group, renumber the cards, and update the group
count, the jump-link count and the "Models" total.assets/js/routine.js chains trackers into a routine with rest timers between exercises. No page in
the site creates routines yet; it is a hook for future programmes. To start one, store the routine and
open the first tracker with ?routine=true:
localStorage.setItem('activeRoutine', JSON.stringify([
{ name: 'Squats', url: 'squat_web.html', targetReps: 10, restTime: 60 },
{ name: 'Push-Ups', url: 'pushUps_web.html', targetReps: 12, restTime: 45 },
{ name: 'Plank', url: 'plank_web.html', targetReps: 30 } // seconds for timed holds
]));
localStorage.setItem('routineStep', '0');
location.href = 'exercises/squat_web.html?routine=true';
When a tracker's repCompleted count reaches targetReps, the routine stops the camera and shows a
rest countdown (restTime seconds, default 60). It then opens the next exercise, and finishes with a
completion screen.
The trackers were verified during development with an automated browser harness. It is not part of this repository.
| Check | Before the fixes | Now |
|---|---|---|
| First engine suite: 33 synthetic scenarios, 16:9 camera | 20 / 33 | 33 / 33 |
| Technique suite: 73 synthetic scenarios, 16:9 camera | 36 / 73 | 73 / 73 |
| The technique suite on a 4:3 camera | — | 73 / 73 |
| Wrong movements counted as reps or hold time | 17 / 20 | 0 / 20 |
| Added trackers (8): 27 scenarios, 13 of them wrong movements, 16:9 and 4:3 | — | 27 / 27 |
| Squat reps across 3 Start/Stop sessions (real MediaPipe model) | 2 / 0 / 2 | 2 / 2 / 2 |
| Trackers still tracking after restarts (real model) | — | 31 / 31 |
| Text drawn over the camera view | Angle labels | None |
@mediapipe/pose package with a fake webcam feed.These tests prove each tracker follows its rules. They do not prove the thresholds suit every body, because no study with real athletes has been run yet.
The interface follows a "Volt Lab" sports-tech direction: an electric volt accent (#D7FF3A) and a
cyan data accent (#5CD3F5) on a near-black canvas (#0A0C0F), with viewfinder motifs taken from the
pose tracker itself. Type is Archivo (expanded cut for headlines) with JetBrains Mono for data, labels
and readouts.
All colour, spacing, typography and motion values are CSS custom properties declared once at the top of
assets/css/common.css. Change a token there and it propagates across all 29 pages; avoid hardcoding
hex values or pixel sizes in page-level styles. design-system/ai-fitness-assistant/MASTER.md documents
the tokens and components.
prefers-reduced-motion.Every push to main runs .github/workflows/static.yml, which publishes the AIFitnessAsisstant/
folder to GitHub Pages at https://21boah.github.io/AIFA/. The repository must be public, or on a plan
that supports Pages, with Settings → Pages → Source set to GitHub Actions.
The documentation's limitations and future work section goes further.
This project is built for educational and research demonstration purposes. Rep thresholds, form checks, strength standards and 1RM estimates are informational benchmarks, not clinical assessments.
| Elbow angle of each raised arm |
| < 60 → > 150 |
| Elbow above the shoulder (one arm or both) |
| Tricep Kickback | Elbow angle, nearest arm | < 90 → > 150 | Hinged forward · upper arm off the torso (> 50) |
| Skull Crushers | Elbow angle + upper arm from vertical | < 80, upper arms up → > 150 | Lying back, elbows pointing up · upper arms drifting (> 40) |
| Core | Situps | Hip angle | > 120 lying back → < 70 | Torso near the floor, feet at hip height · straight legs (knee > 140) |
| Plank | Body line + torso tilt | Timer while propped up, level and body line > 150 | Lift or lower the hips |
| Lying Leg Raises | Hip angle (shoulder–hip–ankle) | > 160 → < 110 | Lying down · bent knees (< 140) |
| Russian Twists | Hands vs the middle of the hips, in hip widths | 0.8 to one side, then the other = 1 rep | Seated, knees above the hips |
| Legs | Squats | Knee + hip angle | Knee < 100 → knee > 160 and hip > 150 | Chest dropping (torso lean > 55) |
| Lunges | Both knees | Front < 100, back < 135, feet split → front > 150 | Split stance · torso lean > 30 |
| Bulgarian Split Squat | Knee of the planted front leg | < 100 → > 150 | Torso lean > 45 |
| Wall Sit | Knee angle + torso tilt | Timer while 80–110 with the back upright | Slide down / rise up / back against the wall |
| Calf Raises | Heel lift ÷ shin length vs a baseline | < 0.03 → > 0.08 | Bent knees (< 150) |
| Glute Bridge | Hip angle (shoulder–hip–knee) | < 140 → > 165 with the knees bent | Lying, feet at hip height · straight legs; arching past the shoulder–knee line |
| Step-Ups | Both legs: foot height above the floor, knees, hip | Lead foot on the box, knee < 110 → standing tall on the box (knee and hip > 160) | Torso lean > 45 |
| Full Body | Burpees | Torso tilt, knee, body line, wrists | Stand → plank → stand → jump with the hands overhead | — |
| Jumping Jacks | Wrists vs the head; ankles apart, in shoulder widths | Open (hands overhead, feet > 1.2 apart) → closed (hands down, feet < 0.8) | — |
| Kettlebell Swings | Hip + knee angles, arm height | Hip < 110 → hip > 165, knee > 160, arms > 70 from the torso | Squatting it (hip < 130, knee < 120); bent arms; back rounding > 35 |