Stop changing your sprite sheet to fix animation speed
DEV Community

Stop changing your sprite sheet to fix animation speed

Here's a thinking process:

  1. Analyze User Input:
    • Role: Expert editor for DevPlace (social network for software developers)
    • Task: Reformat raw article body into clean, readable Markdown for technical audience
    • Rules:
    • Break into short, well-structured paragraphs separated by blank lines
    • Add Markdown section headings (## Heading) where topic clearly shifts
    • Use bullet/numbered lists for enumerations, inline code/fenced code blocks for code/commands/identifiers
    • Preserve every fact, name, number, quotation exactly as given. Never invent, add, remove, or reorder information.
    • Only restructure and lightly polish wording for flow and grammar; no introduction, conclusion, opinion, or commentary
    • Do not repeat the article title as heading
    • Do not wrap whole answer in code fence
    • Output only reformatted article body as Markdown, nothing else
  • Input Text: (the "Article:" section)
  • Title: "Stop changing your sprite sheet to fix animation speed"
  • Current date in prompt: 05/09/2026 (but not sure if I need to use it, probably just context)
  1. Deconstruct the Article Text:
    Let me read the article carefully and identify sections, topics, lists, code blocks, etc.

Article text:
"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. Check the animation alone at its intended game size. Run it beside actual movement or attack timing. If cadence improves but a foot or weapon still jumps, inspect the missing phase instead of raising FPS again. If every frame jumps by a small amount, inspect canvas and pivot alignment. 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.”"

Wait, the user said "Title: Stop changing your sprite sheet to fix animation speed" and then "Article:" with the text. The rules say "Do not repeat the article title as a heading and do not wrap the whole answer in a code fence." So I should not output the title as a heading. I should just output the reformatted article body.

Let me carefully parse and restructure, preserving every fact, number, quote exactly. I need to identify topic shifts for ## headings.

Let me outline the content flow:

  • Opening statement about 8-frame animation duration vs FPS
  • Intro to FrameSprite
  • Three mix-upable numbers: Source FPS, Frame count, Playback FPS
  • Example: 24fps source -> 8 poses at 12fps
  • Equations: duration_seconds, frame_hold_ms, fps_for_target
  • Table: Same eight frames, hold per frame, Full loop, at 8, 12, 16 fps
  • "You changed the cadence without changing one pixel..."
  • Test reproducible: public eight-frame sample, steps
  • Not evidence that 8 frames is right budget
  • Do not accumulate rounded timestamps
  • Function: timingRows
  • Example: 60 frames at 24 fps, last entry index 59, start/end ms
  • Round when displaying/serializing, not before calculating next boundary
  • Transfer schedule, not assumption
  • Godot: SpriteFrames.set_animation_speed()
  • Individual frames relative durations
  • Phaser: animation frameRate
  • Both references checked on 2026-09-05
  • Variable holds, ping-pong traversal, transition blending, runtime stalls need additional reasoning
  • Spreadsheet of equal frame holds cannot diagnose them
  • Small handoff: shareable settings and per-frame timing CSV to FrameSprite timing calculator
  • CSV contains index, start, end and hold in ms
  • It's a planning file, not engine importer
  • Calculations run locally, no image upload or account needed, paid AI generation separate
  • Send timing settings alongside sheet
  • Quote: "Eight frames at 12 fps, equal holds, no duplicate endpoint" is a more useful contract than "make it smooth."

Now I need to format this into Markdown with:

  • Short paragraphs separated by blank lines
  • headings where topic shifts

  • Bullet/numbered lists for enumerations
  • Inline code or fenced code blocks for code/commands/identifiers
  • Preserve all facts, names, numbers, quotations exactly
  • No invention, addition, removal, reordering
  • Only restructure and lightly polish wording for flow/grammar
  • No intro/conclusion/opinion/commentary
  • No title as heading
  • No code fence wrapping whole answer

Let me identify topic shifts for headings. I'll go paragraph by paragraph and group related content.

Let me re-read the raw text carefully, it's all one block but has clear sections. I need to split it logically.

I'll parse the text into segments:

Segment 1: "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."

Segment 2: "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."

Segment 3: "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"

Segment 4: Table: "| 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 |"

Segment 5: "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. Check the animation alone at its intended game size. Run it beside actual movement or attack timing. If cadence improves but a foot or weapon still jumps, inspect the missing phase instead of raising FPS again. If every frame jumps by a small amount, inspect canvas and pivot alignment. 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."

Segment 6: "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: [code function]"

Segment 7: "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."

Segment 8: "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."

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

Segment 10: "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."

Segment 11: Quote: "Eight frames at 12 fps, equal holds, no duplicate endpoint" is a more useful contract than "make it smooth."

Now I need to assign headings where topics clearly shift. I'll use ## for each major section. I need to be careful not to over-head, but the rule says "Add Markdown section headings (## Heading) where the topic clearly shifts, so the article scans well."

Let me decide on headings. I'll make them descriptive but concise, based on the content shifts. I need to preserve exact wording for facts/numbers/quotes, but heading text can be derived as long as it's not adding/removing info from the article body itself. The headings are part of the reformatting, not the article content. The rule says "Add Markdown section headings (## Heading) where the topic clearly shifts" - I think I can create headings that summarize the topic shift, but I need to ensure they don't contradict or add false info. I'll make them neutral and based on the content.

Possible headings:

  • Animation Duration and FPS

  • Key Definitions: Source FPS, Frame Count, Playback FPS

  • Timing Equations

  • Frame Hold Reference Table

  • Reproducible Test Setup

  • Avoiding Rounding Drift

  • Timing Function Implementation

  • Rounding Best Practices

  • Engine-Specific Animation Controls: Godot and Phaser

  • Advanced Considerations and Limitations

  • Handoff Workflow: FrameSprite Timing CSV

  • Contract Phrase

Wait, I need to check if the headings should be derived from the text or can be arbitrary. The rule says "Add Markdown section headings (## Heading) where the topic clearly shifts, so the article scans well." It doesn't say the headings must be exact quotes from the text. I'll create headings that reflect the topics, keeping them short and descriptive. I must ensure the headings don't become part of the "content" that needs preserving exactly; they're structural. The preserving rule applies to the article body facts, names, numbers, quotations. Headings are my addition for scanning. I'll make them neutral.

Now, let's format each segment into Markdown, with paragraphs, lists, code blocks as needed.

Let me go through each segment and format:

Segment 1: I'll keep as paragraphs. Maybe a heading: ## Animation Duration Varies by Playback FPS
Content: "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."

Segment 2: Heading: ## Three Numbers Easy to Mix Up
Content: "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."

Segment 3: Heading: ## Timing Equations for Equal Holds
Content: "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"

Segment 4: Table. I need to format the table in Markdown. The original has a table with pipes. I'll keep the exact content. Heading maybe not needed if I just format the table, but rule says add headings where topic shifts. The table is a distinct topic. I'll add a heading or just format it. I'll include it under a section. Maybe heading: ## Frame Hold Reference Table
But I need to preserve the table exactly. I'll format it as a Markdown table.

Segment 5: Heading: ## Test Setup: Vary Playback FPS Without Changing Frames
Content: "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. Check the animation alone at its intended game size. Run it beside actual movement or attack timing. If cadence improves but a foot or weapon still jumps, inspect the missing phase instead of raising FPS again. If every frame jumps by a small amount, inspect canvas and pivot alignment. 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."

Segment 6: Heading: ## Avoiding Rounding Drift in Timestamps
Content: "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 , }));"

I need to format the function. It's code. I should use a fenced code block or inline code. The rule says "Use bullet or numbered lists for enumerations, and inline code or fenced code blocks where code, commands, or identifiers appear." The function is a code block. I'll use a fenced code block with js or just as appropriate. But I need to preserve the exact text. I'll put it as a fenced code block.

Segment 7: Content: "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."

Segment 8: Heading: ## Engine-Specific Animation Controls
Content: "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."

Segment 9: Heading: ## Advanced Considerations Beyond Equal Holds
Content: "Variable holds, ping-pong traversal, transition blending and runtime stalls need additional reasoning. A spreadsheet of equal frame holds cannot diagnose them."

Segment 10: Heading: ## Handoff Workflow: FrameSprite Timing CSV
Content: "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."

Segment 11: Heading: ## Useful Contract Phrase
Content: "“Eight frames at 12 fps, equal holds, no duplicate endpoint” is a more useful contract than “make it smooth.”"

Now I need to combine all these into the final Markdown output, ensuring:

  • Short paragraphs separated by blank lines
  • headings where topic shifts

  • Bullet/numbered lists for enumerations
  • Inline code or fenced code blocks for code/commands/identifiers
  • Preserve every fact, name, number, quotation exactly
  • No introduction, conclusion, opinion, or commentary
  • Do not repeat the article title as a heading
  • Do not wrap whole answer in code fence
  • Output only reformatted article body as Markdown

Let me now carefully write the Markdown, segment by segment, making sure I preserve exact wording.

Important: I must check the original text for any exact phrasing, numbers, etc., and not alter them. Also, the function code must be exact.

Let me re-read the original article text carefully, copying it here mentally to ensure exact preservation:

Original:
"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. Check the animation alone at its intended game size. Run it beside actual movement or attack timing. If cadence improves but a foot or weapon still jumps, inspect the missing phase instead of raising FPS again. If every frame jumps by a small amount, inspect canvas and pivot alignment. 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

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.