| name | ta-phaser-sprite-optimization |
| description | Optimizes sprite atlases and textures for Phaser. Use proactively when optimizing sprite workflows or managing texture memory. |
| category | techartist |
Phaser Sprite Optimization
"Maximize performance and minimize memory with optimized sprite workflows."
When to Use This Skill
Use when:
- Creating sprite atlases
- Optimizing texture memory
- Reducing draw calls
- Managing sprite memory
- Preparing assets for production
Quick Start
this.load.atlas("game", "assets/atlas.png", "assets/atlas.json");
const sprite = this.add.image(400, 300, "game", "player.png");
Optimization Principles
- Minimize Draw Calls - Use atlases to batch sprites
- Reduce Texture Swaps - Group related sprites
- Optimize Memory - Use appropriate texture formats
- Cache Smartly - Preload, reuse, pool
- Target Platform - Mobile vs desktop considerations
Progressive Guide
Level 1: Texture Atlas Creation
Using TexturePacker:
1. Import all sprite images
2. Set settings:
- Algorithm: MaxRects
- Padding: 2px (prevent bleeding)
- Extrude: 1px (prevent seams)
- Rotate: Disabled (Phaser compatible)
3. Export format: JSON (Hash)
preload() {
this.load.atlas('characters', 'assets/characters.png', 'assets/characters.json');
this.load.atlas('ui', 'assets/ui.png', 'assets/ui.json');
this.load.atlas('tiles', 'assets/tiles.png', 'assets/tiles.json');
}
create() {
const player = this.add.image(400, 300, 'characters', 'player/idle.png');
const coin = this.add.image(400, 300, 'tiles', 'items/coin.png');
const button = this.add.image(400, 300, 'ui', 'buttons/start.png');
}
Level 2: Texture Format Optimization
const TEXTURE_CONFIG = {
desktop: {
format: 'png',
quality: 1.0
},
mobile: {
format: 'webp',
quality: 0.8,
maxTextureSize: 2048
},
ios: {
format: 'pvr',
compression: 'PVRTC_4BPP'
},
android: {
format: 'etc2',
compression: 'ETC2_RGB'
}
};
preload() {
const isMobile = this.sys.game.device.os.android ||
this.sys.game.device.os.iOS;
if (isMobile) {
this.load.atlas('game', , );
} {
..(, , );
}
}
Level 3: Sprite Sheet Best Practices
class SpriteSheetConfig {
static readonly PIXEL_ART = {
scale: 2,
filterMode: Phaser.Textures.FilterMode.NEAREST
};
static readonly HD_ART = {
scale: 1,
filterMode: Phaser.Textures.FilterMode.LINEAR
};
static setupPixelArt(scene: Phaser.Scene) {
scene.textures.setDefaultFilterModes(
Phaser.Textures.FilterMode.NEAREST,
Phaser.Textures.FilterMode.NEAREST
);
}
static setupHDArt(scene: Phaser.Scene) {
scene.textures.setDefaultFilterModes(
Phaser.Textures..,
...
);
}
}
() {
.();
..(, , {
: ,
: ,
: ,
:
});
}
Level 4: Memory Management
class AssetManager {
private cache: Map<string, boolean> = new Map();
private references: Map<string, number> = new Map();
constructor(private scene: Phaser.Scene) {}
preloadAtlas(key: string, textureUrl: string, jsonUrl: string) {
if (!this.cache.has(key)) {
this.scene.load.atlas(key, textureUrl, jsonUrl);
this.cache.set(key, false);
this.references.set(key, 0);
}
this.references.set(key, (this.references.get(key) || ) + );
}
() {
refCount = ..(key) || ;
(refCount <= ) {
...(key);
..(key);
..(key);
} {
..(key, refCount - );
}
}
() {
sceneAssets = .(sceneKey);
sceneAssets.( .(key));
}
(: ): [] {
[];
}
}
{
: <
,
{ : ; : ; : }
> = ();
() {
source = texture.[];
..(key, {
: source.,
: source.,
: source. || ,
});
}
(): {
total = ;
..( {
total += tex. * tex. * ;
});
total;
}
(): {
.() / ( * );
}
() {
.();
..( {
size = (tex. * tex. * ) / ;
.(
,
);
});
.();
}
}
Level 5: Advanced Optimization Techniques
class SpriteOptimizer {
static organizeIntoAtlases(assets: string[]): {
characters: string[];
tiles: string[];
ui: string[];
effects: string[];
} {
return {
characters: assets.filter((a) => /player|enemy|npc/.test(a)),
tiles: assets.filter((a) => /tile|ground|wall/.test(a)),
ui: assets.filter((a) => /button|panel|icon/.test(a)),
effects: assets.filter((a) => /explosion|spark|smoke/.test(a)),
};
}
static getTextureScale(): number {
const pixelRatio = window.devicePixelRatio || 1;
if (pixelRatio >= 3) return ;
(pixelRatio >= ) ;
;
}
() {
texture = scene..(key);
source = texture.[];
halfSizeCanvas = .();
halfSizeCanvas. = source. / ;
halfSizeCanvas. = source. / ;
ctx = halfSizeCanvas.()!;
ctx.(
source. ,
,
,
source.,
source.,
,
,
halfSizeCanvas.,
halfSizeCanvas.,
);
texture.(, halfSizeCanvas);
}
(
: ..[],
): .. {
container = sprites[]...(, );
byTexture = <, ..[]>();
sprites.( {
key = (sprite )..;
(!byTexture.(key)) byTexture.(key, []);
byTexture.(key)!.(sprite);
});
byTexture.( {
spriteList.( container.(sprite));
});
container;
}
() {
bounds = ..();
sprites.( {
sprite.();
});
camera.(, {
camera.(bounds);
sprites.( {
inView = ...(
bounds,
sprite.(),
);
sprite.(inView);
});
});
}
}
Anti-Patterns
❌ DON'T:
- Load individual images for sprite frames
- Use textures larger than device max
- Forget to unload unused textures
- Mix pixel art scaling with HD art
- Create new textures at runtime without cleanup
- Overuse full-screen sprite sheets
✅ DO:
- Use atlases for related sprites
- Check device max texture size
- Unload textures between scenes
| Keep filter modes consistent
| Cache generated textures
| Partition large sprite sheets
Texture Size Guidelines
| Platform | Max Size | Recommended |
|---|
| Desktop | 4096+ | 2048 |
| Mobile (high) | 2048 | 1024-2048 |
| Mobile (low) | 1024 | 512-1024 |
| WebGL1 | 2048 | 1024 |
Memory Calculator
function calculateTextureMemory(
width: number,
height: number,
format: "rgba" | "rgb" | "pvrtc" = "rgba",
): number {
const bytesPerPixel = {
rgba: 4,
rgb: 3,
pvrtc: 0.5,
etc2: 0.5,
s3tc: 0.5,
};
return width * height * bytesPerPixel[format];
}
const texture1MB = calculateTextureMemory(512, 512, "rgba") / (1024 * 1024);
const textureCompressed =
calculateTextureMemory(512, 512, "pvrtc") / (1024 * 1024);
Checklist
Reference