Put your game in a city
What a city is
Ibadan Life and Osogbo Life are life games set on real streets. Every player carries a phone, and the phone has an arcade. Your game can be on it: players open it from the phone, play, and their scores go on the city’s board. The city plays it inside a locked frame, so a game for a city is built a little differently from one for the Arena54 Arcade.
- 01
Make one file
Build your game into a single HTML file with no network. The prompt below does it.
- 02
Register
In the dashboard, register the game and tick City arcade game.
- 03
Send
Under Cities, fill in the form, pick the file and icon, and send it for review.
- 04
Play
Once the city approves it, it is on every player’s phone.
Already in the Arcade? Offer it in one tick
If Arena54 has approved your game for the Arcade, you don’t need to make anything new. Any game that runs in the Arcade can go, 3D included, at any size.
- In the dashboard, open Games, then your game, and scroll to Offer this game to cities, just below Arcade.
- Check the name and the one line, the age, what it asks the city for, and the cities. Add an icon (40,000 bytes at most).
- Leave Send new builds to my cities ticked if each new Arcade build should go to them too, and press Offer to cities.
The city plays your approved build exactly as it is, from Arena54’s game host, pinned to that build: a later upload never changes what the city approved. Sign-in, saves and leaderboards keep working through Arena54, as in the Arcade. A new approved build goes to the city as a new version, and players keep the current one until the city approves it. Withdraw it under Cities, as for any city game.
The rest of this page is the other way in, for a game that isn’t in the Arcade: one HTML file, run with no network.
One file: what a city takes
- One HTML file, at most 200,000 characters, with all its scripts and styles inside it.
- No network at all. Nothing may load from another address: no scripts, stylesheets, web fonts or pictures by URL. Pictures go in as data: URLs; the device’s own fonts are fine.
- The review turns a file down on sight if any of these words appear anywhere in it, even in code that never runs:
fetch,XMLHttpRequest,WebSocket,EventSource,sendBeacon,importScripts,localStorage,sessionStorage,indexedDB,document.cookie, or a<form>. Libraries count too, so an SDK that calls the network has to be left out of the city build. - A screen about 360 px wide and 416 px tall, touch only. No pop-up boxes; sound only after the first tap.
- An icon: PNG, JPEG or WebP, at most 40,000 bytes.
- Unity and Godot web exports can’t be sent as they are: they are many files and megabytes. A game made with plain JavaScript or a bundler such as Vite can.
Scores and saves: the bridge
With no network, a game reaches the city through window.ibadan, which the phone adds before your code runs (the same name in every city). Every call returns a promise. Outside a city it doesn’t exist, so check for it and keep the game playable without it.
const city = window.ibadan ?? null;
const me = city ? await city.player() : null; // { name }
const sound = city ? await city.storage.get("sound") : null; // a small save per player, text only
if (city) await city.storage.set("sound", "on");
// At the end of a round: a whole number from 0 to 1,000,000,000, at most one every 20 seconds.
if (city) { const { best } = await city.score(4200); }Arena54’s own sign-in, saves and leaderboards need the network, so they are switched off in a city build: scores go to the city’s board through score instead. Tick Report a score (and Keep a small save per player if you use storage) when you send the game.
1. Make the city file
The easy way: open your game’s project in an AI assistant that can work with files (Claude Code, Cursor or Codex), paste the prompt at the bottom of this page, and let it work. It adds a separate city build without touching your normal one, and ends with city/app.html, city/icon.png, and a test page that plays the file in the same locked frame.
What it does, if you’d rather do it yourself (this is how Pikin Owambe Kitchen went in, at 72,621 characters):
- Add a
citybuild mode (for Vite,vite build --mode city) that swaps every module using the network or browser storage for a small stand-in using the bridge. A game whose server checked scores can replay the shift on the phone instead and send the result withibadan.score. - Leave web fonts out of the city build, and turn off Vite’s modulepreload polyfill (it contains the word fetch).
- After building, inline the one stylesheet and the one script into the HTML and save it as
city/app.html. Count its characters and search it for the words above. - Open it inside an
<iframe sandbox="allow-scripts">with a Content-Security-Policy that blocks every connection and a stand-inwindow.ibadan, and play a round to the end with no errors.
2. Send it
- Open the dashboard and sign in. Under Games, register the game (or open it) and tick City arcade game.
- Open Cities. Pick the game, choose Arcade game, an age rating, a name (30 characters at most) and one line about it (80 at most).
- Under What it asks the city for, tick Report a score, and Keep a small save per player if it saves anything.
- Tick the cities, pick
city/app.htmlas the file andcity/icon.pngas the icon, and press Send for review.
After you send it
A person at the city plays it and approves or declines it, with a reason; you see the answer under Cities in the dashboard. To update the game, send the new file the same way: players keep the version they have until the new one is approved.
If something goes wrong
- “The file is … characters”: leave out web fonts and big pictures, and make sure only one copy of each library is in the build.
- A refused word is found: search the built file for it to see whose code it is. If it belongs to a library, that library needs the network or storage: swap it for a stand-in in the city build.
- The score doesn’t arrive: the score must be a whole number, the Report a score permission must be ticked, and only one score is taken every 20 seconds.