Inspiration - An idle game is usually a spreadsheet wearing a costume. Progress is a number with more digits, the fiction is a skin over a menu, and the menu is where you actually spend your time. That is the genre's own complaint about itself.

I wanted the spreadsheet to live inside the fiction instead — so progress is legible at a glance, because the room is fuller and the furniture is nicer. It is for people who like the cadence of an idle game (check in, spend, forget it for twelve hours) and would like something to look at while they do it.

What it does - ou inherit a two-room detective agency in a rained-on river town and turn it into a national outfit. Cases resolve while you watch, and they keep resolving while you are away. Every fee goes back into the office, and the office is the progress bar.

There is no upgrade list. You buy equipment by tapping the equipment, and it visibly improves. A battered pine desk becomes a leather-topped partner's desk. A cheap kettle becomes a bean-to-cup tower. Thirty-two objects, four hand-drawn tiers each, across four rooms you unlock in turn — a bullpen, a records room, an interview suite and a forensics lab.

What makes it more than a number going up:

  • Ten detectives across four specialties, and the specialties compete. Each owns a different term of the economy — the equipment trickle, the value of a dropped coin, interrogation payouts, or everything at a lower rate — and a trait's strength is that specialty's share of your working roster. No agency can have all four, so "who do I hire next" is a real question with a different right answer depending on how you play.
  • Retirement moves you to a bigger city, and the cities are not a reskin. Each releases its equipment in a different order, so what is worth buying next genuinely changes. Measured rather than asserted: against the city before it, city 2 differs in 83% of its purchase order, city 3 in 73%, city 4 in 41%.
  • Case Crossroads. Every so often you get a real two-folder decision on the evidence board. No right answer is labelled anywhere, both folders are thin on information, and the consequence lands slowly, days later, as a story beat. The road not taken stays on screen.
  • An interrogation mini-game with no fail state. You link evidence threads against a clock, every correct link banks immediately, and walking away early keeps what you earned.
  • Twelve hours of offline earnings at the full rate, with no decay curve. Most idle games taper it. I measured what mine was actually worth and left it alone.
  • Twenty-four short story cases, authored rather than generated, plus a most-wanted board that fills as the agency works, daily quests, and achievements that pay a permanent income bonus.
  • Thirteen languages at launch — English, Spanish, Portuguese, Indonesian, Arabic (full RTL), German, French, Russian, Turkish, Hindi, Japanese, Korean and Simplified Chinese.
  • No account, no sign-in. Your agency lives on your device. Cloud save is optional.

How it makes money. Six products through RevenueCat, and no subscription — a game whose promise is that you can forget it for twelve hours has no business asking for a monthly commitment. Night Shift (one-time) makes the away rate whole permanently; an away agency earns half what an open one does, and offline is 84–95% of a real player's income, so this is the most valuable thing the game can sell. Senior Partner (one-time) removes interstitials and raises the offline cap. A starter bundle, once per player, pays in seconds of the player's own income rather than a flat sum — a fixed amount is generous in city 1 and a rounding error by city 2. Then three consumable Leads packs feeding four existing sinks.

Two decisions a judge might otherwise read as oversights. Neither permanent unlock removes the rewarded ad — that ad pays the offline window a second time, and offline is most of the economy, so selling its removal would take away the player's single best lever and call that a premium feature. What is sold is not having to watch one. And the Leads packs required capping an income multiplier first: my own code said that multiplier was unbounded, and safe only because Leads accrued on a wall clock that no amount of money could accelerate. Selling Leads repeals that premise. I bounded it at 2.0x and re-ran the economy simulator before shipping a single pack, rather than after.

How I built it - A solo build — one person on design, code, economy and art pipeline.

Flutter and Dart, with no game engine. The isometric office is a custom renderer: its own projection, a per-room camera, painter's-order z-sorting, and a mount system that puts a lamp on a desk or hangs a clock on a wall without changing how it sorts or hit-tests. The rug is the interesting case, because it is the only object that changes z-order rather than height — it anchors at the tile's back edge so a sofa standing on it sorts in front. Each of the fifteen room backgrounds was measured by a calibration tool that finds where that painting's floor actually is, because the generator's angle wanders between 1.50:1 and 2.10:1 against a nominal 2:1.

The simulation layer is presentation-agnostic pure Dart with no Flutter imports, which is what lets the economy be tested and simulated headlessly. Currency is a custom BigNumber (mantissa and exponent), never a double, because an idle game outruns 64-bit floats early. Saves are a single AES-GCM-encrypted blob — versioned behind a migration ladder, checksummed, and written to a rolling primary/backup pair so a corrupt primary restores silently — with the key in the platform keystore.

