By Allen Zhao for MotifDeck. Prepared with AI assistance.
At 12 frames per second, a frame lasts 1000 / 12 milliseconds: 83⅓ ms. If a format only stores whole milliseconds and you round every frame down to 83 ms, twelve frames last 996 ms instead of one second. Over a long repeated playback that small difference accumulates.
One way to distribute the rounding error is to calculate each frame boundary from the start, then subtract adjacent boundaries. The following example creates exactly one second of holds for a positive integer frame rate up to 1000.
def one_second_holds(fps):
if not 1 <= fps <= 1000:
raise ValueError('fps must be between 1 and 1000')
return [
((i + 1) * 1000 // fps) - (i * 1000 // fps)
for i in range(fps)
]
holds = one_second_holds(12)
print(holds) # [83, 83, 84, 83, 83, 84, 83, 83, 84, 83, 83, 84]
print(sum(holds)) # 1000
The individual holds still differ by a millisecond: this is a representation of a constant average rate in a whole-millisecond format. At 60 fps the holds are 16 or 17 ms and also sum to 1000 ms. I checked both examples and their totals.
For an animation with an arbitrary number of frames, compute successive boundaries across the whole timeline rather than restarting the calculation every frame. If the target format supports fractional durations or an exact frame-rate field, use those facilities instead. Some formats quantize to larger units, and some engines use a separate timeline; consult the actual format and verify playback after export. Intentional variable holds should be preserved rather than forced to a uniform rate.
MotifDeck is an AI game-art workspace I work on. Its workflow includes choosing frames and adjusting timing after motion generation; this is a general timing example, not a claim about a specific MotifDeck export format.
Top comments (0)