Turning a Web Game into a PWA

Updated June 2026
Converting an existing web game into a Progressive Web App requires adding a manifest file, writing a service worker, and linking both from your game's HTML. The process takes an afternoon for a basic implementation, and the result is a game that installs to the home screen, loads instantly from cache, and plays offline without any app store involvement.

This guide assumes you already have a working browser game built with HTML, CSS, and JavaScript, whether it uses Canvas, WebGL, Phaser, Three.js, or plain DOM rendering. The PWA conversion does not change your game code; it adds a layer around it that handles caching, installation, and offline support.

Step 1: Serve Over HTTPS

Service workers only register on secure origins. If your game is already hosted with a provider like Cloudflare, Netlify, Vercel, or AWS CloudFront, you likely have HTTPS already. If you are self-hosting, obtain a free certificate from Let's Encrypt and configure your web server to redirect HTTP traffic to HTTPS.

For local development, browsers treat localhost as a secure origin, so you can test service workers without a certificate on your development machine. Use a local server like npx serve or Python's http.server module rather than opening HTML files directly, since the file:// protocol does not support service workers.

Step 2: Create the Web App Manifest

Create a file called manifest.json in your game's root directory. This file tells browsers how to display the game when installed. Here is a practical manifest for a game:

The name field is the full title shown during installation and in the app list. short_name is the label under the home screen icon, keep it under 12 characters. Set display to "fullscreen" for action games that need every pixel, or "standalone" for casual games where seeing the clock and battery in the status bar is acceptable.

The orientation field locks the screen rotation. Use "landscape" for games designed for wide screens, "portrait" for vertical gameplay, or "any" to let the player choose. The start_url should point to your game's entry point, and scope defines which URLs belong to the app so external links open in the default browser.

Icons need to be provided at minimum 192x192 and 512x512 pixel sizes. The 192px icon appears on the home screen, while the 512px version is used for splash screens and app listings. Both should be PNG format with a transparent or solid background that matches your background_color value. Use a tool like PWA Asset Generator to create all required sizes from a single high-resolution source image.

Step 3: Add Manifest and Meta Tags to HTML

In the <head> of your game's HTML file, add a link to the manifest. You also need Apple-specific meta tags because Safari on iOS does not fully read the manifest file for all display properties.

The link rel="manifest" tag points browsers to your manifest file. The apple-mobile-web-app-capable meta tag tells Safari to run in standalone mode when added to the home screen. The apple-mobile-web-app-status-bar-style controls the status bar appearance (use black-translucent for games to maximize screen area). The apple-touch-icon provides the home screen icon on iOS since Safari ignores the manifest's icon definitions.

The theme-color meta tag sets the browser toolbar color on Android before installation and the title bar color for desktop PWAs. Choose a color that matches your game's visual identity.

Step 4: Create the Service Worker

The service worker is the core of the PWA. Create a file called sw.js in your game's root directory (it must be at the root or scope level to control all game URLs). The service worker handles three lifecycle events: install, activate, and fetch.

During the install event, the service worker opens a cache and downloads all the assets your game needs for offline play. List every file the game requires: the HTML page, CSS stylesheets, JavaScript files, sprite sheets, audio files, font files, and any JSON data. Be thorough, a missing file in the cache list means a broken experience offline. Use self.skipWaiting() to activate the new service worker immediately rather than waiting for all tabs to close.

During the activate event, clean up old caches from previous versions. Use a versioned cache name like "game-cache-v2" and delete any cache whose name does not match the current version. Call clients.claim() to take control of all open pages immediately.

During the fetch event, intercept network requests and serve cached responses. The cache-first strategy works best for games: check the cache first, and only go to the network if the asset is not cached. This gives instant loading from cache while still allowing new assets to be fetched on first encounter. For API calls (leaderboards, multiplayer), use a network-first strategy that falls back to cached data when offline.

Version the cache name every time you update game assets. When the service worker file changes (even by one byte), the browser installs the new version. The new install event caches updated assets under the new cache name, and the activate event deletes the old cache, ensuring players get the latest version on their next visit.

Step 5: Register the Service Worker

In your game's main JavaScript file or in a script tag in the HTML, add the registration code. Check that the browser supports service workers first with if ('serviceWorker' in navigator), then call navigator.serviceWorker.register('/sw.js'). The registration returns a promise that resolves when the service worker is installed.

Handle the registration promise to track success or failure. Log the scope of the registered service worker to confirm it covers all your game URLs. If registration fails, the game still works normally, it just will not have offline support or installation capability, so do not let registration errors block gameplay.

You can also listen for the controllerchange event on navigator.serviceWorker to detect when a new service worker takes over. This is useful for showing a "Game updated, refresh to get the latest version" message to the player without interrupting their current session.

Step 6: Test Offline and Installation

Open Chrome DevTools and go to the Application panel. Under "Manifest," verify that all your manifest fields are correctly parsed and that icons are loading. Under "Service Workers," confirm the service worker is registered, active, and controlling the page. The "Cache Storage" section shows which caches exist and what files they contain.

To test offline play, check the "Offline" checkbox in the Service Workers panel (or use the Network panel's "Offline" toggle) and reload the page. The game should load entirely from cache. If any assets fail to load, add them to the service worker's install cache list and try again.

To test installation, look for the install icon in Chrome's address bar (a small plus or computer icon). Click it to trigger the install flow. After installation, the game should appear in your device's app list and launch in a standalone window matching the display mode specified in the manifest.

Use Lighthouse (built into Chrome DevTools) to audit your PWA. Run the PWA audit, and it will check for a valid manifest, a registered service worker, HTTPS, offline functionality, and other best practices. Aim for a perfect PWA score before shipping. Lighthouse also identifies common issues like missing icon sizes, incorrect start URLs, and unhandled offline scenarios.

Common Pitfalls

Forgetting to cache dynamically loaded assets. If your game loads levels, sprites, or audio on demand, those files will not be in the pre-cache. Either add them to the install cache, or use the fetch event to cache them as they are requested (cache-on-demand pattern).

Cache size growing unbounded. If you lazy-cache every asset without cleanup, storage can grow large over time. Set a maximum cache size and evict least-recently-used items when the limit is reached, or use versioned caches that get completely replaced on each update.

Service worker scope issues. A service worker at /game/sw.js can only control pages under /game/. Place it at the root of your game's URL space, or use the Service-Worker-Allowed header to extend its scope.

Not updating the service worker on deployment. If you deploy new game assets but do not change the service worker file, players will keep getting the cached old version. Always bump the cache version string in the service worker when you update any cached assets.

Key Takeaway

Adding PWA support to an existing web game requires a manifest file for installation metadata, a service worker for caching and offline support, and a few lines of registration code. The game itself does not change, but the player experience transforms from a throwaway browser tab to an installable, offline-capable application.