Stop changing your sprite sheet to fix animation speed

An eight-frame animation does not have a fixed duration. At 8 fps it lasts one second; at 12 fps it lasts two-thirds of a second; at 16 fps it lasts half a second. Before drawing or generating more frames, check whether the problem is missing poses or the time each pose stays on screen.

We maintain FrameSprite, a browser workspace for game assets. This is a timing and export note, not a claim that a particular frame count makes AI animation reliable. The equations work with hand-drawn sprites too.

Three numbers that are easy to mix up

Source FPS describes how a recording was sampled. Frame count is the number of entries you put in an animation. Playback FPS controls how fast those entries advance in the game. A 24 fps source video can provide eight selected poses that you play at 12 fps. You do not need to preserve every source frame.

For equal holds, forward playback and a speed multiplier of 1:

duration_seconds = frame_count / playback_fps
frame_hold_ms = 1000 / playback_fps
fps_for_target = frame_count * 1000 / target_duration_ms
Same eight frames Hold per frame Full loop
8 fps 125 ms 1.000 s
12 fps 83.333… ms 0.667 s
16 fps 62.5 ms 0.500 s

You changed the cadence without changing one pixel of the sprite sheet.

A test you can reproduce

Use the public eight-frame sample. Keep the same frames, order, canvas and pivot for all three trials. Change only playback FPS between 8, 12 and 16.

Eight bone-dragon frames in a fixed 4-by-2 grid, used for the timing experiment

  1. Check the animation alone at its intended game size.
  2. Run it beside actual movement or attack timing.
  3. If cadence improves but a foot or weapon still jumps, inspect the missing phase instead of raising FPS again.
  4. If every frame jumps by a small amount, inspect canvas and pivot alignment.
  5. If the pause happens only at the seam, look for an accidental duplicate endpoint.

The sample makes the arithmetic test repeatable. It is not evidence that eight frames is the right budget for every character or action.

Do not accumulate rounded timestamps

At 24 fps, one hold is 41.666… milliseconds. Storing 41.67 ms and repeatedly adding it introduces rounding drift. Calculate each boundary from its index instead:

function timingRows(frameCount, fps) {
  if (!Number.isInteger(frameCount) || frameCount < 1 || frameCount > 1000) {
    throw new RangeError("Frame count must be an integer from 1 to 1000");
  }
  if (!Number.isFinite(fps) || fps < 0.01 || fps > 240) {
    throw new RangeError("FPS must be between 0.01 and 240");
  }
  return Array.from({ length: frameCount }, (_, index) => ({
    index,
    startMs: index * 1000 / fps,
    endMs: (index + 1) * 1000 / fps,
  }));
}

For 60 frames at 24 fps, the last entry is index 59. It starts at approximately 2458.333 ms and ends at 2500 ms. Round when displaying or serializing, not before calculating the next boundary.

Transfer the schedule, not an assumption

In Godot, SpriteFrames.set_animation_speed() sets animation FPS. Individual frames also have relative durations: a duration of 2 holds twice as long as 1. The simple equation above assumes all those durations are 1 and playback speed has not been changed. See the official SpriteFrames reference.

In Phaser, animation frameRate controls frame advancement; it is separate from the game’s rendering frame rate. Check overrides such as extra frame duration, delays and playback speed. See the official Animation reference. Both references were checked on 2026-09-05.

Variable holds, ping-pong traversal, transition blending and runtime stalls need additional reasoning. A spreadsheet of equal frame holds cannot diagnose them.

A small handoff that prevents rework

We added shareable settings and a per-frame timing CSV to the free FrameSprite timing calculator. The CSV contains index, start, end and hold in milliseconds. It is a planning file, not an engine importer. Calculations run locally, no image upload or account is needed, and paid AI generation is a separate feature.

Send the timing settings alongside the sheet when handing work to another developer. “Eight frames at 12 fps, equal holds, no duplicate endpoint” is a more useful contract than “make it smooth.”

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post

Scaling from Solo to Stellar: The Tech Stack for a One-Person Dev Agency

Related Posts