Phaser 4 Build and Deployment
Development Server
Start the dev server with hot reload:
npm run dev
Opens at http://localhost:5173 (Vite default). Changes auto-reload in the browser.
Production Build
npm run build # Compiles TypeScript + bundles with Vite → dist/
npm run preview # Serve the dist/ folder locally to test production build
Output in dist/ — static files ready for any web host.
Diagnose the Project
Run the validation script before building:
bash scripts/validate-project.sh
The script checks for common issues automatically.
TypeScript Type Checking
npx tsc --noEmit
Fix all type errors before shipping. Common Phaser 4 TypeScript issues:
Missing Phaser types:
// tsconfig.json — Phaser 4 resolves its own types via its exports map
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"skipLibCheck": true
}
}
Then import it explicitly in each file: import Phaser from 'phaser';
If you see TS2688: Cannot find type definition file for 'Phaser', the project is carrying
the v3-era typeRoots + types: ["Phaser"] pair. Delete both keys — Phaser 4 ships a single
types/phaser.d.ts, which is not a valid type-root package.
this.input.keyboard nullable:
// Wrong:
const cursors = this.input.keyboard.createCursorKeys();
// Correct:
const cursors = this.input.keyboard!.createCursorKeys();
sprite.body nullable:
// Wrong:
sprite.body.velocity.x
// Correct:
(sprite.body as Phaser.Physics.Arcade.Body).velocity.x
// Or:
sprite.body?.velocity.x
this.scene.get() returns base Scene type:
// Cast to specific scene class:
const game = this.scene.get('GameScene') as GameScene;
Common Build Errors
"Cannot find module 'phaser'"
npm install phaser # installs Phaser 4 (latest stable). Do NOT use phaser — that tag still points at 4.2.1
Asset 404 Errors (game loads but assets missing)
In Vite, assets must be in the public/ directory. They are served as-is at the root.
✅ public/assets/images/sky.png → loads as 'assets/images/sky.png'
❌ src/assets/images/sky.png → won't work with this.load.image()
Never import assets via ES imports for Phaser. Just reference the path string:
this.load.image('sky', 'assets/images/sky.png'); // ✅
"Phaser.Geom.Point is not a constructor"
v3 → v4 breaking change. Replace with Vector2:
// Old (v3):
const pt = new Phaser.Geom.Point(x, y);
// New (v4):
const pt = new Phaser.Math.Vector2(x, y);
"Math.PI2 is undefined"
v3 → v4 breaking change:
// Old (v3):
const angle = Math.PI2; // was π (wrong!) in v3
// New (v4):
const angle = Math.TAU; // π×2
const half = Math.PI_OVER_2; // π/2
Black Screen on Launch
- Check browser console for errors (F12)
- Check for 404s in Network tab (missing assets)
- Verify scene is in GameConfig's
scene: []array - Verify HTML has
<div id="game-container">if usingparent: 'game-container' - Try removing
parentconfig to append to document.body directly
WebGL Context Lost
// Try Canvas renderer as fallback
const config = {
type: Phaser.CANVAS, // instead of Phaser.AUTO or Phaser.WEBGL
// ...
};
Or add WebGL context loss recovery listener:
this.game.renderer.on('contextlost', () => {
console.warn('WebGL context lost — attempting recovery');
});
Vite Configuration
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
base: './', // IMPORTANT for itch.io and subdirectory deployment
build: {
outDir: 'dist',
assetsDir: 'assets',
minify: 'terser',
rollupOptions: {
output: {
manualChunks: {
phaser: ['phaser'], // Bundle Phaser separately for caching
},
},
},
},
server: {
port: 5173,
},
});
base: './' is critical for itch.io, GitHub Pages, and any subdirectory deployment. Without it, assets load from / (root) which breaks on subdirectory hosts.
Deployment Targets
This section covers the mechanics of getting a build onto a host. For deciding whether the game is ready — the readiness gate, versioning, store presence, launch day — see
skills/phaser-release/.
itch.io
npm run build- Zip the
dist/folder contents (not the folder itself — zip what's inside) - Upload the zip to itch.io → "Upload files" → HTML game
- Set "This file will be played in the browser"
- Check "SharedArrayBuffer support" if needed
GitHub Pages
# .github/workflows/deploy.yml
name: Deploy to GitHub Pages
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with: { node-version: '18' }
- run: npm ci
- run: npm run build
- uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
Set base: '/repo-name/' in vite.config.ts for GitHub Pages (subdirectory).
Netlify / Vercel
Connect the git repo. Set:
- Build command:
npm run build - Output directory:
dist
Both auto-deploy on push to main.
Capacitor (iOS/Android)
npm install @capacitor/core @capacitor/cli @capacitor/ios @capacitor/android
npx cap init
npx cap add ios
npx cap add android
npm run build
npx cap sync
npx cap open ios # Opens Xcode
npx cap open android # Opens Android Studio
Performance Checklist Before Shipping
-
arcade: { debug: false }in GameConfig - Assets in texture atlases (not individual images)
- Audio compressed (128kbps MP3 for BGM, 96kbps for SFX)
- Object pooling for bullets/particles/enemies
-
maxParticlesset on all particle emitters -
npx tsc --noEmitpasses with 0 errors - Tested in Chrome, Firefox, and Safari
- Mobile tested (if targeting mobile)
For a detailed performance playbook (measurement-first prompting, per-frame allocation elimination, spatial grid, audio lazy-load, atlas packing, tween leak fixes), see
skills/phaser-analyze/references/performance-playbook.md. Use/phaser-analyzefor a full project audit before shipping if FPS is a concern.
Scripts
Run the project validator:
bash scripts/validate-project.sh
The validator checks: Phaser version, tsconfig fields, presence of scene files, deprecated v3 API usage.