Animation craft
Timing and spacing ARE the animation: per-frame durations carry more weight
than frame counts. With 4–8 frame budgets, every frame is a key pose — if it
doesn't read from keys alone, more inbetweens won't save it. Fewer, stronger
frames beat smooth; even inbetweening reads as mush.
Timing & spacing
- Logic at 60fps, sprites at 8–12fps. 12 is the pixel-art sweet spot;
18+ reads rushed, ≤6 reads idle. Never tie hitboxes to render framerate.
- Vary rate per clip: idle 4–8fps, walk 8–10, run/attacks up to 15–20.
- Uneven timing for attacks: slow anticipation (
120–200ms/frame),
near-instant strike (1–2 frames at ~30–60ms, one may be a smear), moderate
recovery (80–150ms). Even timing for cycles (walk ~80–150ms/frame, run
~50–80ms). In Phaser use per-frame duration overrides, not one global
frameRate, for non-cycles. Aseprite-authored sheets: keep the exported
per-frame durations verbatim and set no frameRate — hand-guessed fps ran
40–120% fast. Never pass duration to play() (freezes on frame 1); retime
with anims.timeScale.
- Slow in, slow out: cluster spacing at a pose's start/end, big jump
mid-motion. Five frames = small, small, HUGE, small, small.
- Hold the follow-through pose a beat longer; overshoot past rest on fast
moves, then settle.
- Loops must not double the contact pose at the seam. Watch every loop
for 10 straight seconds before calling it done.
Frame budgets (small sprites)
| Clip |
Frames |
Notes |
| Idle |
2–4 |
1px breathing bob; move interior pixels/hair, not the silhouette |
| Walk |
4–8 |
4 = two contacts + two passing; cut "up/recoil" frames first |
| Run |
6 (4 tiny, 3 with extreme poses) |
not a fast walk: airborne moments, lean, arm swing |
| Jump |
3–5 phases |
crouch → stretch ascent → apex → fall → land squash; select by velocity, not a timed clip |
| Attack |
3–8 |
1–3 anticipation, 1–2 strike, 1–3 recovery |
| Hit react |
1–3 |
one strong recoil pose + white flash beats three soft poses |
| Death |
4–6 |
one-shot, ends on a settled pose |
| FX (slash/smoke) |
3–6 |
expand → disperse → fade; can run 15–24fps |
(Shipped reference points: Celeste runs on 4 frames, Shovel Knight walks on
6.) AI-generated sheets: cut down to these budgets — delete
near-duplicates first; redundancy reads as floatiness.
The gameplay/animation contract
Responsiveness vs weight is a contract, not a contest: put the weight in
follow-through and recovery, which don't delay input.
- Player attacks: short startup (~2–5 logic frames for light moves),
compensating with exaggerated poses and smears. Long windups are a
deliberate cost for heavies only.
- Enemy attacks invert it: long, silhouette-distinct telegraphs that show
an attack is coming AND where it lands. The anticipation pose is gameplay
information.
- Fast transition into the strike reads as MORE powerful — power is
contrast in spacing, not duration. Slow windups convey weakness.
- Cancel windows: dodge/move/next-attack may interrupt startup and
recovery; only active frames are committed (the Dead Cells trick).
- Never look idle before you can act: hold the recovery pose, then pop
to idle — the sprite is the UI for vulnerability state.
- Movement animation never gates movement. Velocity changes instantly;
the run cycle starts mid-stride.
- Drive animation FROM state: pick jump/fall frames by velocity, trigger
land-squash from the collision event. Never
setTimeout gameplay to clip
length. On interrupt (hit mid-attack), cut instantly to the hit reaction —
never finish the old clip.
- Give control back during follow-through: the player moves while cloth/hair
finish their arcs.
- Damage lands on the contact frame, measured per clip. For authored
sheets, find the strike frame in each attack clip (bone track / the frame
the blade crosses the target line) and time the hit to it; retimed
variants keep windup →
[0, a0], strike = [a0, a1], held recovery after.
A uniform tempo-stretch of an attack clip is never the answer — it lands
damage 300–700 ms before the blade shows. When a boss shares an atlas with
variants, keep its authored telegraph timings; only the variants retime.
Readability at small sizes
- Silhouette test: idle, walk, anticipation, attack must be
distinguishable as pure black shapes. If not, redraw the pose — don't add
frames.
- One smear frame for any fast arc: stretch the weapon/limb into a blur
covering the path, 1 frame at ~30–60ms. The smear can visually carry the
hitbox.
- Exaggerate past reality: extend limbs on the strike, squash 1–2px on
landings/hits. At 16–32px, realistic motion reads as nothing.
- Keep arcs: hands/feet/weapon tips trace curves across frames — overlay
the frames and check; zigzags only as deliberate snap accents.
- Pixel hygiene on AI frames: consistent palette across frames, no
swimming outline pixels, constant volumes (head mass shouldn't pulse),
feet pinned to one baseline.
Secondary motion & life
- Hair/cape/scarf lag the body by 1 frame and settle 1–2 frames after it
stops — the cheapest way to make a 4-frame loop feel alive.
- Sub-pixel animation for motion under one pixel: animate the
anti-aliasing shades inside the sprite instead of moving the silhouette.
Best for idles, faces, small objects.
- Idle personality: blinks every few seconds, hair sway — secondary
elements, not whole-body motion.
- Transitions last: idle↔walk snaps fine at 8–12fps; spend frames only
on land-squash and attack-recovery-pop. Add dedicated transitions only
where the snap visibly jars.
- The one transition always worth it: the pivot. A single 1-frame
turn pose shown on direction change (and a lean/head-turn on wall-slide)
is the highest life-per-frame purchase in the sheet — direction flips
without it are where "stiff" reads come from.
Review checklist
- Silhouette test passes for idle/walk/anticipation/attack?
- No player attack delays its hit >~100ms after input unless deliberately heavy?
- Every enemy attack telegraphed by a distinct readable pose?
- Strikes are 1–2 fast frames with a smear; anticipation/recovery hold longer?
- Recovery cancelable; sprite never looks idle before control returns?
- All loops seamless — no doubled seam frame, no baseline bob?
- Hair/cloth lags one frame and settles after the body?
- Feet planted — stride matches move speed, no moonwalking?
- Palette/outline/volume consistent across frames (no AI shimmer)?
- Land/hit squash triggered by the game event, not the clip timeline?
- Hitboxes on 60Hz timers, sprites at 8–15fps — decoupled?
- Watched each clip 10s straight — idle still alive, run still energetic?
Related skills: pixel-art / animated-spritesheets (generate the frames),
vfx (effects animation), game-feel (hit stop, squash from code),
phaser (animation API).
1---2name: animation3description: Make 2D sprite animation read well: frame budgets per clip, timing and spacing, cancel windows, smears, secondary motion, wiring clips in Phaser. Generating frames is pixel-art / animated-spritesheets.4---56# Animation craft78Timing and spacing ARE the animation: per-frame durations carry more weight9than frame counts. With 4–8 frame budgets, every frame is a key pose — if it10doesn't read from keys alone, more inbetweens won't save it. Fewer, stronger11frames beat smooth; even inbetweening reads as mush.1213## Timing & spacing1415- **Logic at 60fps, sprites at 8–12fps.** 12 is the pixel-art sweet spot;16 18+ reads rushed, ≤6 reads idle. Never tie hitboxes to render framerate.17- **Vary rate per clip**: idle 4–8fps, walk 8–10, run/attacks up to 15–20.18- **Uneven timing for attacks**: slow anticipation (~120–200ms/frame),19 near-instant strike (1–2 frames at ~30–60ms, one may be a smear), moderate20 recovery (~80–150ms). Even timing for cycles (walk ~80–150ms/frame, run21 ~50–80ms). In Phaser use per-frame `duration` overrides, not one global22 `frameRate`, for non-cycles. Aseprite-authored sheets: keep the exported23 per-frame durations verbatim and set no `frameRate` — hand-guessed fps ran24 40–120% fast. Never pass `duration` to `play()` (freezes on frame 1); retime25 with `anims.timeScale`.26- **Slow in, slow out**: cluster spacing at a pose's start/end, big jump27 mid-motion. Five frames = small, small, HUGE, small, small.28- **Hold the follow-through pose** a beat longer; overshoot past rest on fast29 moves, then settle.30- **Loops must not double the contact pose at the seam.** Watch every loop31 for 10 straight seconds before calling it done.3233## Frame budgets (small sprites)3435| Clip | Frames | Notes |36| ---------------- | -------------------------------- | ----------------------------------------------------------------------------------------- |37| Idle | 2–4 | 1px breathing bob; move interior pixels/hair, not the silhouette |38| Walk | 4–8 | 4 = two contacts + two passing; cut "up/recoil" frames first |39| Run | 6 (4 tiny, 3 with extreme poses) | not a fast walk: airborne moments, lean, arm swing |40| Jump | 3–5 phases | crouch → stretch ascent → apex → fall → land squash; select by velocity, not a timed clip |41| Attack | 3–8 | 1–3 anticipation, 1–2 strike, 1–3 recovery |42| Hit react | 1–3 | one strong recoil pose + white flash beats three soft poses |43| Death | 4–6 | one-shot, ends on a settled pose |44| FX (slash/smoke) | 3–6 | expand → disperse → fade; can run 15–24fps |4546(Shipped reference points: Celeste runs on 4 frames, Shovel Knight walks on476.) **AI-generated sheets: cut down to these budgets** — delete48near-duplicates first; redundancy reads as floatiness.4950## The gameplay/animation contract5152Responsiveness vs weight is a contract, not a contest: put the weight in53follow-through and recovery, which don't delay input.5455- **Player attacks: short startup** (~2–5 logic frames for light moves),56 compensating with exaggerated poses and smears. Long windups are a57 deliberate cost for heavies only.58- **Enemy attacks invert it**: long, silhouette-distinct telegraphs that show59 an attack is coming AND where it lands. The anticipation pose is gameplay60 information.61- **Fast transition into the strike reads as MORE powerful** — power is62 contrast in spacing, not duration. Slow windups convey weakness.63- **Cancel windows**: dodge/move/next-attack may interrupt startup and64 recovery; only active frames are committed (the Dead Cells trick).65- **Never look idle before you can act**: hold the recovery pose, then pop66 to idle — the sprite is the UI for vulnerability state.67- **Movement animation never gates movement.** Velocity changes instantly;68 the run cycle starts mid-stride.69- **Drive animation FROM state**: pick jump/fall frames by velocity, trigger70 land-squash from the collision event. Never `setTimeout` gameplay to clip71 length. On interrupt (hit mid-attack), cut instantly to the hit reaction —72 never finish the old clip.73- Give control back during follow-through: the player moves while cloth/hair74 finish their arcs.75- **Damage lands on the contact frame, measured per clip.** For authored76 sheets, find the strike frame in each attack clip (bone track / the frame77 the blade crosses the target line) and time the hit to it; retimed78 variants keep windup → `[0, a0]`, strike = `[a0, a1]`, held recovery after.79 A uniform tempo-stretch of an attack clip is never the answer — it lands80 damage 300–700 ms before the blade shows. When a boss shares an atlas with81 variants, keep its authored telegraph timings; only the variants retime.8283## Readability at small sizes8485- **Silhouette test**: idle, walk, anticipation, attack must be86 distinguishable as pure black shapes. If not, redraw the pose — don't add87 frames.88- **One smear frame** for any fast arc: stretch the weapon/limb into a blur89 covering the path, 1 frame at ~30–60ms. The smear can visually carry the90 hitbox.91- **Exaggerate past reality**: extend limbs on the strike, squash 1–2px on92 landings/hits. At 16–32px, realistic motion reads as nothing.93- **Keep arcs**: hands/feet/weapon tips trace curves across frames — overlay94 the frames and check; zigzags only as deliberate snap accents.95- **Pixel hygiene on AI frames**: consistent palette across frames, no96 swimming outline pixels, constant volumes (head mass shouldn't pulse),97 feet pinned to one baseline.9899## Secondary motion & life100101- **Hair/cape/scarf lag the body by 1 frame** and settle 1–2 frames after it102 stops — the cheapest way to make a 4-frame loop feel alive.103- **Sub-pixel animation** for motion under one pixel: animate the104 anti-aliasing shades inside the sprite instead of moving the silhouette.105 Best for idles, faces, small objects.106- **Idle personality**: blinks every few seconds, hair sway — secondary107 elements, not whole-body motion.108- **Transitions last**: idle↔walk snaps fine at 8–12fps; spend frames only109 on land-squash and attack-recovery-pop. Add dedicated transitions only110 where the snap visibly jars.111- **The one transition always worth it: the pivot.** A single 1-frame112 turn pose shown on direction change (and a lean/head-turn on wall-slide)113 is the highest life-per-frame purchase in the sheet — direction flips114 without it are where "stiff" reads come from.115116## Review checklist1171181. Silhouette test passes for idle/walk/anticipation/attack?1192. No player attack delays its hit >~100ms after input unless deliberately heavy?1203. Every enemy attack telegraphed by a distinct readable pose?1214. Strikes are 1–2 fast frames with a smear; anticipation/recovery hold longer?1225. Recovery cancelable; sprite never looks idle before control returns?1236. All loops seamless — no doubled seam frame, no baseline bob?1247. Hair/cloth lags one frame and settles after the body?1258. Feet planted — stride matches move speed, no moonwalking?1269. Palette/outline/volume consistent across frames (no AI shimmer)?12710. Land/hit squash triggered by the game event, not the clip timeline?12811. Hitboxes on 60Hz timers, sprites at 8–15fps — decoupled?12912. Watched each clip 10s straight — idle still alive, run still energetic?130131Related skills: `pixel-art` / `animated-spritesheets` (generate the frames),132`vfx` (effects animation), `game-feel` (hit stop, squash from code),133`phaser` (animation API).