| name | animations |
| description | Use this skill when creating or controlling sprite animations in Phaser 4. Covers spritesheets, atlases, AnimationManager, AnimationState, play/stop/chain, frame callbacks, and animation events. Triggers on: sprite animation, spritesheet, play animation, animation frames. |
Phaser 4 -- Sprite Animations
AnimationManager (global), AnimationState (per-sprite), creating animations from spritesheets and atlases, playing/pausing/chaining, animation events, frame callbacks.
Related skills: ../sprites-and-images/SKILL.md, ../loading-assets/SKILL.md
Quick Start
this.load.spritesheet('explosion', 'explosion.png', {
frameWidth: 64,
frameHeight: 64
});
this.anims.create({
key: 'explode',
frames: this.anims.generateFrameNumbers('explosion', { start: 0, end: 11 }),
frameRate: 24,
repeat: 0
});
const sprite = this.add.sprite(400, 300, 'explosion');
sprite.play('explode');
Core Concepts
AnimationManager vs AnimationState
Phaser has two distinct animation objects:
| Aspect | AnimationManager | AnimationState |
|---|
| Access | this.anims (in a Scene) or this.game.anims | sprite.anims |
| Scope | Global -- shared across all scenes | Per-sprite instance |
| Purpose | Create/store animation definitions | Control playback on one Game Object |
| Class | Phaser.Animations.AnimationManager | Phaser.Animations.AnimationState |
The AnimationManager is a singleton owned by the Game. Animations registered there are available in every Scene. The AnimationState lives on each Sprite and handles playback for that specific object.
An Animation is a sequence of AnimationFrame objects plus timing data. Created via this.anims.create(config) (global) or sprite.anims.create(config) (local to one sprite).
Local vs Global Animations
When sprite.anims.play(key) is called, it first checks for a local animation with that key, then falls back to the global AnimationManager. Use local for sprite-specific animations; use global when shared across sprites.
this.anims.create({ key: 'walk', frames: 'player_walk', frameRate: 12, repeat: -1 });
sprite.anims.create({ key: 'walk', frames: 'npc_walk', frameRate: 10, repeat: -1 });
sprite.play('walk');
Common Patterns
Spritesheet Animation
Use generateFrameNumbers for spritesheets (numeric frame indices).
this.load.spritesheet('dude', 'dude.png', { frameWidth: 32, frameHeight: 48 });
this.anims.create({
key: 'run',
frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 7 }),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'idle',
frames: this.anims.generateFrameNumbers('dude', { frames: [0, 1, 2, 1] }),
frameRate: 6,
repeat: -1
});
generateFrameNumbers config:
start (default 0) -- first frame index
end (default -1, meaning last frame) -- final frame index
first -- a single frame to prepend before the range
frames -- explicit array of frame indices (overrides start/end)
Atlas Animation
Use generateFrameNames for texture atlases (string-based frame names).
this.load.atlas('gems', 'gems.png', 'gems.json');
this.anims.create({
key: 'ruby_sparkle',
frames: this.anims.generateFrameNames('gems', {
prefix: 'ruby_',
start: 1,
end: 6,
zeroPad: 4
}),
frameRate: 12,
repeat: -1
});
generateFrameNames config:
prefix -- prepended to each frame number
suffix -- appended after each frame number
start, end -- numeric range
zeroPad -- left-pad numbers to this length with zeros
frames -- explicit array of frame numbers (overrides start/end)
If you call generateFrameNames(key) with no config, it returns all frames from the atlas.
String as Frames
Pass a texture key string as frames to use all frames from that texture, sorted numerically by default. Set sortFrames: false to disable sorting.
this.anims.create({ key: 'walk', frames: 'player_walk', frameRate: 12, repeat: -1 });
Yoyo and Repeat
this.anims.create({
key: 'pulse',
frames: this.anims.generateFrameNumbers('orb', { start: 0, end: 5 }),
frameRate: 10,
yoyo: true,
repeat: -1,
repeatDelay: 500
});
When yoyo is true, the animation plays forward then reverses. The full cycle counts as one play.
Chaining Animations
sprite.play('attack');
sprite.chain('idle');
sprite.chain(['fall', 'land', 'idle']);
sprite.anims.chain();
Chaining is per-sprite. Chained animations start after animationcomplete or animationstop. An animation with repeat: -1 never completes -- call stop() to trigger the chain.
Playing in Reverse
sprite.playReverse('walk');
sprite.anims.reverse();
playReverse sets forward = false and inReverse = true. The reverse() method toggles direction mid-playback.
Play Variants
sprite.play('walk', true);
sprite.anims.playAfterDelay('walk', 1000);
sprite.anims.playAfterRepeat('walk', 2);
Animation Mixing
Adds a transition delay between two specific animations, set globally on the AnimationManager.
this.anims.addMix('idle', 'walk', 200);
this.anims.addMix('walk', 'idle', 300);
sprite.play('idle');
sprite.play('walk');
this.anims.removeMix('idle', 'walk');
this.anims.removeMix('idle');
Mix delays only apply with sprite.play(), not playAfterDelay or playAfterRepeat.
Pause, Resume, and Stop
sprite.anims.pause();
sprite.anims.resume();
this.anims.pauseAll();
this.anims.resumeAll();
sprite.anims.stop();
sprite.anims.stopAfterDelay(2000);
sprite.anims.stopAfterRepeat(1);
sprite.anims.stopOnFrame(frame);
All stop methods fire animationstop (not animationcomplete). Chained animations trigger after stop.
Animation Events
sprite.on('animationcomplete', (anim, frame, gameObject, frameKey) => {
console.log('completed:', anim.key);
});
sprite.on('animationcomplete-explode', (anim, frame, gameObject, frameKey) => {
gameObject.destroy();
});
Available events: animationstart, animationcomplete, animationcomplete-{key}, animationupdate, animationstop, animationrepeat, animationrestart. All share the same callback signature: (animation, frame, gameObject, frameKey).
Frame-Level Callbacks via animationupdate
sprite.on('animationupdate', (anim, frame, gameObject, frameKey) => {
if (anim.key === 'attack' && frame.index === 4) {
this.checkHit(gameObject);
}
});
Per-Frame Duration
Individual frames can have a duration (ms) that is added to the base msPerFrame.
this.anims.create({
key: 'combo',
frames: [
{ key: 'fighter', frame: 'punch1', duration: 50 },
{ key: 'fighter', frame: 'kick', duration: 200 },
{ key: 'fighter', frame: 'recover', duration: 100 }
],
frameRate: 24
});
Visibility, Random Start, and TimeScale
this.anims.create({
key: 'appear', frames: 'sparkle', frameRate: 12,
showOnStart: true,
hideOnComplete: true,
showBeforeDelay: true
});
this.anims.create({
key: 'ambient', frames: 'fire', frameRate: 10, repeat: -1,
randomFrame: true
});
sprite.anims.timeScale = 2;
sprite.play({ key: 'walk', timeScale: 0.5 });
this.anims.globalTimeScale = 0.5;
Staggered Playback
const enemies = this.add.group({ key: 'enemy', repeat: 9 });
this.anims.staggerPlay('walk', enemies.getChildren(), 100);
JSON Export and Import
const data = this.anims.toJSON();
this.anims.fromJSON(data);
this.anims.fromJSON(data, true);
if (!this.anims.exists('walk')) {
this.anims.create({ key: 'walk', frames: 'player_walk', frameRate: 12, repeat: -1 });
}
Modifying Animation Frames at Runtime
const anim = this.anims.get('walk');
anim.addFrame(this.anims.generateFrameNumbers('player', { start: 8, end: 10 }));
anim.addFrameAt(this.anims.generateFrameNumbers('player', { frames: [5] }), 2);
const frame = anim.frames[3];
anim.removeFrame(frame);
anim.removeFrameAt(0);
Aseprite Support
this.load.aseprite('paladin', 'paladin.png', 'paladin.json');
this.anims.createFromAseprite('paladin');
this.anims.createFromAseprite('paladin', ['walk']);
sprite.play('walk');
Configuration Reference
AnimationConfig (used with this.anims.create())
| Property | Type | Default | Description |
|---|
key | string | -- | Unique identifier for the animation |
frames | string or AnimationFrame[] | [] | Texture key string (uses all frames) or array of frame config objects |
sortFrames | boolean | true | Numerically sort frames when using a string key |
defaultTextureKey | string | null | Fallback texture key if not set per-frame |
frameRate | number | 24 | Playback rate in frames per second (used if duration is null) |
duration | number | null | Total animation length in ms (derives frameRate if set) |
skipMissedFrames | boolean | true | Skip frames when lagging behind |
delay | number | 0 | Delay before playback starts (ms) |
repeat | number | 0 | Times to repeat after first play (-1 = infinite) |
repeatDelay | number | 0 | Delay before each repeat (ms) |
yoyo | boolean | false | Reverse back to start before repeating |
showBeforeDelay | boolean | false | Show first frame immediately during delay period |