Inspiration
We were not trying to build a game. We were trying to build a reason for two people to sit down together.
Every game we grew up on gave everybody the same castle. You and the stranger across the world got identical bricks, and the only thing that was yours was how fast you cleared them. That is fine, and it is also why a game stops being something you share and becomes something you finish.
So the question we started from was: what if the stage were made out of you — your photograph, your answers, the small pop-culture furniture of your actual week, the things you would text a friend about — and what if the person you care about could walk into it?
Two ideas came out of that, and they are the whole product:
Personalisation that is actually personal. Not a name inserted into a template. The stage is generated from your own words and your own photo, so the thing you play is a place that could not have been made for anybody else. And because it is generated rather than authored, it does not have to be the same tomorrow — a new stage, carrying whatever you are into this week, is a generation away rather than a content update away.
Connected play as a way of showing someone your inside. Two players can pair with a link code and have their worlds forged into one shared place. That is the part we care about most. Handing someone a code is a small thing; letting them walk around inside a world grown from your own answers is not. We wanted a game that leaves two people knowing each other slightly better than before they played, which is a strange thing to ask of a platformer and exactly why it was worth trying.
A stage takes about ten minutes for a capable player to finish. That is on purpose. It is short enough to play in the gap in an evening, and short enough that "here, try mine" is a reasonable thing to say to somebody. Your earlier stages stay yours to go back to.
What it does
You describe what you want and hand over a photograph. The backend generates a playable game from that — art, world, lore, and the code itself.
Then it does the part we are proudest of: it plays the game before you do. The generated build is run in a real headless Chromium at a 390×844 portrait phone viewport, driven with real touch gestures through the Chrome DevTools Protocol. A vision model audits the frames it captured. A critic scores it against measured criteria. If it falls short, the code is changed and the whole loop runs again.
The loop stops itself for a reason it will name — the target was reached, no defects remain, it converged, or it detected itself oscillating — and reports the passes it actually ran rather than the passes it planned.
Alongside that, two players can mint and redeem a link code to pair their worlds, and an opt-in WhatsApp loop can ask a player questions and forge their stage out of the answers.
How we built it
- Google Agent Development Kit runs the pipeline as a genuine multi-agent system: a coordinator delegates to a coder, a QA agent and a critic, each choosing its own tools and looping on its own observations. Nine real tools, real hand-offs.
- Google GenAI SDK against Gemini 3.5 Flash for generation, the vision audit and play planning; Gemini 3.1 Flash Image for art; Veo 3.1 for cinematics.
- FastAPI on Cloud Run in
asia-south1, built and deployed by Cloud Build. Firestore for persistence, with a vector memory bank over Vertex AI embeddings. - Playwright-driven headless Chromium inside the container for the playtest. The emoji and CJK fonts are installed in the image deliberately — a game that renders tofu boxes on someone's screen is a defect the auditor should catch.
- An Android client in Kotlin and Compose, authenticating with RS256 end-user tokens. No API key, model key or shared secret is compiled into the app; anything in an APK is published.
Challenges we ran into
The hard problem was not generation. It was honesty under failure — and we lost that fight before we won it.
An earlier version of this backend narrated a "60-task dual-lead agent fleet" to its logs
and returned 60/60 TASKS COMPLETED, 100% CERTIFIED. We audited it line by line. The task
counter was incremented unconditionally. The quality report was a dictionary of hardcoded
strings. The certification could not fail. And on the run we captured, every single
model call had failed — the system substituted pre-written text and certified itself
anyway.
We rebuilt the pipeline around the opposite principle, and the guarantees are now properties of the tools rather than of the running order, so an autonomous agent cannot route around them:
- When every model call fails, the caller gets the real upstream error and no game. There is no substitute build. A test pins it.
- The scoring tool takes only a build identifier and looks the playtest up itself. There is no channel through which an agent can hand it a score.
- A build no browser ever ran comes back
UNVERIFIED, not a number. - Unmeasured values stay empty. Frame rate used to default to 60, which read as a measurement; that default is gone.
- Every generated artefact ships with its provenance — the model requested, the region, every failed asset, and the verbatim upstream error.
The second challenge was that none of it mattered if the game never arrived. A real run took over forty-five minutes and was then destroyed when the server recycled, leaving a record that claimed it was still working — a lie that would outlive the run for ever. We measured where the time went, cut the refinement default from twelve passes to three, added stops that end a run once it is genuinely good, and made a lost job report itself as honestly failed. The same game now generates in about ten minutes.
What we learned
A success message that cannot fail is not a success message. The tell is always structural: a status assigned as a literal instead of derived from a result; a counter incremented outside the branch that did the work; a check whose failing path is unreachable. We grep our own code for that shape now.
Give an agent autonomy and your invariants have to move into the tools. Once a model decides what happens next, anything guaranteed by running order is no longer guaranteed at all. Making the scoring tool physically incapable of accepting a score was worth more than any amount of instruction telling an agent to be honest.
And the honest version is the better demo. "It ran your game in a real browser, scored it 92, and named the one change it chose not to make" is a stronger sentence than "100% verified" — because a judge can check it.
What's next
Lifting the single-instance cap by moving four named subsystems to shared storage; a durable queue so a long generation survives anything; finishing the WhatsApp loop end to end once business verification clears; and the daily rhythm this was always aimed at — a new stage waiting for you tomorrow, made out of a different corner of your week.
Log in or sign up for Devpost to join the conversation.