AI sprite animation: make a playable character
AI sprite animation starts with one character design and a plan for how it moves. Make the idle, walk and attack from that same reference, clean up the frames, then pack and play them. A good first frame earns its keep by staying recognizable in the next one.
Our catalog lists 438 live games as of October 1, 2026. Of those, 57 list Phaser, PixiJS or Canvas as their engine: Phaser 24, PixiJS 10, Canvas 23. These counts describe the recorded engines; they don’t tell us how the animation was made.
The assets guide covers making the art and choosing a style. This guide starts with that art approved and ends with a character who can idle, walk and attack. Tool features, access and terms below were checked on October 1, 2026.
Five ways to make sprite animation
Choose the method by what you need to control: pose, appearance, timing or a reusable rig.
| Method | Use it for | What you supply | What you still fix |
|---|---|---|---|
| Reference frame plus pose sheet | A specific walk or attack silhouette | Approved character and ordered pose drawings | Anatomy, missing in-betweens and loop seam |
| Image-to-image, frame by frame | A short move with precise poses | The same reference plus one target pose per frame | Design drift and inconsistent lighting |
| Video-to-frames | Broad, flowing motion | A still character and a motion prompt or reference | Camera motion, blur, background and usable pose selection |
| Skeletal 2D | A painted character with reusable body parts | Separate images for torso, head, limbs and equipment | Part overlap, pivots, drawing order and keyframes |
| Procedural | A bobbing idle, squash, recoil or simple limbs | A fixed sprite or drawing functions | Timing, readable contact poses and collision behavior |
These are production choices, not promises that a generator will deliver a finished cycle. Try one move before buying a month of retries.
Tools you can use today
The generator’s output rights and its free access are separate questions. Here is what each maker states.
| Tool | Animation and output | Free access and cost | Output terms |
|---|---|---|---|
| PixelLab | New Animate with Text: 4-16 frames, even counts, input first frame preserved, transparent option (docs). | No subscription required. A 64 by 64 animation costs 1 generation (costs). Limited trial, some tools excluded (FAQ). | Commercial use allowed. No model training on output without permission; Open RAIL-M compliance required (terms). |
| Ludo.ai | Still image, keyframes or transferred motion to a PNG grid, PNG frame ZIP, sheet plus JSON, GIF or MP4 (sprite docs) | 30 starting credits, no card (Ludo). Free sheet downloads are watermarked (docs). Indie is $15/month billed annually, $180/year (pricing) | Users retain rights to use, share and monetize content (account docs) |
| Scenario | Reference-guided images (docs). Its sprite workflow converts a still to video, then extracts and packs poses (tutorial). | Pricing page advertises 50 free daily credits, no card. Generation costs depend on the model (pricing). | Free output: personal/evaluation only. Paid plans allow commercial use without royalties. Images include PNG; videos include MP4 (pricing). |
PixelLab’s Pose to image takes a pose skeleton and an initial image; its Image to image (Depth) uses a reference to guide shape. Those are ways to build individual target frames. Keep settings fixed between frames and change the pose deliberately.
1. Lock the reference and plan the poses
Approve one frame at the size it will appear in the game, then draw a pose plan before asking for the rest. Use the same reference for idle, walk and attack; don’t make the finished walk the new design reference for the attack.
For the worked example, use a 64 by 64 transparent canvas. Put the character’s ground line at Y=60 and its horizontal center at X=32. Leave room for the widest attack pose; if it won’t fit, enlarge every frame’s canvas together.
Save a small specification alongside the reference:
hero-reference.png is the approved design.
Side view, facing right; fixed camera and scale.
64 x 64 canvas; center X=32; ground Y=60.
Keep the same head size, costume, weapon and outline.
Palette: [paste your approved colors].
Idle: 4 frames. Walk: 8 frames. Attack: 6 frames.
Transparent background; no labels or sheet dividers.
These are example settings to adapt, not a required model format. Plan the actions separately:
| Action | Pose plan | Starting timing |
|---|---|---|
| Idle | Neutral, small inhale, peak, settle; feet fixed | 4 frames at 6 fps |
| Walk | Right contact, down, passing, up; left contact, down, passing, up | 8 frames at 10 fps |
| Attack | Ready, anticipation, windup, impact, follow-through, recovery | 6 frames at 12 fps; play once |
For a walk, make the body move in place. The game moves the character across the level. The two contact poses should read differently; mirrored arms alone don’t make a stride.
Reference plus pose sheet
A pose sheet is your ordered set of target silhouettes. Sketch it with simple shapes, keeping the feet and body on the same grid. Supply it as pose guidance where supported, rather than assuming a picture containing eight poses will be interpreted as eight output frames.
PixelLab documents skeleton guidance, while Ludo documents first, middle and final keyframes (PixelLab, Ludo). Use those controls for contact poses or an attack’s windup, impact and recovery. Then inspect the generated in-betweens yourself.
An example motion prompt:
Animate the attached approved character walking in place, facing right. Use the attached pose plan for the order of the contacts and passing poses. Keep the camera, character size, costume, weapon and palette fixed. No forward travel, camera movement, background or motion blur. The last pose must lead naturally into the first; do not duplicate the first frame at the end.
Image-to-image, frame by frame
Generate each target pose against the original character reference. Use the pose sketch to guide structure and the approved image to guide appearance. Scenario’s reference controls let you adjust influence; its docs say the result also depends on the model, prompt and settings (Scenario).
For the attack’s impact frame, try:
Keep the attached character’s design unchanged. Draw only the impact pose from the attached sketch, facing right on the same canvas. Preserve head size, clothing colors, weapon length and the ground line. No added effects; the attack must remain readable without them.
If a frame changes the costume or anatomy, repair that frame or draw the correction. Feeding an altered frame into the next generation makes the mistake part of your reference.
2. Extract frames or animate fixed parts
Video, a 2D rig and procedural motion all offer alternatives to generating each frame separately.
Video-to-frames
Generate one action with a locked camera, then choose a short usable section. Scenario’s tutorial follows this route: a still becomes video, key poses are extracted with screenshots or a video editor, then aligned into a sheet. Ludo also exposes individual PNG frames and video downloads (Ludo).
- Ask for one in-place action, no camera movement and a plain contrasting background.
- Find two equivalent contact poses enclosing one complete walk cycle. End before the repeated contact frame.
- Sample evenly spaced frames for a first pass. For an attack, preserve anticipation, impact and recovery even if they need unequal holds later.
- Remove the background, clean edge halos, and align every frame to the approved canvas. Check dark and light backgrounds.
- Reject blurred limbs and changes in scale. Cropping can’t undo a camera turn.
Inspect the exact small images you will ship as well as the generated video’s preview.
Skeletal 2D with AI-drawn parts
Generate or cut out the character’s parts once, then rig those fixed drawings in Spine. Paint the hidden overlap behind shoulders, elbows and knees so rotating a limb won’t reveal a hole. Keep left and right limbs separate, set joints where they bend, and put the weapon on its own attachment.
Spine’s purchase page lists Essential at $69 and Professional at $379, with Enterprise required at $500,000 or more annual revenue. A paid license enables saving and exports; the free trial is for evaluation and grants no runtime integration rights (purchase, license). These are editor and runtime terms; the generated drawings also need usable output rights.
Keyframe idle, walk and attack on the same rig. Export JSON or binary data for a Spine runtime, or bake a PNG image sequence and use the grid examples below (Spine export). Essential excludes meshes and other advanced features, so a rig using those needs the appropriate edition (purchase).
Use this route when you want to reuse a drawing across many poses. For pixel art, inspect rotated edges at native size before choosing it; you may prefer hand-corrected raster frames.
Procedural motion
Keep the art fixed and let code control the movement. Start with a small idle bob, a short recoil or limbs rotating around fixed joints. Use elapsed time to compute a repeating phase, such as Math.sin(seconds * Math.PI * 2), and round positions when the art needs whole pixels.
Metal Assault is the catalog’s concrete example of procedural assets: its entry, sourced from its repository, says the sprites, levels and audio come from code and it runs on Canvas. That supports the method; it doesn’t establish a particular animation formula.
3. Reject drift before packing
Check every frame against the approved reference, then play the cycle at its intended size and speed. Packing a bad frame only makes it easier to load.
- Design: same face, costume, colors, outline and weapon; count visible fingers or limbs where they matter.
- Scale: head and torso dimensions stay fixed unless the pose deliberately squashes them. Align a reference overlay; don’t auto-resize each pose to its bounding box.
- Ground and origin: planted feet stay on the baseline; airborne poses move relative to that baseline. The canvas origin stays fixed.
- Silhouette: each contact, anticipation and impact reads at game size. Check with a solid-color silhouette view.
- Loop: scrub the last-to-first transition. Remove an accidental duplicate endpoint; don’t append reverse playback to disguise a broken walk.
- Edges: actual transparency, no background fragments, no clipped sword tip, no light halo on a dark scene.
- Timing: watch slowly to find mistakes, then at target speed to judge the action. Hold anticipation or recovery deliberately rather than padding with blank frames.
Keep movement and hitboxes in game coordinates. A longer attack drawing should not silently enlarge the character’s standing collision box. The game-feel guide covers hit stop and other timing around a hit.
4. Lay out and slice the sheet
Use a uniform grid for the first moveset, or an atlas when you need irregular sizes and trimmed space. Phaser’s texture docs distinguish the two: a sheet has uniform cells; an atlas has named frames at different sizes and positions.
Plain grid: the contract used below
Arrange the accepted frames in reading order on one 512 by 192 PNG, eight columns and three rows of 64 by 64 cells. Margin and spacing are both zero. Leave unused cells transparent and exclude them from playback.
| Row | Animation | Global frame indices | Unused cells |
|---|---|---|---|
| 0 | Idle | 0-3 | 4-7 |
| 1 | Walk | 8-15 | None |
| 2 | Attack | 16-21 | 22-23 |
Numbering is zero-based. For a frame index i, the source rectangle starts at:
const sx = margin + (i % columns) * (frameWidth + spacing);
const sy = margin + Math.floor(i / columns) * (frameHeight + spacing);
When slicing in an editor, enter the actual cell dimensions and the correct outer margin and gap. Count the sliced frames and preview the first and last cell of each row. Painted grid lines from a generator are part of the pixels; remove them before slicing.
TexturePacker-style atlas
Pack individual files named idle_00.png, walk_00.png, attack_00.png and so on. Choose the export format for the target engine and keep frame names predictable. TexturePacker has a 7-day full-feature trial and a free Essential mode; advanced settings require a license and can turn sprites red in free-mode output (licensing).
Its settings cover PNG output, frame names, trimming, rotation, padding and extrusion. Start with rotation disabled, two pixels between shapes and one pixel of edge extrusion; check the actual atlas at game size. Use the corresponding metadata to position frames.
A trimmed frame has three separate rectangles or sizes:
| Field | Meaning | Example |
|---|---|---|
frame |
Packed rectangle in the atlas PNG | { x: 2, y: 2, w: 36, h: 54 } |
sourceSize |
Original full canvas | { w: 64, h: 64 } |
spriteSourceSize |
Placement of the trimmed drawing within that canvas | { x: 14, y: 6, w: 36, h: 54 } |
Keep trimmed: true and rotated: false for that example. If you discard the original size and offset, a tight crop can make a stationary character jump when its arm extends. Phaser documents these fields, and PixiJS textures retain original and trimmed dimensions (Phaser, PixiJS).
5. Play the grid in Phaser 3/4
Load the PNG with spritesheet, define the three ranges, and play the attack with zero repeats. This uses the APIs in Phaser’s textures, animations and completion events docs. pixelArt selects nearest sampling and rounded positions (config).
Use this in a JavaScript module in a project with Phaser installed, and serve your packed sheet at /art/hero-grid.png. Add <div id="game"></div> to the page.
import Phaser from 'phaser';
class Demo extends Phaser.Scene {
preload() {
this.load.spritesheet('hero', '/art/hero-grid.png', {
frameWidth: 64, frameHeight: 64, margin: 0, spacing: 0
});
}
create() {
const image = this.textures.get('hero').getSourceImage();
if (image.width !== 512 || image.height !== 192) {
throw new Error('Expected a 512 x 192 hero sheet');
}
for (const [key, start, end, fps, repeat] of [
['idle', 0, 3, 6, -1],
['walk', 8, 15, 10, -1],
['attack', 16, 21, 12, 0]
]) {
this.anims.create({ key, frameRate: fps, repeat,
frames: this.anims.generateFrameNumbers('hero', { start, end }) });
}
const hero = this.add.sprite(160, 180, 'hero', 0)
.setOrigin(0.5, 60 / 64).setScale(2);
hero.play('idle');
hero.on('animationcomplete-attack', () => hero.play('idle'));
this.hero = hero;
}
setMotion(name) { // call from your input/state code
if (!['idle', 'walk', 'attack'].includes(name)) throw new Error(name);
const state = this.hero.anims;
if (state.currentAnim?.key === 'attack' && state.isPlaying) return;
this.hero.play(name, true); // don't restart a move already playing
}
}
const game = new Phaser.Game({
type: Phaser.AUTO, width: 320, height: 240,
parent: 'game', pixelArt: true, scene: Demo
});
Call game.scene.getScenes(true)[0].setMotion('walk') when movement starts and setMotion('idle') when it stops; call setMotion('attack') for an attack. The example returns to idle when the attack finishes. A full game can choose walk instead if input is still held.
For an atlas, replace the grid loader with this.load.atlas('hero', '/art/hero-atlas.png', '/art/hero-atlas.json'). Replace the numeric frame helper with this.anims.generateFrameNames('hero', { prefix: 'walk_', start: 0, end: 7, zeroPad: 2, suffix: '.png' }), and do the same for idle and attack. Start the sprite on 'idle_00.png'. Both frame helpers are documented in Phaser’s animation guide.
6. Play the grid in PixiJS v8
Load the image as a texture, then make a new texture view for each cell using the image’s source. PixiJS separates Texture from TextureSource (textures); the asynchronous Application setup and AnimatedSprite handle rendering and playback.
Use a JavaScript module in a project with pixi.js v8 installed. Each frame object below has an explicit duration in milliseconds, as defined in the FrameObject API.
import { Application, Assets, Texture, Rectangle, AnimatedSprite } from 'pixi.js';
const app = new Application();
await app.init({ width: 320, height: 240, roundPixels: true });
document.body.appendChild(app.canvas);
const base = await Assets.load('/art/hero-grid.png');
if (base.width !== 512 || base.height !== 192) {
throw new Error('Expected a 512 x 192 hero sheet');
}
base.source.scaleMode = 'nearest';
function frames(start, count, fps) {
return Array.from({ length: count }, (_, n) => {
const i = start + n;
const texture = new Texture({ source: base.source,
frame: new Rectangle((i % 8) * 64, Math.floor(i / 8) * 64, 64, 64) });
return { texture, time: 1000 / fps };
});
}
const clips = {
idle: frames(0, 4, 6), walk: frames(8, 8, 10), attack: frames(16, 6, 12)
};
const hero = new AnimatedSprite(clips.idle);
hero.anchor.set(0.5, 60 / 64);
hero.position.set(160, 180);
hero.scale.set(2);
hero.animationSpeed = 1;
app.stage.addChild(hero);
let motion;
function setMotion(name) {
if (!clips[name]) throw new Error(name);
if (name === motion || (motion === 'attack' && hero.playing)) return;
motion = name;
hero.textures = clips[name];
hero.loop = name !== 'attack';
hero.gotoAndPlay(0);
}
hero.onComplete = () => setMotion('idle');
setMotion('idle');
Call setMotion('walk'), setMotion('idle') or setMotion('attack') from your game state. onComplete runs for the non-looping attack; it switches back to idle without restarting an existing walk every update.
For an atlas, const sheet = await Assets.load('/art/hero-atlas.json') loads the sheet described by the JSON. Build each clip from explicitly ordered names, for example sheet.textures['walk_00.png'] through 'walk_07.png', and wrap them as { texture, time }. If the JSON includes animation definitions, sheet.animations.walk is available too (AnimatedSprite). Keep the PNG path in the JSON correct.
7. Play the grid in plain Canvas
Crop each cell with the nine-argument drawImage call and advance by elapsed time. MDN documents drawImage, image decoding and animation timing. Don’t advance one animation frame per screen refresh.
Add <canvas id="game" width="320" height="240"></canvas> and run this as a module. Disable image smoothing for pixel art.
const canvas = document.querySelector('#game');
const ctx = canvas.getContext('2d');
if (!ctx) throw new Error('Canvas 2D is unavailable');
const sheet = new Image();
sheet.src = '/art/hero-grid.png';
await sheet.decode();
if (sheet.naturalWidth !== 512 || sheet.naturalHeight !== 192) {
throw new Error('Expected a 512 x 192 hero sheet');
}
ctx.imageSmoothingEnabled = false;
const clips = {
idle: { start: 0, count: 4, fps: 6, loop: true },
walk: { start: 8, count: 8, fps: 10, loop: true },
attack: { start: 16, count: 6, fps: 12, loop: false }
};
let motion = 'idle', elapsed = 0, last;
function setMotion(name) {
if (!clips[name]) throw new Error(name);
if (name === motion || motion === 'attack') return;
motion = name;
elapsed = 0;
}
function tick(now) {
elapsed += last === undefined ? 0 : Math.min(now - last, 100);
last = now;
let clip = clips[motion];
let n = Math.floor(elapsed * clip.fps / 1000);
if (!clip.loop && n >= clip.count) {
motion = 'idle'; elapsed = 0; clip = clips.idle; n = 0;
}
const i = clip.start + (n % clip.count);
const sx = (i % 8) * 64, sy = Math.floor(i / 8) * 64;
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Anchor: sheet point (32, 60) sits at game point (160, 180).
ctx.drawImage(sheet, sx, sy, 64, 64, 160 - 32 * 2, 180 - 60 * 2, 128, 128);
requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
Call the same three setMotion names from input code. This example caps a single elapsed-time step at 100 ms so returning from a hidden tab doesn’t skip the whole attack. Choose a pause policy for your game and reset last when resuming if it should freeze entirely.
For an unrotated, trimmed atlas, crop frame and draw at x - originX * scale + spriteSourceSize.x * scale, with the matching Y calculation. Multiply the packed width and height by the scale. Disable atlas rotation for this simple path, or implement rotated-frame handling explicitly.
Check the character in the game
Switch between idle, walk and attack against a visible ground line. Try the attack repeatedly, hold movement during it, stop moving and resume after a tab switch. The character’s scale and origin should stay fixed, and every attack should finish once.
Vibe Party shows the relevant combination in the catalog: its creator’s submission says it was made with Codex on Phaser and uses AI-generated characters, backgrounds and assets. It doesn’t name the art model or how its animation frames were produced. The Phaser, PixiJS and Canvas hubs link more games on each runtime.
Finish one accepted idle, walk and attack before adding directions. Keep the reference, pose plan, prompts, original frames, sheet layout and tool terms together. If the character needs a 3D skeleton instead, use AI 3D animation for games; if the base art needs work, return to the assets guide.
Games to look at
Questions
How do I keep an AI sprite animation consistent?
Reuse one approved character reference, fixed proportions and a pose plan for every animation. Compare each generated frame against that reference before packing. Align the canvas and ground line; reject costume, palette and silhouette changes instead of hiding them with extra frames.
Can I generate sprite animations for free?
Free access is limited: PixelLab offers a trial, and its new animation tool uses generations without requiring a subscription. Ludo offers 30 starting credits with watermarked free sheet downloads; Scenario restricts free outputs to personal and evaluation use. The tool table links current terms.
Should I use a sprite sheet or a texture atlas?
Use a plain grid when every frame has the same canvas size. Use an atlas when you want to pack frames of different sizes or trim transparent space. Keep the original dimensions and trim offsets in the atlas metadata so the character's origin stays fixed.
Can I load the same sprite sheet in Phaser and PixiJS?
Yes. Phaser splits a uniform grid with its spritesheet loader; in PixiJS v8 you create a Texture for each cell or load an atlas JSON. The examples below use one 512 by 192 PNG with 64-pixel cells in both libraries and plain Canvas.