# Writing Phaser 3 Games

> Provides battle-tested patterns, best practices, and code examples for building Phaser 3 games. Use when writing game code, implementing game mechanics, setting up scenes, handling physics, animations, input, or any Phaser-related development. Covers architecture, performance, algorithms, and common pitfalls.

- Skill: `pmarashian/writing-phaser-3-games` (Agent Skill, multi-file: 21 files)
- Install (CLI): `npx skillmds@latest add pmarashian/writing-phaser-3-games`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pmarashian/writing-phaser-3-games/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: pmarashian (https://skillmd.com/u/pmarashian)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/pmarashian/writing-phaser-3-games

---


# Phaser 3 Game Development Skill

## Quick Start

### Most Common Patterns

**Multi-Scene Flow**
```javascript
const config = {
    scene: [ Boot, Preloader, MainMenu, Game, GameOver ]
};
// Boot → Preloader → MainMenu → Game → GameOver
```

**Object Pooling**
```javascript
create() {
    this.projectiles = this.physics.add.group({
        classType: Projectile,
        frameQuantity: 20,
        active: false,
        visible: false
    });
}

fire() {
    const projectile = this.projectiles.getFirstDead(false);
    if (projectile) projectile.fire(x, y);
}
```

**Global Animations**
```javascript
// In Preloader.js create()
this.anims.create({
    key: 'walk',
    frames: this.anims.generateFrameNumbers('player', { start: 0, end: 3 }),
    frameRate: 10,
    repeat: -1
});
```

**JustDown Input**
```javascript
if (Phaser.Input.Keyboard.JustDown(this.spacebar)) {
    this.jump();
}
```

## Navigation Guide

### How to Use This Skill

This skill provides access to 18 comprehensive pattern files covering all aspects of Phaser 3 development. Each file contains detailed patterns, code examples, and best practices.

**To find specific patterns:**

```bash
# Read a specific topic
Read knowledgebase/06-input-handling.md

# Search for a pattern across all files
grep -r "object pooling" knowledgebase/

# Find patterns by keyword
grep -i "animation" knowledgebase/*.md
grep -i "collision" knowledgebase/*.md
```

**Quick lookup by number:**
- 01 = Scene Architecture
- 02 = Asset Management
- 03 = Physics & Collision
- 04 = Movement Patterns
- 05 = Animation System
- 06 = Input Handling
- 07 = State Management
- 08 = Object Pooling & Memory
- 09 = Grid Systems
- 10 = Custom Game Objects
- 11 = UI/HUD Patterns
- 12 = Tween & Visual Effects
- 13 = Audio Integration
- 14 = Game Loop Patterns
- 15 = Algorithm Implementations
- 16 = Performance Optimization
- 17 = Code Organization
- 18 = Development Philosophy

## Table of Contents

### 1. Scene Architecture
[knowledgebase/01-scene-architecture.md](knowledgebase/01-scene-architecture.md)

Multi-scene flow, parallel scenes for UI overlays, scene transitions with effects, data passing between scenes.

**Key Patterns:**
- Boot → Preloader → MainMenu → Game → GameOver flow
- Parallel UI scene with `scene.launch()`
- Camera fade transitions
- Scene data passing via `init(data)`

### 2. Asset Management
[knowledgebase/02-asset-management.md](knowledgebase/02-asset-management.md)

Two-stage loading, texture atlases, multi-format audio, font synchronization.

**Key Patterns:**
- Bootstrap assets in Boot, full assets in Preloader
- Texture atlas for multiple sprites
- Multi-format audio for browser compatibility
- WebFont loading synchronization

### 3. Physics & Collision
[knowledgebase/03-physics-collision.md](knowledgebase/03-physics-collision.md)

Physics configuration, collision vs overlap, dynamic responses, state-gated collision.

**Key Patterns:**
- Collider for physical blocking, overlap for triggers
- Position-based dynamic response (paddle hits)
- State-gated collision checks
- Per-track/layer collision isolation

### 4. Movement Patterns
[knowledgebase/04-movement-patterns.md](knowledgebase/04-movement-patterns.md)

Velocity-based, time-based discrete, timer-based, pursuit, direction validation, screen wrapping.

**Key Patterns:**
- Velocity-based for continuous action games
- Time-based discrete for grid games
- Timer-based for turn-based games
- Direction validation (prevent 180° turns)

### 5. Animation System
[knowledgebase/05-animation-system.md](knowledgebase/05-animation-system.md)

Global animation creation, reverse animations, animation-driven logic, state-based control.

**Key Patterns:**
- Create animations once in Preloader, use everywhere
- Animation events for gameplay synchronization
- State-based animation control with caching
- External animation JSON data

### 6. Input Handling
[knowledgebase/06-input-handling.md](knowledgebase/06-input-handling.md)

Keyboard, pointer, touch, JustDown pattern, dual input support, input gating, cleanup.

**Key Patterns:**
- JustDown for single-press detection
- Dual keyboard + pointer support
- Input gating during animations
- Event-driven pointer movement
- One-time input with `once()`

### 7. State Management
[knowledgebase/07-state-management.md](knowledgebase/07-state-management.md)

Scene-level state, registry for persistence, localStorage, GameObject data component, state machines.

**Key Patterns:**
- Constructor for config, init() for resettable state
- Registry for cross-scene data
- GameObject data component for custom properties
- Boolean flags vs state machines

### 8. Object Pooling & Memory
[knowledgebase/08-object-pooling-memory.md](knowledgebase/08-object-pooling-memory.md)

Group-based pooling, enable/disable pattern, object reuse, dynamic texture cleanup.

**Key Patterns:**
- Pre-create with `frameQuantity`, recycle with `getFirstDead()`
- Enable/disable instead of create/destroy
- Reset pattern for reusable objects
- Dynamic texture cleanup prevention

### 9. Grid Systems
[knowledgebase/09-grid-systems.md](knowledgebase/09-grid-systems.md)

Dual coordinates, 2D arrays, adjacency helpers, grid alignment, visualization.

**Key Patterns:**
- Separate grid coordinates from pixel coordinates
- 2D array with bounds checking
- Adjacency helpers (8-direction, 4-direction)
- Grid-aligned group creation

### 10. Custom Game Objects
[knowledgebase/10-custom-game-objects.md](knowledgebase/10-custom-game-objects.md)

Extending Sprite/Image, composition pattern, extending groups, self-registration.

**Key Patterns:**
- Extend Sprite when physics + animations needed
- Extend Image for static sprites with physics
- Composition for non-visual managers
- Must call `super.preUpdate()` for animations

### 11. UI/HUD Patterns
[knowledgebase/11-ui-hud-patterns.md](knowledgebase/11-ui-hud-patterns.md)

Text styling, positioning, dynamic updates, bitmap fonts, icon counters, layering.

**Key Patterns:**
- Text origin for positioning (top-left, center, etc.)
- Animated counters with tweens
- Icon-based counters (hearts, lives)
- Depth/z-ordering for layered UI

### 12. Tween & Visual Effects
[knowledgebase/12-tween-visual-effects.md](knowledgebase/12-tween-visual-effects.md)

Basic tweens, staggered animations, choreographed sequences, camera effects, particles.

**Key Patterns:**
- Tween callbacks for sequencing
- Staggered grid animations
- Choreographed sequences with accumulating delays
- Camera shake, flash, fade
- Particle emitters

### 13. Audio Integration
[knowledgebase/13-audio-integration.md](knowledgebase/13-audio-integration.md)

Sound management, audio lock handling, centralized audio, delayed/sequenced audio.

**Key Patterns:**
- Multi-format audio loading
- Audio lock detection and handling
- Centralized audio management in UI scene
- Persistent music across scenes

### 14. Game Loop Patterns
[knowledgebase/14-game-loop-patterns.md](knowledgebase/14-game-loop-patterns.md)

Standard update, minimal event-driven, preUpdate for custom objects, delegation, timers.

**Key Patterns:**
- Early exit guards in update()
- Event-driven for turn-based games
- Timer-based recurring/one-shot events
- Update delegation to managers

### 15. Algorithm Implementations
[knowledgebase/15-algorithm-implementations.md](knowledgebase/15-algorithm-implementations.md)

Flood fill, smart random placement, solvable puzzle generation, proximity calculation, following chains, AI.

**Key Patterns:**
- Recursive flood fill with 4-direction spread
- Valid position collection for guaranteed placement
- Walker algorithm for solvable puzzles
- Following chain with ShiftPosition

### 16. Performance Optimization
[knowledgebase/16-performance-optimization.md](knowledgebase/16-performance-optimization.md)

Object pooling, texture caching, minimize update logic, event-driven, static groups.

**Key Patterns:**
- Object pooling eliminates GC pauses
- Static groups for immovable objects
- Event-driven over polling in update()
- Texture atlas for rendering performance

### 17. Code Organization
[knowledgebase/17-code-organization.md](knowledgebase/17-code-organization.md)

File structure, ES6 modules, constants management, separation of concerns.

**Key Patterns:**
- Scenes, gameobjects, managers, utils, constants folders
- ES6 module imports
- Constant files for sprite/animation/audio keys
- Clear initialization order in create()

### 18. Development Philosophy
[knowledgebase/18-development-philosophy.md](knowledgebase/18-development-philosophy.md)

Architecture principles, when to use physics, scene decisions, update vs events, progressive difficulty.

**Key Patterns:**
- When to use physics vs manual logic
- Single vs multi vs parallel scenes
- Update() vs event-driven approaches
- Progressive difficulty patterns

## Core Patterns Reference

### Scene Setup

```javascript
export default class Game extends Phaser.Scene {
    constructor() {
        super('Game');
        // Configuration constants
        this.GRID_SIZE = 32;
    }

    init() {
        // Reset resettable state
        this.score = 0;
        this.gameOver = false;
    }

    create() {
        // Initialize objects
        this.player = new Player(this);
        this.enemies = this.physics.add.group();

        // Setup collisions
        this.physics.add.collider(this.player, this.enemies, this.onHit, null, this);
    }

    update(time, delta) {
        if (this.gameOver) return;
        // Game logic
    }
}
```

### Custom Game Object

```javascript
export default class Player extends Phaser.Physics.Arcade.Sprite {
    constructor(scene, x, y) {
        super(scene, x, y, 'player');

        // Self-register
        scene.add.existing(this);
        scene.physics.add.existing(this);

        // Configuration
        this.setCollideWorldBounds(true);
        this.isAlive = true;
    }

    preUpdate(time, delta) {
        super.preUpdate(time, delta);  // ESSENTIAL for animations

        if (!this.isAlive) return;
        this.handleMovement();
    }
}
```

### Grid System

```javascript
// Dual coordinate system
this.gridX = 5;
this.gridY = 3;
this.sprite.x = this.gridX * TILE_SIZE;
this.sprite.y = this.gridY * TILE_SIZE;

// 2D array with bounds checking
getCellXY(x, y) {
    if (x < 0 || x >= this.width || y < 0 || y >= this.height) {
        return null;
    }
    return this.grid[y][x];
}
```

### Collision Handling

```javascript
// Physical collision
this.physics.add.collider(player, platforms);

// Trigger-based overlap
this.physics.add.overlap(player, coins, this.collectCoin, null, this);

// Collision with state gates
onHit(player, enemy) {
    if (!player.isAlive) return;
    if (player.invincible) return;
    if (enemy.alpha < 1) return;

    this.handleDamage(player, enemy);
}
```

### Input Patterns

```javascript
create() {
    // Cursor keys
    this.cursors = this.input.keyboard.createCursorKeys();

    // Specific key
    this.spacebar = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);

    // Pointer/touch
    this.input.on('pointerdown', this.handleClick, this);
}

update() {
    // Continuous input
    if (this.cursors.left.isDown) {
        this.player.setVelocityX(-160);
    }

    // Single-press input
    if (Phaser.Input.Keyboard.JustDown(this.spacebar)) {
        this.jump();
    }
}
```

### Animation Setup

```javascript
// In Preloader create() - global animations
this.anims.create({
    key: 'player-walk',
    frames: this.anims.generateFrameNumbers('player', { start: 0, end: 3 }),
    frameRate: 10,
    repeat: -1
});

// In Game create() - use animations
this.player.play('player-walk');

// Animation events
this.player.on('animationcomplete-attack', this.spawnProjectile, this);
```

### State Management

```javascript
// Scene-level state
init() {
    this.score = 0;
    this.lives = 3;
    this.gameOver = false;
}

// Registry for cross-scene data
this.registry.set('highscore', this.score);
const highscore = this.registry.get('highscore');

// GameObject data
sprite.setData({ gridX: 5, gridY: 3, type: 'enemy' });
const gridX = sprite.data.values.gridX;
```

## Common Pitfalls

**Critical mistakes to avoid:**

1. **Forgetting `super.preUpdate(time, delta)` in custom sprites**
   - Animations won't work without this
   - Always call first thing in preUpdate()

2. **Not calling `refreshBody()` after scaling static groups**
   ```javascript
   this.platforms.create(x, y, 'platform')
       .setScale(2)
       .refreshBody();  // REQUIRED
   ```

3. **Creating objects in update() instead of pooling**
   - Causes GC pauses and poor performance
   - Pre-create pools, recycle with enable/disable

4. **Missing bounds checks on grid access**
   ```javascript
   if (x < 0 || x >= width || y < 0 || y >= height) return null;
   ```

5. **Not cleaning up listeners/timers**
   ```javascript
   shutdown() {
       this.input.off('pointerdown', this.handleClick);
       if (this.timer) this.timer.remove();
   }
   ```

6. **Using wrong collision method**
   - Use `collider` for physical blocking
   - Use `overlap` for triggers/pickups

7. **Polling in update() when events would work**
   - Use physics overlap instead of distance checks
   - Use pointer events instead of checking every frame

8. **Hard-coding values instead of constants**
   ```javascript
   // Bad: magic numbers
   sprite.x = 400;

   // Good: named constants
   const GRID_SIZE = 32;
   sprite.x = gridX * GRID_SIZE;
   ```

9. **Not resetting state in init()**
   - Scene restart won't work properly
   - Put all resettable state in init(), not constructor

10. **Ignoring context binding in callbacks**
    ```javascript
    // Use arrow functions or bind
    this.time.delayedCall(1000, () => this.start(), [], this);
    ```

## Performance Tips

- **Object pools** for frequently created/destroyed objects
- **Texture atlases** combine sprites into single image
- **Static groups** for immovable platforms/walls
- **Event-driven** over polling in update()
- **Minimal update logic** - only what's necessary each frame
- **Cache lookups** - don't search arrays every frame
- **Set depth once** - don't sort every frame

## When to Read Detailed Files

**Starting a new game?** → Read [01-scene-architecture.md](knowledgebase/01-scene-architecture.md)

**Performance issues?** → Read [16-performance-optimization.md](knowledgebase/16-performance-optimization.md)

**Grid-based game?** → Read [09-grid-systems.md](knowledgebase/09-grid-systems.md)

**Complex animations?** → Read [05-animation-system.md](knowledgebase/05-animation-system.md) and [12-tween-visual-effects.md](knowledgebase/12-tween-visual-effects.md)

**Input problems?** → Read [06-input-handling.md](knowledgebase/06-input-handling.md)

**Memory leaks?** → Read [08-object-pooling-memory.md](knowledgebase/08-object-pooling-memory.md)

**Implementing algorithms?** → Read [15-algorithm-implementations.md](knowledgebase/15-algorithm-implementations.md)

**Architecture decisions?** → Read [18-development-philosophy.md](knowledgebase/18-development-philosophy.md)

## Quick Reference Cheat Sheet

### Scene Flow
```javascript
Boot → Preloader → MainMenu → Game → GameOver
this.scene.start('NextScene', { score: this.score });
this.scene.launch('GameUI');  // Parallel scene
```

### Physics
```javascript
this.physics.add.collider(a, b);        // Physical blocking
this.physics.add.overlap(a, b, callback); // Trigger only
sprite.setCollideWorldBounds(true);
sprite.setImmovable(true);
```

### Groups & Pooling
```javascript
this.items = this.physics.add.group({ frameQuantity: 20 });
const item = this.items.getFirstDead(false);
item.enableBody(true, x, y, true, true);
item.disableBody(true, true);
```

### Input
```javascript
this.cursors = this.input.keyboard.createCursorKeys();
Phaser.Input.Keyboard.JustDown(key)
this.input.on('pointerdown', callback, this);
```

### Tweens
```javascript
this.tweens.add({
    targets: sprite,
    x: 400,
    duration: 1000,
    ease: 'Power2',
    onComplete: () => {}
});
```

### Camera
```javascript
this.cameras.main.shake(duration, intensity);
this.cameras.main.fadeOut(duration);
this.cameras.main.flash(duration, r, g, b);
```

## Test Seam Setup

**Always include test seam in scene `create()` method**

Expose game state, scene key, and common commands:

```typescript
create() {
  // ... scene initialization ...
  
  // Test seam setup
  window.__TEST__ = {
    sceneKey: this.scene.key,
    gameState: () => this.gameState,
    commands: {
      // Scene-specific commands
      clickStartGame: () => {
        this.scene.start('GameScene');
      }
    }
  };
}
```

Document test seam in code comments for future reference.

## Scene Navigation

**Use `this.scene.start('SceneName', data)` for transitions**

Pass game state via data parameter:

```typescript
// Transition with data
this.scene.start('GameOverScene', {
  score: this.gameState.score,
  level: this.gameState.level
});

// In destination scene
create(data?: any) {
  const score = data?.score || 0;
  const level = data?.level || 1;
  // Use passed data
}
```

Clean up resources in scene `shutdown()` method. Document navigation flow in progress.txt.

## HUD Implementation (Enhanced)

**Use Graphics objects for backgrounds** (rounded rectangles):

```typescript
// HUD background
const bg = this.add.graphics();
bg.fillStyle(0x000000, 0.7);
bg.fillRoundedRect(10, 10, 200, 50, 5);
bg.setDepth(100); // Above game elements
```

**Set depth to 100+ for HUD elements**:
- Background: depth 100
- Text: depth 101+

**Position relative to camera dimensions**:
```typescript
const x = this.cameras.main.width - 220; // Right side
const y = 10; // Top
```

**Update text in real-time via `setText()`**:
```typescript
this.timerText.setText('01:00');
```

## Audio Integration (Enhanced)

**Load audio in scene `preload()` method**:
```typescript
preload() {
  this.load.audio('bgMusic', 'assets/music.mp3');
}
```

**Initialize AudioManager in `create()` method**:
```typescript
create() {
  this.bgMusic = this.sound.add('bgMusic', { loop: true });
}
```

**Respect volume settings from localStorage**:
```typescript
const settings = loadSettings();
this.bgMusic.setVolume(settings.musicVolume);
```

**Use test seam to verify audio state**:
```typescript
window.__TEST__ = {
  commands: {
    getAudioState: () => ({
      musicPlaying: this.bgMusic.isPlaying,
      volume: this.bgMusic.volume
    })
  }
};
```

This skill provides comprehensive Phaser 3 patterns. Read the detailed knowledgebase files for in-depth examples and advanced techniques.

## Camera Management Patterns

**Camera zoom and centerOn() for full visibility:**

```typescript
// Camera zoom for full visibility
this.cameras.main.setZoom(1.0);
this.cameras.main.centerOn(canvasWidth / 2, canvasHeight / 2);
```

**Viewport scaling with aspect ratio maintenance:**

```typescript
// Viewport scaling
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const gameWidth = 800;
const gameHeight = 600;

// Maintain aspect ratio
const scaleX = viewportWidth / gameWidth;
const scaleY = viewportHeight / gameHeight;
const scale = Math.min(scaleX, scaleY);

this.cameras.main.setZoom(scale);
```

**window.innerWidth/innerHeight for viewport calculations:**

```typescript
// Use window dimensions for viewport
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;

// Calculate camera bounds
this.cameras.main.setBounds(0, 0, gameWidth, gameHeight);
this.cameras.main.setScroll(0, 0);
```

**Canvas centering with flexbox:**

```css
/* CSS for canvas centering */
canvas {
  display: block;
  margin: 0 auto;
}
```

## Audio Integration Patterns

**Loading audio in preload() method:**

```typescript
preload() {
  // Load audio files
  this.load.audio('bgMusic', 'assets/audio/music/bg-music.mp3');
  this.load.audio('coinCollect', 'assets/audio/sfx/coin-collect.mp3');
}
```

**Volume control integration with Settings:**

```typescript
create() {
  // Get volume settings
  const settings = loadSettings();
  
  // Set background music volume
  this.bgMusic = this.sound.add('bgMusic', { loop: true });
  this.bgMusic.setVolume(settings.musicVolume);
  
  // Set sound effects volume
  this.coinSound = this.sound.add('coinCollect');
  this.coinSound.setVolume(settings.sfxVolume);
}
```

**Background music with looping:**

```typescript
create() {
  this.bgMusic = this.sound.add('bgMusic', { loop: true });
  this.bgMusic.play();
}
```

**Sound effect triggering patterns:**

```typescript
collectCoin() {
  // Play sound effect
  this.coinSound.play();
  
  // Update score
  this.gameState.score += 10;
}
```

**Test seam commands for audio testing:**

```typescript
window.__TEST__.commands = {
  getAudioState: () => ({
    musicPlaying: this.bgMusic?.isPlaying || false,
    musicVolume: this.bgMusic?.volume || 0,
    sfxVolume: this.coinSound?.volume || 0
  }),
  setMusicVolume: (volume: number) => {
    this.bgMusic?.setVolume(volume);
  }
};
```

## Sprite Management Patterns

**Individual sprite images vs spritesheets:**

```typescript
// Individual sprites
this.load.image('wizard-south', 'assets/wizard-south.png');
this.load.image('wizard-north', 'assets/wizard-north.png');

// Spritesheet
this.load.spritesheet('wizard', 'assets/wizard-sheet.png', {
  frameWidth: 48,
  frameHeight: 48
});
```

**Proper scaling (setScale) and origin (setOrigin):**

```typescript
// Set scale
sprite.setScale(2.0); // 2x size

// Set origin (0.5 = center, 0 = top-left)
sprite.setOrigin(0.5, 0.5); // Center origin
```

**Texture existence checks (this.textures.exists()):**

```typescript
// Check if texture exists before using
if (this.textures.exists('wizard-south')) {
  this.wizard = this.add.sprite(100, 100, 'wizard-south');
} else {
  // Fallback
  this.wizard = this.add.rectangle(100, 100, 48, 48, 0x0000ff);
  console.warn('Wizard texture not found, using placeholder');
}
```

**Fallback to placeholder graphics:**

```typescript
create() {
  if (this.textures.exists('wizard-south')) {
    this.wizard = this.add.sprite(100, 100, 'wizard-south');
  } else {
    // Fallback: Create placeholder
    this.wizard = this.add.rectangle(100, 100, 48, 48, 0x0000ff);
    this.wizard.setStrokeStyle(2, 0x000000);
    console.warn('Using placeholder for wizard sprite');
  }
}
```

**Union types for Sprite | Rectangle:**

```typescript
// Union type for flexible objects
type SpriteOrRectangle = Phaser.GameObjects.Sprite | Phaser.GameObjects.Rectangle;

function handleObject(obj: SpriteOrRectangle) {
  if (obj instanceof Phaser.GameObjects.Sprite) {
    // Sprite-specific code
  } else {
    // Rectangle-specific code
  }
}
```

## Error Handling Patterns

**Retry logic with new seeds:**

```typescript
generateMaze(maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const maze = this.mazeGenerator.generate(this.seed + i);
      return maze;
    } catch (error) {
      console.warn(`Maze generation attempt ${i + 1} failed:`, error);
      if (i === maxRetries - 1) {
        // Use fallback maze
        return this.createFallbackMaze();
      }
    }
  }
}
```

**Fallback mechanisms for critical operations:**

```typescript
create() {
  try {
    // Try to load generated asset
    this.load.image('wizard', generatedAssetUrl);
  } catch (error) {
    // Fallback to placeholder
    console.warn('Failed to load generated asset, using placeholder');
    this.load.image('wizard', 'assets/fallback/wizard.png');
  }
}
```

**Try-catch in scene create() methods:**

```typescript
create() {
  try {
    // Scene initialization
    this.initScene();
  } catch (error) {
    console.error('Scene initialization failed:', error);
    // Graceful degradation
    this.createFallbackScene();
  }
}
```

**Console warnings for debugging:**

```typescript
if (!this.textures.exists('asset-key')) {
  console.warn('Asset not found:', 'asset-key');
  console.warn('Using fallback placeholder');
}
```

**Test seam commands for error scenarios:**

```typescript
window.__TEST__.commands = {
  forceMazeFailure: () => {
    this.mazeGenerator.forceFailure = true;
  },
  restoreMazeGeneration: () => {
    this.mazeGenerator.forceFailure = false;
  }
};
```

