I'm Abdulbasit, 19, from Rawalpindi. For the last month, I've been building Paceball, an Android app that measures how fast a cricket ball is bowled using nothing but a phone video. No radar, no sensors, no hardware. You film a delivery side-on, mark a known length in the frame (usually the two sets of stumps, 20.12 m apart), mark where the ball left the hand and where it bounced, and the app works out the speed from the distance and the frame count.
It's my first mobile app. I'm building it with my partner Mustafa, the lead developer after me, for RevenueCat's Shipaton 2026, and this is the story of the second half, the part where the maths was mostly done and everything else tried to break.
Early on, a real test at the nets went wrong in a quiet way. The ball was invisible at the bounce; I guessed where it landed, and the app happily gave me a clean, believable number.
That number was fiction. And the worst part was that it looked exactly like a real one.
So Paceball got a rule that everything else now bends around: if the bounce wasn't actually seen, there is no speed. Not a zero, not a dash in a big font, not a greyed-out guess. The app says "No speed measured", keeps the video and the marks, and moves on.
The second rule came from the same place. A speed is never shown without its error range. Every reading carries a range built from the frame timing, the length of the reference, and how precisely each point was tapped. A delivery isn't "128.4 km/h". It's 128.4 km/h plus or minus whatever the footage can actually support. If you can't show the range, you can't show the number.
Those two rules sound small. They ended up deciding the design, the paywall, the share card and even the animations.
Shipaton is a RevenueCat hackathon, so purchases had to be real. The setup itself was straightforward: one entitlement called pro, one offering, a monthly plan at $3.99 and an annual plan at $24.99 with a 7-day free trial on the annual only. Free users get 3 analyses a week, anchored to the day of their first ever analysis, shared across every player profile on the phone.
The paywall was more interesting than the plumbing. I watched a RevenueCat livestream with their paywall designer, and a few of her rules went straight into the app:
Then came the lesson I didn't expect. Purchases kept failing with ITEM_UNAVAILABLE. The code was fine. The problem was that I was testing on an APK I'd installed by hand. Google Play billing only works properly when the app was installed from Play itself. Once I tested on a build from the internal track, buy, restore, cancel, expire and re-buy all worked.
The same mistake cost me more elsewhere. I'd sent our closed testers a direct APK link instead of the Play opt-in link. Their usage never counted, so when I applied for production access, Google declined it and asked for another 14 days of closed testing. That pushed the public release past the Shipaton deadline and narrowed us down to one category, the Next Gen award for student builders. If you're doing closed testing for the first time: testers have to install from Play, through the opt-in link, or it doesn't count.
For a while, speeds on some clips were slightly off, in a way that was hard to pin down. The cause turned out to be almost funny.
To get the frame rate, the app was dividing the number of frames by the duration of the video file. But the file's duration includes every track, and the audio track often runs a little longer than the video. So the "duration" was too long, the frame rate came out too low, and every speed calculated from it drifted.
The fix was to read the duration of the video track alone, in the native Kotlin frame extractor, instead of the container. It's a one-line idea that took a lot longer to find than to fix, and it's the kind of error that would never show up in a demo but would quietly make every reading a bit wrong.
Before the design phase, I ran a full audit of the codebase with Claude Code, which has been doing most of the actual typing on this project while I review, decide and test. It came back with 21 commits of fixes and the test suite at 245 passing tests. A few of the findings were genuinely scary in hindsight:
None of these would have shown up in normal testing. All of them would have shown up to a judge.
The design phase started with a brief from ChatGPT and a mockup sheet, which I then checked line by line against the actual repo. About a third of it had to go. It rewrote the measurement maths, changed how the free allowance worked, and invented features I'd explicitly ruled out. Good design ideas, wrong app.
What survived is a calm, flat, black-and-lime look where numbers are the hero and lime only appears when it means something. The part I care about most is the reveal on the Result screen. The number counts up from zero to the measured speed and never overshoots, not even for a single frame, because an overshoot would briefly show a speed faster than what was measured. The range is on screen from the first frame, so the number is never alone. When the count lands, a small set of stumps locks into place with a bail dropping, and that's the moment the reading turns lime.
The line drawn between release and bounce is straight on purpose, labelled "Marked, not tracked". A curve would look nicer. It would also draw a flight path nobody measured.
Even here the honesty rule found bugs. On my phone the counter reached the speed and then, a second later, dropped back to zero. A number that shows up and disappears is its own kind of dishonest, so that's the first fix on the list, along with turning the reveal into a proper speedometer.
With four days to go, I hit the free monthly build limit on Expo's cloud service. The reset date was after the deadline.
Instead of paying, I moved the builds to Codemagic, running Expo's local build command on their free machines. It still pulls the signing key and settings from Expo, so the app is signed exactly like before and Play accepts it as an update. It took a few tries:
Small things, but each one would have been a disaster the night before submission instead of four days out.
Paceball is in closed testing on Google Play right now, and the code is public under the MIT licence here.
If you bowl, or coach someone who does, I'd love for you to try it and tell me where the numbers feel wrong.