Every sprite sheet in this game animates at 24fps, and drawn clips play at the speed they were drawn at. Applies whenever writing or reviewing animation code — pokemon sheets, effect and particle atlases, overworld characters — or converting a frame count into milliseconds.
Scanned 9/20/2026
Install to Claude Code
npx -y skills add lxsmnsyc/overwander --skill sprite-fps --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Sprite Fps?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/lxsmnsyc-sprite-fps-overwander)More formats (shields.io, HTML) on the badges page.
---
name: sprite-fps
description: >
Every sprite sheet in this game animates at 24fps, and drawn clips
play at the speed they were drawn at. Applies whenever writing or
reviewing animation code — pokemon sheets, effect and particle
atlases, overworld characters — or converting a frame count into
milliseconds.
---
**All sprites are 24fps.** A sheet's durations are counted in ticks, and one tick is `SPRITE_TICK` — `1000 / SPRITE_FPS` in [`src/canvas/sprite-sheet.ts`](../../../src/canvas/sprite-sheet.ts). Pokemon sheets, effect and particle atlases and overworld characters all count in that same tick.
## Rules
- **Never write a frame time as a literal.** `SPRITE_TICK * frames`, not `16.67 * frames`, `1000 / 60`, or a milliseconds figure worked out by hand. The same goes for tests: assert against `SPRITE_TICK`, or against the clip's own `length`/`duration`.
- **Do not confuse it with the battle clock.** The engine ticks at 60fps and its timings — cast windows, cooldowns, move delays — are unrelated. A sprite tick is about how a drawing was animated, not how the fight is simulated.
- **Clips play at their own speed by default.** A hand-drawn clip has a pace, and that pace is most of what it looks like. Play it and let it run.
- **Stretch only where something must line up.** A caster's throw is fitted to the window its move is in the air, so the gesture ends as the hit lands. That is the exception, not the habit.
- **Effects are never stretched.** A spark, a shockwave, a puff of smoke plays at its drawn speed. A beat's span says when the next beat starts and how long this one is drawn for; a span longer than the clip holds its last frame rather than slowing the clip down.
- **Looping clips repeat rather than stretch** — see `isLoopingCast`. Filling a long window with one slow pass reads as the game hanging.
## Why 24
These are hand-drawn animations of a few frames each. Run at the sixty a second the battle ticks at, a ten-frame clip is over in a sixth of a second and reads as a flicker. At 24 the same clip takes 833ms and reads as a movement.
## Converting
```ts
// Bad — a magic number, and wrong the moment the rate changes
sprite.advance(16.67 * 8);
expect(sprite.duration).toBeCloseTo(650);
// Good — counted in what the sheets are counted in
sprite.advance(SPRITE_TICK * 8);
expect(sprite.duration).toBeCloseTo(sprite.length * SPRITE_TICK);
```
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!