Inspiration

Every fitness app I tried had the same three problems. Rep counters were boring. The "smart" ones wanted to upload video of my body to a server. And the gamified ones sold you progress - pay to skip the grind, pay to save your streak.

I wanted the opposite: a game where the workout is the combat system, where the camera watches you without anything ever leaving your phone, and where money can buy content but never strength.

What it does

REPforged is a fitness RPG. Your real repetitions are the controller: a squat swings the Forge Hammer, a push-up launches a Flame Strike, a plank holds a shield. Across seven regions, each boss teaches one movement mechanic - consistent depth builds combos, only fault-free reps break a guard, steady pacing stops an overheat.

  • Camera witness - an on-device pose model finds your joints; a transparent state machine counts the reps and coaches your form live.
  • Multiplayer without footage - Ghost Duels race a rival's signed rep timings, and community raids store only aggregate totals. No video is ever exchanged.
  • Privacy Glass - live counters in every session: frames saved 0, frames uploaded 0. The Privacy Vault shows everything stored and deletes it in one tap.
  • Recovery quests - a logged rest day keeps your streak, so nobody trains through pain to protect a number.
  • Forge Pass - three regions are free forever; the Pass adds four more, Workshop publishing and cosmetics. It never sells combat strength.

How I built it

The app is Expo SDK 57 / React Native 0.86 / TypeScript, running on Android, iOS and the web from one codebase.

Pose detection runs MediaPipe's pose_landmarker_lite entirely on device - a custom native module on mobile, MediaPipe Tasks for Web in the browser. The model only locates 33 joints. Everything after that is deterministic: each exercise is a YAML file defining the joint angle to measure, the thresholds for each state, and the form faults to watch for. The angle at joint \( b \) formed with \( a \) and \( c \) is

$$\theta = \left|\operatorname{atan2}(c_y - b_y,\ c_x - b_x) - \operatorname{atan2}(a_y - b_y,\ a_x - b_x)\right|$$

folded into \( [0^\circ, 180^\circ] \), and a rep only counts when the angle crosses each threshold and holds it long enough to rule out jitter.

Scoring is deliberately anti-grind - it measures you against your own calibrated baseline, and volume can never win:

$$\text{Forge Score} = 60\%\ \text{completion} + 25\%\ \text{rhythm} + 15\%\ \text{range}$$

with completion clamped at 100%, so doing extra reps earns nothing.

Monetisation uses the RevenueCat SDK with a forge_pass entitlement and annual, monthly and lifetime packages. RevenueCat is configured with no app user ID, so it only ever sees an anonymous ID — never a name, workout or body data. A Spring Boot 3.4 / Java 21 backend receives RevenueCat webhooks, deduplicates them, and mirrors entitlement state into an admin console alongside moderation, raid totals and Motion Ghost verification.

The code has 104 TypeScript tests and 36 Java tests. The client and server safety rules for player-built battles are mirrored, and both test suites must agree.

Challenges I ran into

My push-up counter was wrong, and I only found out by measuring it. I built a tool that replays real footage through the shipped engine and reports every threshold crossing. A clip with its own burned-in counter showed 9 reps; my engine counted 4. Every descent was detected — the misses were all at the top. I required the elbow to hold past 160° for 200 ms, but a fast athlete's lockout lasts 0–170 ms. Loosening it to 150° for 100 ms gave 9 out of 9. Squats keep the longer hold, because people really do stand still at the top of a squat.

MediaPipe's confidence scores lied to me. I tried ignoring frames where joints had low "visibility". It turned out a side-on squat scores about 0.99 while a push-up scores about 0.23 — with the arms plainly in shot. Any useful threshold stopped counting push-ups entirely, so I reverted it and wrote down why.

Camera angle matters more than the model. A front-on squat counts zero reps, because the knee angle is foreshortened and never appears to bend enough. Every threshold assumes a side-on camera.

A purchase that grants nothing. In early sandbox testing, purchases succeeded but unlocked nothing, because the products weren't attached to the entitlement. Instead of celebrating a fake unlock, the app now says exactly that. It also refuses to start billing if a Test Store key ever reaches a release build.

What I learned

Measure, don't guess. My most confident code was my least accurate until I tested it against real footage. I also learned that privacy is most convincing when it's visible - a counter reading zero, live on screen, persuades people more than any policy page.

What's next

Recording my own exercise footage for every movement, validating the lunge, plank and jumping-jack thresholds the same way I fixed push-ups, and running the seasonal expeditions the Pass is built for.

Built With

Share this project:

Updates

Submission history