Squat
92–168°Knee angle
- Go deeper — aim for thigh parallel
- Chest up, less forward lean
- Keep both feet planted
Kinetix reads 33 pose landmarks from your camera at frame rate, turns them into joint angles, and counts reps with a hysteresis state machine that does not double-count when you shake. Every rep is scored on range of motion and tempo, with live cues when your form slips.
Illustration of the trainer readout. Open it for live tracking on your own camera.
A rep counter is easy to demo and easy to get wrong: a naive threshold on a noisy 30 Hz angle trace double-counts almost every rep. So the state machine is checked on every push against synthetic traces with known ground truth, each one modelling a failure mode that actually broke it during development.
| Trace | What it models | Expected | Counted | Result |
|---|---|---|---|---|
| clean | textbook cadence | 8 | 8 | pass |
| noisy | ±4° jitter | 8 | 8 | pass |
| partial | 40% ROM must not count | 0 | 0 | pass |
| pause-at-bottom | 1.2 s hold mid-rep | 5 | 5 | pass |
| micro-bounce | twitching at the top | 4 | 4 | pass |
| dropout | 0.5 s tracking loss | 6 | 6 | pass |
| fast-cadence | 0.8 s reps | 10 | 10 | pass |
| drift | rising baseline | 6 | 6 | pass |
8/8 traces exact, plus 5 movement definitions checked for threshold consistency. Generated 2026-07-31 by npm run validate, which CI runs on every push. Frame rate and inference latency are shown live in the trainer rather than quoted here, because they depend on your hardware.
MediaPipe BlazePose returns 33 landmarks per frame. The wasm runtime and the model bundle are self-hosted, so no CDN and no upload — the video stays in the tab.
Phone cameras are off-axis, so one limb is always half-occluded. Kinetix scores landmark visibility per side and measures the confident one instead of averaging a blurred signal.
The chosen movement reduces to one primary joint angle plus secondary checks (torso tilt, hip line, elbow drift). Angles are smoothed with an EMA that tolerates dropped landmarks.
A two-state machine with a hysteresis band. Reps need full travel, at least 45% of target range, and 0.55 s spacing — so jitter, pauses and micro-bounces at the top do not inflate the count.
Every rep is stored with tempo, achieved range of motion, peak angle and which form checks fired, then classified good / partial / sloppy.
Sets persist in IndexedDB and export as CSV, one row per rep. No account, no server, no analytics.
Adding a movement is data, not code: a primary joint, two thresholds, a hysteresis margin, a range target and a list of form checks.
Knee angle
Elbow angle
Elbow angle
Elbow angle
Hip angle
Single-camera 2D. Angles are measured in the image plane, so a rotated body reads shallower than it is. Stand square to the framing the app suggests.
Counting is validated on synthetic angle traces with known ground truth, not on a labelled video corpus. The failure modes covered are jitter, pauses, partial reps, dropouts, fast cadence and baseline drift.
Form checks are heuristics on joint geometry, not physiotherapy. They catch gross faults — sagging hips, a leaning torso, an elbow drifting forward — and say nothing about load, tempo prescription or injury.
One person in frame. Multi-person tracking is off by design: it doubles compute and this is a single-user tool.
Camera permission, 2.5 m of space, and the whole body in frame. The first load pulls a 5.5 MB model; after that it is cached.