Sub-millisecond Frame Durations

If you’re exporting as a sequence of PNGs to be combined into an mp4 or webm that’s not something like 25 or 50 fps, without sub-millisecond durations, the animation preview is giving the animator an incorrect picture of what it would look like in motion.

If one really needed it, it’d probably be fairly easy to write a script that would automatically smooth out a sprite, giving each frame a uniform duration and then adding a millisecond when the fractions accumulate. Like 60fps is 16-2/3 ms, so frame 1 is 16, frame 2 is 17, frame 3 is 17.

hi! while you’re technically correct, i’m sure people aren’t able to tell the difference between 17ms and 16.666…ms (60fps) or 33ms and 33.333…ms (30fps).
after all, when movies in 24fps are converted to 25fps pal tv broadcast, they are simply sped up - that’s ~4% increase in speed. still, it is generally considered better option than to repeat single frame each second, because it is more important to have a smooth visual experience.

another thing is: can aseprite preview 60 fps correctly? i don’t know, i don’t have a way to measure it. what i do know, however, is that after effects are often struggling with 50 or even 25 fps when the project is loaded with data and effects - and that’s on workstation with high end gpu and 64gb of ram. same is true for video players.

so, while it would be possible to make such script, i don’t believe you need it. just round the frame duration to target framerate and keep proper amount of frames per second and you’ll be fine. any difference between aseprite preview and exported video footage won’t be noticeable.

there’s also a question why you would even need to animate in 60 fps, but that’s another topic.

anyway, i was waiting for render to finish, so here’s that script.
change fps and mod values according to your needs (fps is self-explanatory, mod value tells the script which frames should be changed, for 30 fps it’s every 3rd frame, for 60 fps it’s every 2nd frame)

local fps = 30 --60 
local mod = 3 --2

local count = 1 
local swatch = 0 
local all = app.activeSprite.frames 

-- convert fps to ms 
local ms = tonumber( math.floor(1000 / fps) ) --tonumber( math.floor(1000 / fps + 0.5) )
-- convert ms to frame duration 
local dur = ms/1000 

app.transaction( function() 
	for i, frame in ipairs(all) do 
		swatch = count % mod --math.mod(count,mod)
		app.activeFrame = i 
		if swatch == 0 then 
			app.activeFrame.duration = dur + 0.001
		elseif swatch ~= 0 then
			app.activeFrame.duration = dur 
		count = count + 1