Phaser 3 Sprite Sheets: Frames, Atlases, and Animation
Loading sprite sheet JSON in Phaser 3, frame naming, animation config, and when to use atlas vs spritesheet loader.
Phaser 3 can load sprite sheets and texture atlases via the loader. The usual flow is: one PNG and one JSON that describes either a uniform grid (spritesheet) or named regions (atlas). This guide covers both formats, how to load them, how to create and play animations, and how to match frame rate so timing looks correct in-game.
Spritesheet vs Atlas in Phaser
Spritesheet: the JSON specifies frame width, frame height, and optionally total frames or columns. Phaser computes rects in a grid. Atlas: the JSON lists each frame by name with x, y, width, height (and optionally more). Use spritesheet when all frames are the same size; use atlas when frames vary or you want named frames for animation.
Spritesheet format is minimal: you only need frameWidth, frameHeight, and the image path; Phaser will slice the texture into a grid. Atlas format is more flexible: each frame can have a custom rect and a name, which is useful when you've packed variable-size frames or when you want to reference frames by name (e.g. "idle_0", "walk_3") in your animation config. Many export tools can output both; choose based on whether your frames are uniform and whether you prefer index-based or name-based animation.
Loading and Frame Names
Load the PNG and JSON with this.load.spritesheet() or this.load.atlas() (see Phaser 3 Loader). After loading, you can create an AnimatedSprite and add an animation that references frames by index or by name. If your export tool outputs frame names (e.g. run_0, run_1), use those in the animation config so the code stays readable.
In your scene's preload: this.load.spritesheet('hero', 'hero.png', { frameWidth: 64, frameHeight: 64 }) or this.load.atlas('hero', 'hero.png', 'hero.json'). In create, add an animation with this.anims.create({ key: 'run', frames: this.anims.generateFrameNumbers('hero', { start: 0, end: 7 }) }) for spritesheet, or use generateFrameNames('hero', { prefix: 'run_', start: 0, end: 7 }) for atlas with named frames. Then assign the animation to an AnimatedSprite and play it.
Multiple animations on one sheet
If your sheet has idle (frames 0–3), walk (4–11), and jump (12–15), create three animations with the correct start/end or frame names. Use the same texture key for all; only the frame range or names differ. This keeps loading simple (one texture, one JSON) and lets you switch animations by key (e.g. play('walk')).
FPS and Duration
Phaser animations use frame duration in milliseconds (or a default). If your sprite sheet was exported at 24 FPS, each frame is about 42 ms. Set the duration in the animation config to match so that playback speed matches the original. Our export can include frame duration in the metadata; if your loader supports it, you can pass it through to the animation config.
In this.anims.create(), set frameRate (e.g. 24) or use duration per frame if you have variable timing. Matching the export FPS ensures that a 1-second animation in your tool is 1 second in the game. If you have multiple animations on the same sheet, create one anim per animation and reference the correct frame range or names. You can also set repeat: -1 for looping or repeat: 0 for one-shot.
Summary
Use spritesheet for uniform grids and atlas for variable-size or named frames. Load with the appropriate loader, create animations with frame numbers or names, and set frame rate to match your export FPS. Phaser's AnimatedSprite then handles playback and looping for you.