Ship an HTML5 game as an installable web app
Games are the best case for installable web apps: a finite set of assets that rarely change, a strong reason to launch from an icon, and players who expect fullscreen. Here is the manifest that does it, the caching mode to pick, and the engine-specific gotchas.
Manifest settings that matter for games
The builder's defaults suit content sites. For a game, edit these in the generated manifest.json:
{
"display": "fullscreen",
"orientation": "landscape",
"background_color": "#000000",
"theme_color": "#000000",
"start_url": "/index.html?source=pwa",
"categories": ["games"]
}display: fullscreenhides the status bar as well as browser UI. Browsers that don't support it fall back tostandalone.orientationlocks the installed app. Uselandscapeorportrait, notany, unless the game truly supports both. Safari on iOS ignores this field — handle rotation in-game there.- Black background/theme makes the splash-to-canvas transition invisible for most games.
- The
?source=pwaquery lets your analytics distinguish installed players from browser players.
Caching: choose Aggressive, then be deliberate
Pick Aggressive in the builder. Then open the generated sw.js and add your asset manifest to the precache list — sprites, audio, fonts, level data, the engine bundle — so the game is fully playable offline after the first load, not only the pages the player happened to visit. Two constraints:
- Storage quota. Browsers grant a generous but finite cache. Games under ~50 MB precache comfortably; above that, precache the first levels and cache the rest on demand.
- Versioning. When you ship a new build, change the cache name in
sw.js(the generated file has aCACHE_VERSIONconstant). Old caches are deleted on activation; players get the new build on the next launch.
The offline-first guide goes deeper on what to precache vs cache on demand.
Engine notes
| Engine | Watch out for |
|---|---|
| Phaser / PixiJS | Nothing special. Add the asset list from your loader to the precache array. |
| Construct 3 | Construct exports its own service worker and manifest. Use theirs, or replace both with the builder's — never run two. |
| Godot 4 HTML5 | Large .wasm and .pck files; precache them explicitly. Requires Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers for threads — set them at your host, the service worker cannot add them. |
| Unity WebGL | Very large builds; use Brotli compression and cache on demand. iOS memory limits are the real constraint, not the PWA. |
| Plain canvas / WebGL | Ideal. Keep index.html small so the shell caches instantly. |
Audio, fullscreen and input
- Audio unlock. Browsers block sound until a user gesture. Start your audio context on the first tap — a "Tap to play" screen — and it works in the installed app too.
- Fullscreen on iOS comes from
display: fullscreenplus the meta tag<meta name="apple-mobile-web-app-capable" content="yes">. The home indicator remains. - Touch. Add
touch-action: noneon the canvas to stop the browser interpreting swipes as scroll or back gestures. - Save state in
localStorageor IndexedDB. It persists across app launches and survives cache purges better than the cache itself.
Publishing to Google Play
With the PWA in place, the Android option in the builder gives you a TWA test APK and assetlinks.json; build the AAB with Bubblewrap or PWABuilder from the same manifest, or use the app builder's APK + AAB bundle (US$5) for a WebView-based listing. Games on Play must declare an IARC content rating, and if you show ads or sell items the corresponding policies apply — in-game purchases of virtual goods must use Play Billing (Digital Goods API in a TWA). The checklist walks through each form. Screenshots should be taken from the installed fullscreen game, in the locked orientation.
Frequently asked questions
Does the game work offline immediately after install?
Only if the assets are precached. Aggressive mode caches what the player has loaded; add your full asset list to the precache array in sw.js for guaranteed offline play.
Can I lock landscape on iPhone?
Not through the manifest — Safari ignores orientation. Detect portrait in-game and show a "rotate your device" overlay.
How big can the game be?
Tens of megabytes precache fine on modern devices. Beyond that, precache the core and cache additional levels on demand; browsers may evict caches under storage pressure, so persist saves separately.
Ads in an installed web game?
Web ad networks work in the installed app as in the browser. If you package for Google Play and use AdMob in the shell, don't also run web ads on the same screens.