Inspiration
Every school fun run and push-up challenge works the same broken way. Friends say "a dollar a lap," somebody counts on paper, and a week later a volunteer types the totals in and the platform charges everyone's saved card. Some platforms have no cap at all. Donors get surprised, volunteers do data entry, and the cause waits. We wanted to see if PayPal's authorize-then-capture flow could fix the whole thing: the hold is the donor's promise, the camera is the scorekeeper, and the charge happens while the kid is still on the floor.
What it does
An organizer sets up a challenge (athlete, cause, exercise, time) and shares a link or QR code. Each donor picks a price per rep and a limit, for example $1 a rep, $25 max. PayPal holds the limit. Nothing is charged yet. On the live screen, the athlete's webcam counts push-ups, squats or jumping jacks right in the browser. The scoreboard shows reps, dollars and every donor's tile climbing. At TIME, each donor is charged exactly the lesser of reps times rate and their limit, and the rest of their hold is released. One PayPal Payouts batch then sends the total to the cause. Claude Haiku writes the thank-you lines and a recap, and checks three camera frames so a bounced phone cannot fake a count. Judges can press "Try it now" for a demo with five labeled bot donors, or pledge for real in the PayPal sandbox.
How we built it
TypeScript end to end. The server is Node 22 with SQLite. The web app is built with Vite. The ledger uses AG Grid.
PayPal: Orders v2 with intent=AUTHORIZE for the pledge, order authorize on return, then at TIME a capture of the exact amount with final_capture=true (which releases the rest of the hold), or a void if there were zero reps. Payouts v1 pays the cause, using the batch id as the idempotency key. A mock client with the same interface lets the whole app, tests and demo video run without keys.
Counting: MediaPipe Pose (tasks-vision 1.2.0, lite model) runs in the browser, with the wasm and model served from the repo. Per-exercise counters use joint angles with hysteresis and a minimum rep time. Reps go to the server, which bounds the count by the clock.
AI: Claude Haiku 5.5 for thank-you lines, the recap, and the three-frame spot-check, with a spend cap and fallbacks. Local Kokoro TTS made the voice clips. Claude Code did most of the building, with tests: 116 unit tests plus Playwright end-to-end runs, all muted.
Challenges we ran into
- Making the money rules hard to break. A donor can never be charged above their cap, a retry can never charge or pay twice, and the count cannot beat what a human can do in the time window. Those are server rules, not UI rules.
- Counting push-ups from a normal webcam. They need a side view, and a front-on view loses the arm angle. We added a torso gate, hysteresis and a minimum rep time, and kept "Tap to count" as a fallback.
- Recording an honest demo with no sound and no real athlete on hand. We fed a free stock push-up clip to Chromium as a fake webcam so the video shows the real counter and the real ledger, and we say so in the credits.
- Keeping AI in its lane. The model writes words and flags frames. It never decides who is charged.
Accomplishments that we're proud of
- The authorization really is the donor's cap. We did not find another pledge product that counts live and settles live.
- The full path works end to end: pledge, hold, live count, exact capture, release, payout.
- Everything runs in a browser. No app install, and video never leaves the device except three small spot-check frames.
- A judge can try it alone at a desk with a webcam and three push-ups.
What we learned
Authorize and capture is a better fit for "pay for what happened" than most people use it for. A hold works like a limit the donor sets. Final capture hands back the unused part for free. We also learned that a rep counter is mostly about rejecting bad frames, not about finding good ones, and that a short, honest "real vs simulated" list is worth more than a polished claim.
What's next
- Test the counters on more bodies, cameras and lighting, and add more exercises.
- Team and leaderboard events (a whole class or a Murph day).
- Recurring and corporate-match pledges.
- A real organizer onboarding with PayPal merchant accounts, so causes connect their own payout account.
Real vs simulated
Real: PayPal sandbox authorize, capture, void and Payouts; in-browser MediaPipe counting; Claude Haiku recap and spot-check. Simulated: the labeled demo bot donors, and the mock mode used to record the video (its stand-in athlete is a Pexels stock clip fed in as a fake webcam).
Built With
- ag-grid
- anthropic-claude
- claude-haiku
- kokoro-tts
- mediapipe
- node.js
- paypal
- paypal-orders-api
- paypal-payments-api
- paypal-payouts-api
- playwright
- sqlite
- typescript
- vite
