The Fastest GIF Does Not Exist2022 - February 19th tech
What's the problem with GIFs?
You're trying to create a violently-shaking GIF for comedic purposes (https://knowyourmeme.com/memes/vibrating-gifs). The GIF editor you're using lets you set a frame duration / delay, so you set it to the lowest possible value for maximum shakage. But when you view the resulting GIF, it's playing much slower than intended, and there are definitely GIFs that play faster than this one. What's going on?
If you're here because you want to fix your GIF and want the quick answer, the solution is: set your frame delay to 20ms instead of 10ms. If you want to learn a bit more about GIFs, exactly why this edge case happens, and some thoughts on how to improve things, keep reading!
(Disclaimer: If you're from a distant utopian future where this isn't a problem any more, a few of the example GIFs in this article won't make much sense. Otherwise, my condolences, and please disregard this message.)
We won't be going into detail on how a GIF file is structured. For a great breakdown on the bytes that make up a GIF, check out Matthew Flickinger's "What's in a GIF" project.
In the first version of the GIF format (87a), multiple image frames (of varying size and position) would be overlaid on top of each other to create a single resulting image. Each frame could reference a different 256-colour palette, so an image could be created using more than 256 distinct colours.
In the current version of the GIF format (89a), transparency and animation features are available. Now before each image frame, an optional "delay" value can be set which determines how long that frame should be shown before moving on to the next one. Specifically, it's the number of hundredths of a second to wait before continuing to the next frame. The value can be set from 0 (no delay) to 0xffff (roughly a 10 minute delay).
What would a delay value of 0 look like? The spec doesn't answer the question directly, but mentions two things:
- When decoding a GIF file, each image frame should be processed "without delays other than those specified in the control information".
- Delay values are only used "if not 0".
I would take this to mean that any image frames with a delay of 0 should be combined with the previous image frame data, just like how the original 87a GIF format worked. If every image frame in the GIF had a delay of 0, the result would be a static image with the combined data of all the image frames.
Here's a couple of examples of what GIFs would look like if the spec were followed:
For context, this is what the above GIFs actually render as:
(If you're seeing identical images here - let me know! It means there's a browser out there that is handling GIFs differently to the big browsers.)
Source of the Problem
The problem is, nobody supports 0 delay. That is, none of the programs that you'd typically be viewing GIFs with support it (Firefox, Edge, IE, Chrome, Windows Explorer, Electron apps, Qt apps...). Anything with a delay of less than 2 (20ms) will be clamped to a higher value. By looking at the various programs' source code, we can build up a picture of why.
Qt (version 6.2.2)
Chromium (version 98.0.4754)
Firefox (versions 97 and ~38)
Internet Explorer 5
Internet Explorer 5 is closed source, but could hypothetically look something like this:
To summarise the comments above:
- Qt does it to match IE and Firefox behaviour (and to avoid overloading the CPU)
- Chromium does it to match Firefox behaviour (and to counteract annoying flashy ads)
- Firefox does it to match IE and Opera behaviour (and to support non-spec-conforming GIFs, and to avoid overloading the CPU)
- IE 5 does it because Netscape was slow (which caused lots of people to make badly formed GIFs)
One of the odd things about all these codebases: instead of e.g. clamping a value of 0 or 1 up to 2, they will instead get pushed all the way up to 10 (100ms). So there's a sweet spot – set a delay too small and you'll get a slow GIF. Set the delay juuust a bit higher and you'll get a much faster GIF.
If GIF decoding is incorrect where you're reading this, the above GIFs won't appear in order of speed.
It seems the reason for pushing values like 10ms back up to 100ms originates from a requirement to emulate the slowness of Netscape. Qt and Firefox source code comments both want to reduce CPU usage but since a value of 20ms is supported, I think they should just be clamping the value to 20ms instead. Or, don't clamp the value at all! Modern browsers already render 20ms GIF frames just fine, and I'm not sure the "computers are too slow" argument holds up 30 years later.
On top of that, leave the 0-delay frames alone too. Combine them with prior frame content, like the original GIF specification says. If all frames have a 0-delay, show the static image that results from combining all the frames.
My use case was wanting to update two very-small-but-separate areas in a large GIF in a single frame, to reduce file size. Currently, you need to find the rectangular region that covers all changes on the screen for that frame and add that to the GIF, but if you could define multiple frames, and set all but one to a delay of 0, you could create the same effect but with less image data:
A downside to making any changes is that the badly formatted GIFs won't render any more. I reckon just be bold, like the release of Netscape 2.0:
"Netscape now conforms to GIF standards: Some GIF creation utilities produce GIF images that do not conform to standards. Such images will display empty outlines in place of the image. These outlines are much larger than the actual images. The images will not be visible at all. To repair these GIF images, content providers can read the offending GIF image into a different GIF utility that conforms to the GIF89a specifications, and save the image again."
If Netscape was fine with breaking poorly-made GIFs, we should be too!
Let's Check Out Netscape
I wanted to see what it looked like when Netscape 2.0 rendered a GIF. Here's the red-square test GIF from above, as rendered (correctly!) in Netscape 2.0 on Windows 95:
Since we have Netscape available to us, why don't we have a look at what a 1-delay file actually looks like? Here's a GIF running at delay = 2 (20ms), for comparison. The results aren't quite as expected though. It runs very slowly, unless you're actively moving the mouse (the reason for this is discussed in this Stack Overflow page):
And this is with a delay of 0 - put before the exciting 10ms reveal, for dramatic effect. Look at the speed! I guess the original Netscape doesn't treat an "all-0-delay" GIF as a single static frame, as the spec might suggest it should. Why the mouse movement isn't required to speed this case up, I have no idea:
And finally, the 10ms delay GIF we've all been waiting for...
Huh. Slow, and then crashes. I guess this was before we had 144Hz monitors to render that many frames per second. So if even Netscape 2.0 doesn't display GIFs that fast… does that mean a 10ms GIF has never been perceived in all of history? Netscape crashing is a bit of an ominous sign - who knows what would happen if someone tried too hard to see the fastest GIF. Maybe we shouldn't update the browsers after all 😅
No-one renders GIFs to spec, but they should (IMO). For now, set a GIF delay of 2 (20ms) instead of 1 (10ms) to get the fastest-running GIF. If everyone updated their code to match the spec, we get these upsides:
- Support more than 256 colours in a single frame of GIF animation
- Support fast GIFs (10ms delay)
- No confusing behaviour with small delay = slow GIF
- Better compression for GIFs with multiple small areas updated per frame
Thanks for reading!
- GIF 87a Specification. https://www.w3.org/Graphics/GIF/spec-gif87.txt
- GIF 89a Specification. https://www.w3.org/Graphics/GIF/spec-gif89a.txt
- What's In A GIF. http://www.matthewflickinger.com/lab/whatsinagif/
- Netscape 2.0 Release Notes. https://web.archive.org/web/19990422100600/http://home.netscape.com/eng/mozilla/2.0/relnotes/windows-2.0.html
- Some information about animated GIFs. https://web.archive.org/web/20001212090800/http://help.netscape.com/kb/consumer/19970619-18.html
- GIF Animations. https://web.archive.org/web/20010222034721/http://home.netscape.com/navigator/v2.0/gifanimation.html
- "Following the GIF spec" GIF generated at cooltext. https://cooltext.com/Logo-Design-Burning
- Some GIFs taken from Bugzilla: Remove minimum frame delay from GIF89a files. https://bugzilla.mozilla.org/show_bug.cgi?id=1511298
- Dancing dog GIF from tenor.com. https://tenor.com/view/dance-dancing-dog-dog-look-up-happy-gif-14211308
Source Code references
- Chromium: https://source.chromium.org/chromium/chromium/src/+/refs/tags/98.0.4754.1:third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc;l=353
- Firefox: https://searchfox.org/mozilla-central/source/image/FrameTimeout.h#54 and https://hg.mozilla.org/mozilla-central/file/0b122f0b6fcfda45606c4ee6166436201578f167/image/FrameAnimator.cpp#l314
- Qt: https://github.com/qt/qtbase/blob/6.2.2/src/plugins/imageformats/gif/qgifhandler.cpp#L661
- Opera: https://github.com/proninyaroslav/opera-presto/blob/master/modules/img/src/imagedecodergif.cpp#L282
Fun Extra Things
I came across Netscape's "About GIFs" page, with a little hot air balloon lizard:
Here's Netscape trying to render a very complicated Opus Magnum GIF:
Here's Internet Explorer 5, failing to render the red-squares test GIF correctly. This shows how early it was that programs started going against the spec:
Here's Windows XP picture viewer, failing to produce the colourful GIF at all... except for in the thumbnail view:
Finally, here is Internet Explorer 5's valiant effort to render the colourful GIF: