Deployment & Publishing
This guide covers the full production pipeline: optimized builds, hosting on the web, packaging as a PWA for offline play, wrapping with Capacitor for app stores, and publishing to itch.io.
Production Build
# Build all packages and your game app
pnpm build
# Build only your game (assumes packages are already built)
pnpm --filter my-game buildThe build pipeline (@web-engine-dev/build) handles:
- TypeScript compilation
- Asset optimization (texture compression, audio transcoding, mesh quantization)
- Code splitting by scene/feature
- Tree-shaking of unused engine packages
- Content hashing for long-term caching
Build Configuration
// game.build.config.ts
import { defineGameBuildConfig } from '@web-engine-dev/build';
export default defineGameBuildConfig({
// Entry point
entry: 'src/main.ts',
outDir: 'dist',
// Asset pipeline
assets: {
textures: {
compress: true,
formats: ['bc7', 'astc', 'etc2'], // generate per-platform formats
maxSize: 2048,
generateMipmaps: true,
},
audio: {
formats: ['opus', 'mp3'], // opus for modern browsers, mp3 fallback
quality: 0.7,
},
models: {
draco: true, // geometry compression
maxTextureSize: 1024,
},
},
// Code splitting
codeSplitting: {
byScene: true, // each scene loads lazily
vendorChunk: true, // separate chunk for engine packages
},
// Target environments
targets: {
web: true,
pwa: true,
mobile: false,
},
});Web Deployment
Static Hosting (Any CDN)
After pnpm build, the dist/ folder is a self-contained static site:
# Upload to any static host:
# - Cloudflare Pages
# - Vercel
# - Netlify
# - GitHub Pages
# - AWS S3 + CloudFront
# Example: Cloudflare Pages
npx wrangler pages deploy dist --project-name my-gameRequired Server Headers
For WebGPU and SharedArrayBuffer to work, the server must send these headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corpIn wrangler.toml (Cloudflare Workers/Pages):
[[headers]]
for = "/*"
[headers.values]
Cross-Origin-Opener-Policy = "same-origin"
Cross-Origin-Embedder-Policy = "require-corp"In Nginx:
add_header Cross-Origin-Opener-Policy "same-origin";
add_header Cross-Origin-Embedder-Policy "require-corp";Cloudflare Workers (Zero-Cold-Start)
// worker/index.ts
import { getAssetFromKV } from '@cloudflare/kv-asset-handler';
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
return getAssetFromKV(
{ request, waitUntil: ctx.waitUntil.bind(ctx) },
{
ASSET_NAMESPACE: env.__STATIC_CONTENT,
mapRequestToAsset: (req) => {
const url = new URL(req.url);
// SPA fallback: return index.html for unknown routes
if (!url.pathname.includes('.')) {
return new Request(`${url.origin}/index.html`, req);
}
return req;
},
}
);
},
};Progressive Web App (PWA)
Enable offline play and "Add to Home Screen":
// In your build config
export default defineGameBuildConfig({
targets: {
pwa: {
name: 'My Awesome Game',
shortName: 'MyGame',
description: 'An epic adventure game',
themeColor: '#1a1a2e',
backgroundColor: '#0a0a14',
icons: [
{ src: 'icons/icon-192.png', sizes: '192x192' },
{ src: 'icons/icon-512.png', sizes: '512x512' },
{ src: 'icons/icon-maskable.png', sizes: '512x512', purpose: 'maskable' },
],
display: 'fullscreen',
orientation: 'landscape',
// Precache all game assets for offline play
precacheAssets: true,
cacheStrategy: {
images: 'cache-first',
audio: 'cache-first',
models: 'cache-first',
api: 'network-first',
},
},
},
});Mobile (Capacitor)
Wrap your web game as a native iOS/Android app using Capacitor:
# Install Capacitor
pnpm add @capacitor/core @capacitor/cli @capacitor/ios @capacitor/android
# Initialize
npx cap init "My Game" com.example.mygame --web-dir dist
# Add platforms
npx cap add ios
npx cap add android
# Build web, then sync to native
pnpm build && npx cap syncCapacitor Config
// capacitor.config.ts
import type { CapacitorConfig } from '@capacitor/cli';
export default {
appId: 'com.example.mygame',
appName: 'My Game',
webDir: 'dist',
server: {
androidScheme: 'https',
iosScheme: 'https',
// Required headers for WebGPU
headers: {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
},
},
plugins: {
SplashScreen: {
launchShowDuration: 2000,
backgroundColor: '#0a0a14',
},
},
} satisfies CapacitorConfig;# Open in Xcode / Android Studio
npx cap open ios
npx cap open androidWebGPU on Mobile
WebGPU availability on mobile as of 2026:
- iOS 18+: Available via Metal backend
- Android: Available in Chrome 121+ on supported devices (Vulkan required)
For devices without WebGPU, show a clear error:
const device = await createDevice({ preferredBackend: 'auto' });
if (!device) {
document.body.innerHTML = `
<div style="text-align:center;padding:2rem">
<h2>WebGPU not supported</h2>
<p>Please update your browser or device to play.</p>
<a href="https://caniuse.com/webgpu">Check browser support</a>
</div>
`;
return;
}Publishing to itch.io
- Build your game:
pnpm build - Zip the
dist/folder:zip -r mygame.zip dist/ - On itch.io:
- Create a new project → set Kind to "HTML"
- Upload
mygame.zip - Check **"This file will be played in the browser"`
- Set viewport dimensions
- Add the required COEP/COOP headers via itch.io's "Shared headers" option (required for WebGPU)
- Embed the game with the itch.io widget
itch.io COEP/COOP Headers
In your itch.io game page settings → "Embed options" → "SharedArrayBuffer support: Enabled". This sets the required headers automatically.
Environment Variables
# .env.production
VITE_GAME_VERSION=1.0.0
VITE_API_URL=https://api.mygame.com
VITE_ANALYTICS_KEY=GA-XXXXX
VITE_SENTRY_DSN=https://[email protected]/xxx
VITE_DEBUG=false// Use in code
const version = import.meta.env.VITE_GAME_VERSION;
const isDev = import.meta.env.DEV;Analytics & Error Tracking
import {
createTelemetry,
createConsoleTransport,
createHttpTransport,
} from '@web-engine-dev/telemetry';
const telemetry = createTelemetry({
// Use an HTTP transport for production; swap in createConsoleTransport() for dev
transports: [
createHttpTransport('https://analytics.example.com/events', {
headers: { Authorization: `Bearer ${import.meta.env.VITE_ANALYTICS_KEY}` },
}),
],
autoTrackSessions: true, // emits session_start / session_end automatically
});
await telemetry.initialize();
// Hook into game events manually:
// telemetry.trackProgression('level_complete', { level: 1, time: 42.5 });
// telemetry.track('purchase', { item: 'power-up', price: 0.99 });
// For error reporting, wire to an error boundary or engine diagnostics:
// telemetry.trackError({ message: err.message, stack: err.stack });
// - or - Sentry.captureException(err) if you use Sentry directlyDeployment Checklist
Before publishing:
- [ ]
pnpm buildsucceeds with no warnings - [ ]
pnpm testpasses (orpnpm test:affected) - [ ] COEP/COOP headers configured on your host
- [ ] WebGPU-unavailable error page tested in a non-WebGPU browser
- [ ] All API keys / secrets in environment variables (not source code)
- [ ] Analytics and error tracking enabled
- [ ] Asset compression enabled (
bc7,draco,opus) - [ ] Cache headers configured (long-lived for hashed assets)
- [ ] Service Worker registered (if PWA)
- [ ] Game tested on target devices (Chrome, Edge, Safari iOS 18)
- [ ] Ads / monetization SDK integrated (if applicable)
- [ ] Privacy policy linked (required for app stores)
Next Steps
- Performance, squeeze every millisecond before shipping
- Networking, deploy the game server
- Accessibility, ensure your game is playable for all