GIF Frame Delays vs Game FPS: Preserve the Holds

A GIF's timing is a list of frame delays, not necessarily one frames-per-second value. If some drawings hold longer than others, matching the total duration with a single game FPS value changes the rhythm. Preserve the individual holds when that rhythm is part of the action.
This workflow turns a known delay list into a timing plan and an acceptance record. It does not decode a new GIF, alter an image, or claim that a browser will play every delay exactly as stored.
Key Takeaways
- GIF delay values use units of one hundredth of a second, or 10 milliseconds.
- Equal holds can use one frame rate; uneven holds require per-frame timing or an equivalent representation.
- Average FPS can match total duration while losing the action's pauses and accents.
- Compare the ordered duration list and the destination playback, not just the displayed FPS number.
Read each delay before choosing FPS
Start with the ordered frame-delay list. Record whether each value came from the file's timing data, a decoder report, or an intended timing specification. A player preview alone does not provide that provenance.
The GIF89a specification defines the Graphic Control Extension's delay field in hundredths of a second. For a stored delay value d, the corresponding duration is d × 10 ms. A reported value of 12 therefore represents 120 ms.
Keep the units explicit. A table headed “delay: 12” is ambiguous once copied into an engine where a duration may mean seconds or a multiplier. Prefer columns such as GIF delay units, milliseconds, and engine relative duration.
If a decoder reports a missing or zero delay, do not silently turn it into your preferred FPS. Record the condition and determine the intended hold separately. Stored timing, a player's fallback behavior, and the desired game timing are different evidence.
Use total duration as a check, not a complete specification
Total duration is the sum of all frame durations. For N equal holds at F frames per second, that becomes N / F seconds. For unequal holds, add each duration instead. The sprite animation timing calculator makes this distinction; it calculates timing rather than changing an asset.
Consider these illustrative lists. They are arithmetic examples, not observations from a decoded file.
| Example | Ordered holds | Total duration | Average frame rate |
|---|---|---|---|
| Equal holds | 100, 100, 100, 100 ms | 400 ms | 10 FPS |
| Uneven holds | 50, 50, 100, 200 ms | 400 ms | 10 FPS |
Both lists show four frames over 400 ms. The second spends half its duration on the final drawing. Replacing it with four 100 ms holds preserves the total but removes the short opening steps and long final pose.
Average FPS is N / total seconds. It is useful for a duration comparison, but the number alone cannot reconstruct the delay list. Keep both the total and the ordered holds in the handoff.
Map holds to the destination's timing model
Choose the representation supported by the game engine before entering values. An engine may accept durations directly, use relative duration multipliers, or expose only equal-duration entries.
Godot's SpriteFrames resource uses an animation FPS together with a relative duration for each frame. The absolute frame duration is its relative duration divided by the animation FPS. (Godot SpriteFrames documentation)
At a base rate of 10 FPS, the illustrative uneven list maps as follows:
| Frame | Desired hold | Relative duration at 10 FPS | Check |
|---|---|---|---|
| 1 | 50 ms | 0.5 | 0.5 / 10 = 0.05 s |
| 2 | 50 ms | 0.5 | 0.5 / 10 = 0.05 s |
| 3 | 100 ms | 1 | 1 / 10 = 0.1 s |
| 4 | 200 ms | 2 | 2 / 10 = 0.2 s |
The base FPS is part of the calculation. Changing it without recalculating the multipliers changes every hold. Record both values together.
If the destination only accepts equal-duration entries, repeating a drawing can represent longer holds when all desired durations are exact multiples of a common step. For the illustrative list, a 50 ms step uses one, one, two, and four entries respectively. That is a proposed conversion strategy, not a guarantee about any importer. Confirm that repeated entries preserve the intended event behavior in the destination.
If no suitable exact step exists, decide whether the engine can use a different timing path or whether an approximation is acceptable. Record any rounding per frame and the resulting total. Do not call a rounded conversion timing-equivalent without stating the allowed error.
An archived example shows why the last hold matters
Archived 2026-10-20 demo metadata records this duration list in milliseconds:
270, 280, 270, 270, 270, 280, 270, 270, 280, 270, 270, 40
The listed holds total 3,040 ms. Twelve equal holds over that duration would average about 253.33 ms each, or about 3.95 FPS. That would lengthen the final 40 ms hold substantially while shortening the longer holds.
This calculation uses archived metadata, not a new GIF decode or a test of the current public player. The list illustrates a conversion risk: a very short final hold can vanish into an apparently reasonable average. Confirm the source file and timing provenance before using any archived numbers for production.
Diagnose timing differences by where they appear
Compare data first, then observe destination playback. An identical list does not guarantee identical presentation across all players and displays.
| Symptom | Check first | Decision branch |
|---|---|---|
| Total is wrong and every hold differs by the same ratio | Units, base FPS, and relative durations | Correct the scale, then recalculate the total |
| Total matches but accents feel different | Ordered per-frame holds | Restore unequal holds rather than adjusting average FPS |
| Only the final pose feels too long | Final frame duration and frame order | Check for a flattened or duplicated hold |
| Timing data matches but a browser preview differs | Actual observed playback and player behavior | Keep stored timing separate from playback observations |
| Game timing changes under load | Destination playback conditions | Investigate runtime scheduling before rewriting source timing |
Game animation FPS, source video FPS, and display refresh rate describe different things. A 60 Hz display does not mean that a twelve-drawing action should have twelve equal 1/60-second holds. Browser playback is also not a precision guarantee for every stored GIF delay.
Keep a timing acceptance record
Copy this table into the asset handoff. It allows another developer to reproduce the conversion and identify the scope of the verdict.
| Record | Value to enter |
|---|---|
| Asset and revision | Source file; target resource; revision |
| Timing provenance | Decoder or specification; date; archived if applicable |
| Ordered holds | Frame identifiers with explicit duration units |
| Source total | Sum of the ordered holds |
| Destination model | Direct durations, relative duration/FPS, or repeated entries |
| Conversion values | Base rate and every multiplier or repeated-entry count |
| Rounding | Per-frame differences; total difference; allowed tolerance |
| Playback review | Player or engine; conditions; observed discrepancy |
| Decision | Accept / revise / needs runtime investigation |
| Reviewer and date | Responsible reviewer; review date |
Accept the timing conversion when its ordered holds meet the agreed tolerance and the destination preserves the intended pauses under the reviewed conditions. A matching total is one check. Preserved holds are the evidence that the rhythm survived.
Prepared by the FrameSprite Workflow Team, the operator of the linked timing calculator. This ownership is a commercial interest. AI assisted drafting; the archived timing example is identified separately from current testing. Send factual corrections through the contact page.
About the author
- We operate FrameSprite, a browser tool for indie game asset workflows. We publish practical sprite-sheet and animation export guides with downloadable examples, verification methods, and explicit limitations. Editorial ownership and corrections: https://www.framesprite.com/about#workflow-team. Contact: https://www.framesprite.com/contact.
Comments
Join the discussionNo comments yet. Be the first to comment.




