Crazy Thoughts: Would U Rather

I built a "Would You Rather" game for my kids

Building Crazy Thoughts: Would U Rather — an offline-first family party game in React Native.

Most kids' games I downloaded had the same problem. Ads between every screen. A subscription prompt before the first round. Something asking for an email address on a game meant for a seven-year-old. And almost all of them were built for one kid staring at one screen alone.

I wanted the opposite: something we could play together, in the car or at the kitchen table, that worked with no signal and asked nothing of us. So I built it.

What it is

Crazy Thoughts: Would U Rather is a family party game with over 1,000 hand-written dilemmas across themed decks — animals, food, superpowers, silly, gross-out, deep thinkers. You swipe through cards and pick a side.

Beyond the main deck there are a few modes that came directly from watching how my kids actually wanted to play:

  • Blitz Frenzy — ten questions, five seconds each. No overthinking. Answer fast enough and you get a lightning bonus.
  • Pass The Phone — a two-player telepathy game. Player one secretly picks, hands the phone over, and player two guesses what they chose. You get a "mind meld" score at the end. This one is the reason the app exists.
  • Daily Thought — one curated dilemma per day, 365 of them.
  • Family Thought Studio — write your own cards. Inside jokes and house rules end up in the deck.
  • 27 badges for streaks, speed records, and milestones.

Technical specs

Framework React Native 0.81 on Expo SDK 54, New Architecture enabled
Language TypeScript, strict
Animation Reanimated 4 + Gesture Handler; the swipe deck is a PanResponder card stack with rotation and drag-linked mascot reactions
Storage AsyncStorage — favorites, streaks, custom cards, badges, all on-device
Payments expo-iap talking straight to StoreKit 2 and Play Billing
Backend One Firestore REST endpoint. That's the whole server.
Art 43 custom kawaii sticker PNGs, no emoji rendered anywhere in the UI
Ads / analytics SDKs Zero

Decisions I'd defend

No billing middleware

The obvious move for in-app purchases is RevenueCat or similar. I went direct to StoreKit 2 and Play Billing through expo-iap instead. One non-consumable unlock does not justify a third-party service sitting between my users and Apple, holding purchase data and charging a cut of revenue. The tradeoff is that I own the edge cases — pending transactions, interrupted purchases, restore flows, family sharing — and I had to write all of them properly. That's a few hundred lines of code I understand completely, versus a dependency I'd have to trust.

The rule I held to: Pro access is only ever granted by a real native store transaction or a native entitlement query. Never by local state alone. A cached flag can survive a failed network call, but it can never be the thing that unlocks the app.

Write-only telemetry

The app collects one thing: an anonymous counter of which option was picked on which card. No accounts, no device identifiers, no advertising ID, no location.

The interesting part is that it's write-only. The percentages you see in the app are generated on-device — the app never reads that database back. Which means the Firestore security rules can deny reads entirely, and the data can't be scraped by anyone who pulls the API key out of the binary. Writes are constrained to increment-only: a counter can rise, by at most one, on three specific fields. Nothing else is reachable.

Writing that rule was worth the afternoon. The naive version — allow read, write: if true — is what most tutorials leave you with, and it means anyone can set your vote counts to whatever they like.

Offline first, genuinely

Every deck and every mode works with the phone in airplane mode. The vote sync is a background nicety that queues locally and retries later; if it never succeeds, nothing about the game degrades. This wasn't a purity exercise — it's because the app gets used in the back of a car with no signal, which is exactly where a game like this earns its keep.

Parental gates on every purchase path

Every route to the paywall goes through a math challenge first. It's a small thing, but the whole point was building something I'd hand to a kid without watching over their shoulder.

Things that were harder than expected

  • Sticker extraction. The art came as sheets, and cutting 43 individual stickers out of them left slivers of neighbouring art clipped into the edge of nearly every file. Fixing it meant flood-filling inward from the canvas edge and stopping at each sticker's white die-cut outline — deleting anything the flood reached, keeping anything floating fully inside. Naive "keep the biggest shape" approaches destroyed the intentional details: the thinking cat's question mark, the planet's orbiting stars, the shooting star's rainbow trail.
  • Tall screens. A fixed-height card stack that looked right on a 6.1" phone left a dead band of background on a 6.9" one. The fix was a layout module with one breakpoint and a card stage that scales with screen height — plus resisting the urge to just centre everything, which splits leftover space evenly and puts an awkward gap directly under the header.
  • Silent failures. fetch doesn't reject on HTTP 403 — it resolves normally. So when I tightened the Firestore rules, a rejected write looked exactly like a successful one, and the queued votes were cleared regardless. One missing response.ok check away from losing data forever and never knowing.

Where it's at

It's built, it runs on iPhone and iPad, and it's heading to the App Store. One-time purchase to unlock everything — no subscription, no ads, ever. That part isn't a marketing position, it's just what I wanted to exist for my own kids.

/projects/crazy-thoughts-would-u-rather austin.amento.dev