Upload a game
One HTML file that runs on a phone. It is checked automatically and, if nothing is wrong with it, goes into the feed straight away.
Writing it yourself? How to build a game has a working example to start from, the runtime reference and the safe areas.
Building it with an AI?
Paste this in first. It covers the safe areas, what the runtime gives you, and the things that simply do not work here.
Build a game for Arcade, a feed of small HTML games played on phones.
WHAT TO DELIVER
One self-contained .html file, under 2 MB. No build step, no separate files.
THE SCREEN
Portrait, phone sized. Read the real viewport and handle resize; never hard-code
dimensions.
The feed draws its own controls on top of your game. Keep anything interactive,
and anything the player has to read, out of these edges (CSS pixels):
top 44 — the score readout
right 68 — the like, report, liked-games and next-game buttons
bottom 104 — the game's title and author
Do not hard-code those numbers. Read Arcade.insets, which returns
{top, right, bottom, left}, or use the CSS variables --arcade-inset-top,
--arcade-inset-right, --arcade-inset-bottom and --arcade-inset-left. They can
change, so re-layout on Arcade.on('insets', fn).
THE RUNTIME
window.Arcade already exists when your code runs. Do not add a script tag for it.
Arcade.ready() — call once when playable; clears the loading screen
Arcade.score(n) — current score, shown in the chrome
Arcade.gameOver({score, won}) — a run ended
Arcade.progress(0..1) — optional
await Arcade.save(key, value) — persist a value
await Arcade.load(key) — read it back, or null
Arcade.haptic('light' | 'success') — vibrate
Arcade.on('pause', fn) — player left: stop timers, sound and animation
Arcade.on('resume', fn) — player came back
Arcade.on('insets', fn) — safe area changed, re-layout
Arcade.insets — {top, right, bottom, left}
Arcade.registerAudio(ctx) — lets an AudioContext be suspended on pause
Arcade.next() — ask the feed to move on
WHAT IS NOT AVAILABLE
The game runs in a sandboxed iframe with an opaque origin and no network, so
these throw or silently do nothing:
localStorage, sessionStorage, IndexedDB — use Arcade.save and Arcade.load
fetch, XMLHttpRequest, WebSocket — there is no network at all
cookies and service workers
<iframe>, <object>, <embed>
eval and new Function
alert, confirm, prompt, window.open — no-ops
An upload using any of these is rejected automatically, naming the line.
ASSETS
Everything inline. Draw artwork in code (canvas or CSS) rather than embedding
large images; a 2 MB budget disappears fast in base64.
Scripts may be loaded from: cdnjs.cloudflare.com, cdn.jsdelivr.net. Nothing else will load.
INPUT
Every gesture is yours: tap, hold, drag in any direction, multi-touch, tilt.
Nothing is reserved — players move between games using a button in the chrome,
not a swipe. Design for one thumb, and handle pointercancel.
WHAT MAKES A GOOD ONE
It has to be understandable in about three seconds and worth a minute. Start on
the first tap; no menus, no tutorials, no settings. One idea, done well.
QUALITY BAR
- A delta-time loop in milliseconds. Never assume a frame rate, and never mix
per-frame and per-millisecond units — that is the single most common way these
games end up unplayably fast or slow.
- Nothing still running after Arcade.on('pause'): no timers, no audio, no
animation frames doing work.
- Correct on a resize and after an orientation change.
- Include <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover,user-scalable=no">
- Call Arcade.ready() as soon as it is playable, or the feed shows a loading
screen over your game for eight seconds and then marks it broken.
Give me the complete file, ready to upload.What the checks look for
- One file, 2 MB at most, with everything inline.
- No network of any kind. Scripts may come from cdnjs.cloudflare.com or cdn.jsdelivr.net, and nothing else loads.
- No nested frames, no service workers, no cookies.
- Every gesture is yours — tap, hold, drag in any direction, multi-touch, tilt. Players move between games with a button, so nothing is reserved.
localStoragethrows. UseArcade.save()andArcade.load().