Inspiration

A doom-scroll-named game that does the opposite of what its name implies: after ten minutes of continuous play it asks, once, whether the player wants to call it a day — a dismissible, non-forcing nudge, not a lockout, and gameplay never pauses for it.

What it does

A one-tap endless runner with a mid-air double jump, two obstacle types (boxes, spikes) and a continuous speed ramp tied to distance. Three modes: Classic (endless, one hit ends it), Daily (the same seeded obstacle course as every player, today only, via a deterministic PRNG), and Zen (no game over). A single opt-in rewarded ad ("Mola" — take a break) is the only ad format in the app: never a banner, never an interstitial, never a "continue" offer. Watching it earns a hint (capped at 3/day) and never revives a run or grants a life. One non-consumable purchase, "Ad-free + destek," removes the ad offer and grants the 3 daily hints automatically. Before a session the player can set an intention — a number of runs or minutes — and when it is reached the game says "that's it for today" and means it: an honest stop screen with no "one more run" hook, no countdown, no loss language. Run Complete shows the distance against the record as a plain difference, Daily shows yesterday next to today, and Zen is one tap away. Records and intentions stay on-device; no account. Nineteen languages in the in-app picker, 17 of them passed a two-reviewer translation check and the full set passed an independent audit.

How we built it

Expo SDK ~54.0.33, React Native 0.81.5, TypeScript, Expo Router, react-native-reanimated + react-native-svg for the render loop, frame-rate-independent physics (every step scaled by dtMs / REF_FRAME_MS, clamped so a backgrounded app can never tunnel an obstacle through the player). RevenueCat (react-native-purchases ^9.14.0): entitlement premium, offering doomscroll_default, product doomscroll_adfree (a single lifetime package, no first-package fallback if that exact package is missing — a deliberate refusal to sell the wrong SKU). Advertising: react-native-google-mobile-ads ^16.3.0, wired only to the opt-in rewarded unit; a static gate greps the codebase for BannerAd/InterstitialAd and fails the build if either appears. Sentry crash reporting is bundled but not yet active.

Challenges

Keeping the "one ad format, opt-in only" claim true under refactors needed more than a code review — it needed a negative-controlled static gate (scripts/verify-monetization-safety.mjs) that greps the actual src/+app/ tree for banner/interstitial ad components on every run, because a claim in a listing file drifts from the code the moment nobody is watching it. The session nudge had a similar trap: the play-time clock had to be held across runs and count only simulated frames, or a backgrounded app would either never trigger the nudge or trigger it too early.

Accomplishments

A monetization model with zero dark patterns by construction, not by promise — the "no banner, no interstitial" claim is a CI gate, not a paragraph; an endless runner that asks you how much you want to play and then stops when you said so; a Daily mode whose determinism (same seed, same course, for everyone) is proven by test rather than assumed; and an accessibility pass (44dp touch targets, ≥4.5:1 contrast, labelled controls) checked at the source level for every screen and every theme token.

What we learned

A privacy or monetization claim that lives only in a listing document is a claim that silently stops being true; treating "STORE_TRUTH.md" as a live, re-run artifact against the shipped code — rather than a one-time write-up — is what caught the earlier v1 "no ads" copy after the v1.1 rewarded-ad feature landed.

What's next

No iOS release yet: the AdMob iOS app ID placeholder was removed until a real one is set, and the EAS iOS submit profile is pending the same. The render architecture (currently SVG + Reanimated) has an accepted, deferred rewrite to Skia/SharedValue rendering, tracked as technical debt rather than shipped now.

Built With

  • admob
  • expo-router
  • expo.io
  • github-actions
  • jest
  • react-native
  • react-native-google-mobile-ads
  • react-native-purchases
  • react-native-reanimated
  • react-native-svg
  • revenuecat
  • sentry
  • typescript
Share this project:

Updates

Submission history