TL;DR
PWA support is good enough to ship on Android and usable but limited on iOS. The biggest practical gap is iOS install UX: there is no automatic install prompt — users must navigate a 4+ step Share menu flow. For a canvas game with WebSockets, both platforms work; fullscreen is the main iOS gap on iPhone.

Android (Chrome)

What Works Well

Install Criteria Checklist

iOS (Safari)

What Works Well

Limitations

Recommended Implementation

manifest.webmanifest

{
  "name": "Shadows",
  "short_name": "Shadows",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#0a0a0f",
  "theme_color": "#0a0a0f",
  "icons": [
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
    { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
    { "src": "/icons/icon-512-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
  ]
}

HTML head meta tags (iOS-specific)

<!-- Link the manifest -->
<link rel="manifest" href="/manifest.webmanifest">

<!-- iOS home screen icon (bypasses manifest, required for iOS) -->
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">

<!-- iOS standalone mode -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Shadows">

<!-- iOS splash screens (one per device size) -->
<link rel="apple-touch-startup-image" href="/icons/splash-2048x2732.png"
      media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2)">

<!-- Theme colour for browser chrome (Android) -->
<meta name="theme-color" content="#0a0a0f">

<!-- Viewport — critical for mobile -->
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
viewport-fit=cover is important: it makes the canvas fill the notch/Dynamic Island area on iPhones. Without it there are black bars.

Service Worker (minimum viable)

A bare-minimum SW is required for Android installability. For a live game, offline mode isn't useful, but the SW still needs to exist:

// sw.js — registers the app as installable; no caching needed for an online game
self.addEventListener('fetch', () => {});

Register it in your entry point:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js');
}

Android Custom Install Prompt

let installPrompt = null;
window.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault();
  installPrompt = e;
  // show your own "Install App" button in the game UI
});

// when user clicks your button:
installPrompt?.prompt();

Icon Files to Create

File Size Use
icon-192.png 192×192 Android manifest (any)
icon-512.png 512×512 Android manifest (any)
icon-512-maskable.png 512×512 Android adaptive icon (maskable — safe zone is centre 80%)
apple-touch-icon.png 180×180 iOS home screen
splash-*.png Various iOS launch screens (optional but polished)
For the maskable icon: the subject (logo/artwork) must fit within the centre 80% circle. The outer 10% padding on each side will be cropped by Android adaptive icon shapes.

Verdict for a WebSocket Canvas Game

Feature Android iOS iPhone iOS iPad
Installable ✓ Auto prompt ⚠ Manual 4-tap flow ⚠ Manual 4-tap flow
Fullscreen ✓ ⚠ Standalone only ✓ requestFullscreen
WebSockets ✓ ✓ (use heartbeat) ✓ (use heartbeat)
Canvas rendering ✓ ✓ ✓
Background persistence ✓ ✓ ✓
Icon quality ✓ Adaptive ✓ With apple-touch-icon ✓ With apple-touch-icon

Recommendation: implement the full metadata set — it's low-effort and makes Android excellent. iOS is worth doing for the standalone experience; just don't promise users an install prompt that will never appear.

Sources