The UI is drawn, not themed. Buttons, pills and progress bars are nine-sliced art rather than Material widgets with a colour override, because Flutter's ButtonStyle cannot take an image. Every object sprite and character still was cut from a magenta backdrop through a custom chroma-key pipeline that recovers the matte exactly rather than approximately, then trimmed, scaled per size class, and shipped as WebP with a bit-exact alpha channel. Objects cast their shadows in code rather than in the PNG, so the shadow tracks the idle bob.

Services: RevenueCat (purchases_flutter) for all six products; Firebase Analytics, Crashlytics, Remote Config and Auth; Cloud Firestore for optional cloud save; OneSignal for the offline-earnings push; AdMob for rewarded and interstitial ads; Riverpod for state; Codemagic for iOS delivery.

Challenges I ran into - A save field that was written, parsed, and never read back. retainerLevel shipped fully serialised — and never assigned on load. Every retainer bought with Leads silently reset to zero on the next cold start, while the persistence service's own round-trip test stayed green, because the test never went through the notifier. The rule that came out of it: a field is only persisted once a test has reloaded it through the real load path.

A green test suite says nothing about whether the app builds. Every file under test/ is a widget test or a golden, and none of them invoke Gradle — so an Android build break was invisible to all of them. The project went two sessions without compiling. CI now runs flutter build apk --debug on every push as a third job.

A cloud restore that landed on disk while the player carried on playing the save it replaced. The restore signalled the agency by bumping a counter provider — except the counter was a constant Provider<int>, so Riverpod correctly declined to notify anyone that 0 had become 0.

Deciding which save wins. That had to become a rule, not an if at a call site. It orders by progress, and the device clock is deliberately not in the comparison at all, because that clock is known to move. A cloud save ahead of a non-pristine device is always a question put to the player, never resolved silently.

Keeping the frame rate on a screen with nothing moving on it. Every audio player is built with its position updater disabled; the default asks the engine for a frame on every frame for as long as anything is playing, which kept the app rendering at full rate while idle.

Accomplishments that I'm proud of - Not a feature: the economy has a simulator. tool/economy_sim.dart plays the entire game as six bot archetypes and asserts six pacing gates on every change — first upgrade inside 8 seconds, first room inside 4 minutes, first retirement in 6 to 8 hours, no specialty more than 15% ahead of the others, at least two distinct viable builds across archetypes, and funding a case never being faster than spending it.

It is why every balance claim above is a number rather than an adjective. And it caught things that playing never would: that 84–94% of a city's money is earned with the app closed, and that one build was quietly being paid 67% of its own offline rate for every hour the app was shut.

I am also proud that it holds up in thirteen languages including Arabic. The chrome mirrors for RTL; the isometric scene deliberately does not, because the whole world is lit from the upper left and mirroring it would be a bug rather than a translation.

What I learned - The most valuable thing an idle game can sell is the part the player never sees. I only knew offline was 84–95% of income because I simulated it — and that single number reshaped the entire product lineup, because it meant the away-earnings unlock, not a cosmetic or a flat coin pack, was the honest premium offer.

Build the measuring instrument before the thing you plan to tune. The simulator paid for itself the first time it contradicted me.

Write the reasoning down at the call site, every time. It is why a session six weeks later can change the economy without re-deriving it — and why a bug like an offline path silently paying 67% of its own rate was findable at all.

What's next for Riverside Files - Idle Tycoon - More cities, more authored story cases, and the iOS release — 1.0.1 is in App Store Connect now. Beyond that, the simulator makes a real live-ops cadence practical: any balance change can be re-validated against all six archetypes and six pacing gates before it ever reaches a player, so tuning after launch does not have to be guesswork.

Awards I am entering: Grand Prize, Best Game, the RevenueCat Design Award, the HAMM Award, and OneSignal's Keep Them Coming Back.

Built With

  • admob
  • aes-gcm
  • android
  • cloud-firestore
  • codemagic
  • custom-renderer
  • dart
  • firebase
  • firebase-analytics
  • firebase-auth
  • firebase-crashlytics
  • firebase-remote-config
  • flutter
  • google-play
  • google-play-billing
  • idle-games
  • in-app-purchases
  • ios
  • localization
  • onesignal
  • push-notifications
  • revenuecat
  • riverpod
  • webp
Share this project:

Updates

Submission history