-
-
Live on Google Play
-
Share from any app; it identifies the game, even offline
-
Pull the lever: three cards from your own pile
-
Every ad is opted into, and verified by RevenueCat
-
Roll the credits, then rank head to head
-
Follow a friend's pile, no accounts
-
Offline-first, no secrets in the app
-
1024x1024 app icon
-
screenshots with no device frame
-
-
-
-
Live on Google Play · 1-minute-54 demo above · open source (MIT)
Inspiration
Every gamer I know has a pile of shame: games bought on sale, gifted, recommended by a friend, and never finished. Every backlog app I tried was a spreadsheet with box art, and a spreadsheet of unplayed games is a guilt machine, so people stop opening it.
The fix wasn't a better list. A backlog is a decision problem, not a storage problem. And the arcade already solved the emotional side of it: in an arcade, CONTINUE? was never failure. It was a second chance, a countdown and a coin. So I built the backlog as a cabinet.
What it does
- Save from anywhere. Share a YouTube or TikTok link, a message, or a screenshot to CONTINUE? and a sheet slides up over the app you're in with the game already identified. (How that works is below; it's the hardest thing in the app.)
- Organize, honestly. A time budget tells you the truth, 1,167 hours, 29 games, finished by 2030, based on the hours a week you actually play (set it and the year moves) and recalculated live as you filter. One set of filters works across the whole app, NOW PLAYING is capped at three because focus is a feature, and a stats screen breaks your pile down by genre, length and how varied your taste is.
- Decide. Set the dials (time, mood, genre) and pull the lever. It deals three cards from your own pile and says why each one fits tonight.
- Complete. Clearing a game rolls the credits: your own stats scroll over the key art.
- Rate, head to head. No stars. "Which did you enjoy more?" binary-searches each game into a personal all-time ranking.
- Share. Your HIGH SCORES as an arcade table, and your whole pile as a link a friend can follow, with games you have in common marked.
The part the video can't show: the share-matching engine
A shared link or caption is never a clean title. "Elden Ring Shadow of the Erdtree is brutal" is a sentence, not a name, and IGDB's search is near-exact: Palworld finds the game, Pocketpair Palworld finds nothing.
- Links become titles. YouTube and TikTok links are expanded on my backend to the video's title and channel.
- Titles become candidates. A tokenizer builds an ordered shortlist of substrings of at most six words. It drops caption filler ("official trailer", "gameplay", "reaction") and stopwords, keeps connectors that live inside titles ("of", "the"), and folds sequel numerals (III ↔ 3).
- Candidates are matched on the phone. I built a pipeline over IGDB's data dumps that ships a 17,095-game index with alternative names (0.58 MB) inside the app, so "BG3" and regional titles resolve, and matching is a dictionary lookup that works with no network at all. Screenshots are read with on-device OCR, with no photo permission.
- Every match is scored against the original text. Confident matches add in one tap; ambiguous ones show a short chooser; hopeless ones open a blank field with an honest explanation instead of guessing wrong.
It went from "Elden Ring … is brutal" matching the wrong game at 0.29 confidence to the right game at 0.90. The same ranking code exists in TypeScript on the backend and in Kotlin on the phone, tested against the same fixture captions so the two paths can never disagree.
How I built it
Kotlin and Jetpack Compose, with Hilt, Room, DataStore, Retrofit and Coil. Offline-first: Room is the source of truth and the UI only reads from it, so the pile, DRAW, and matching shared text and screenshots all work on a plane. A Cloudflare Worker (TypeScript, KV cache) is the only thing that talks to IGDB. RevenueCat runs the whole revenue stack: Pro entitlements, the COIN virtual currency, and rewarded-ad verification.
Every sound and all three music packs are synthesised from code (oscillators and seeded noise, no samples) and released as CC0. Even the demo video was built in code: Remotion for the motion graphics, and a score composed from the app's own instruments. I built it solo, pair-programming with Claude Code.
Security and privacy on a public repo
The repository is public and the backend has no user accounts, so nothing secret can ship in the app. I wrote a threat model before going public:
- Keys live only on the Worker. The APK carries only values that are public by design. I scanned the full git history for secrets before publishing.
- Abuse is bounded. Three rate limiters guard the backend: 120 req/min per IP, 20/min on the expensive resolve route, and a 600/min global cap kept under IGDB's own limit so I throttle myself before IGDB bans me. I also added input caps, removed a wildcard CORS header, capped request fan-out, and made errors stop leaking internals. Each control was proven with live traffic, not trusted from config.
- I found and fixed an SSRF. The link resolver checked hosts with a substring match, so
tiktok.com.attacker.examplewould have passed. - FRIENDS needs no accounts and stores nothing on my server. The pile travels in the link's URL fragment, which browsers never send. Each phone signs its links with its own ECDSA P-256 key, so nobody can forge an update to "your" pile on a friend's phone. The decoder is strict and bounded, with 20+ hostile-input tests.
- Three permissions, total: internet, network state, vibrate. No photos, contacts or location, and no account to create.
- Consent done properly. A real UMP consent flow for EEA/UK users, rather than excluding a third of the developed world from the store.
Challenges I ran into
- Google Play's gate for new developer accounts: 12 testers, opted in for 14 consecutive days, before production access. I recruited them, ran four feedback rounds (~21 reports), and shipped four builds during the test, one answering each round.
- Config that looks applied but isn't. My first rate-limiter deploy silently did nothing: 150 of 150 test requests got through, because an older tool version ignored the setting without a warning. A deprecated IGDB field returned empty instead of an error. Since then I verify with traffic, never with the config file.
- A tester found a HAPTICS toggle wired to nothing. I audited every control and found two more. Now each setting is enforced where the capability lives, not where it's called.
Accomplishments I'm proud of
- It's live on Google Play in every country, and closed-test testers completed real (sandbox) purchases of both Pro tiers.
- Ads verified by RevenueCat: a rewarded ad can grant a coin, or a full hour of the real
proentitlement. - 213 app unit tests and 75 Worker tests, plus a migration test that fails the build if a database change would crash upgrading users.
What I learned
A green health check is not working software. Assert on real content. The free tier's 10 ms CPU limit, not taste, decided where matching runs. And users forgive a small app almost anything except a control that does nothing.
What's next
I want CONTINUE? to be where every game lives between "that looks good" and "credits rolled".
- The pile keeps itself up to date. Steam import first, then console libraries where the platforms allow it, with playtime sync so games move to NOW PLAYING and CLEARED on their own.
- Every platform from one codebase. It's all Kotlin, so Compose Multiplatform can take most of it to iOS and desktop.
- Curated piles. Friends' piles are only the start. The same signed-link format can carry a critic's or a community's "finish these before 2027" list, with the games you already own marked.
- Short links on a proper domain. A pile link is long because it carries the pile, which is exactly why nothing is stored on my server. Short, branded links would mean storing piles, so they'll be opt-in, and the private long link stays the default.
- A year in games. An end-of-year recap card built from your clears and rankings.
- Hardening for scale. Play Integrity attestation on the backend, and a server-side coin ledger.
Game data freely provided by IGDB.com.
Log in or sign up for Devpost to join the conversation.