DEV Community

Thai Pangsakulyanont
Thai Pangsakulyanont

Posted on

A GIF optimization algorithm for screen recordings — from 5 MB to 986 KB

When I record my screen and save it as GIF file, sometimes the file size is unsatisfyingly large. For example, here is a GIF file recorded with Kap. 1418x406, 8 fps, 40 seconds (323 frames).

Input

Input file: 5.00 MB

I know the GIF is long (40 seconds), but I’ve already reduced the frame rate to mere 8fps. I do not expect it to be 5 MB large.

I tried to optimize the GIF file using Gifsicle with heavy lossy compression (gifsicle --lossy=200 --optimize=3), the file size is still 3.1 MB large. Reduced the palette to 48 colors (--colors 48), but it still results in a 2.2 MB file.

So I dived a bit into the world of GIF files and was able to reduce the file size down to 986 KB.

Optimization is performed under these conditions:

  • No resizing. The image must look sharp on retina displays.
  • Compression artifacts are okay as long as the result is readable.
  • Colors can be reduced.
  • No proprietary tools, but custom scripts are fine.

…and here is the resulting file, 5 times smaller:

Output

Output: 986 KB

This post outlines how I achieved this level of compression.

Studying the input file

When it comes to optimizing GIF files, what most people do is trying out a bunch of parameters until a satisfactory result is obtained. While this approach works most of the time, there is a ceiling to it. To fully exploit the characteristics of the input file, we must study the input itself, so that we know where to optimize.

ImageMagick's documentation has a chapter about studying GIF files and optimizing them. This has inspired me to create a rudimentary GIF file inspector. It is a web application that displays the raw data of each GIF frame. Decoding of GIF image on the web is possible thanks to the gifuct-js library.

What's so special about screen recordings?

A lot of GIFs are created from full-motion videos, and that is what most articles about GIF file optimizations are about. However, screen recordings have some important characteristics different from full-motion videos:

  • Most of the time, the background is stationary.
  • There are usually only just a few changes from one frame to the next.

Noisy in, noisy out.

As the GIF file inspector shows, the GIF file contains a lot of noise and dithering. Unchanged parts of the screen keeps getting repainted. While this helps make the GIF file look more pleasing, it causes the GIF file to include a lot of noise, resulting in large file size.

Raw GIF frames before

Why is there this noise?

I picked a pixel (443, 234) and graphed its RGB values over time as it goes through the animation.

Graph before

I can think of two possible reasons this may happen:

  • Kap uses aperture-node which in turn uses the AVFoundation framework to record the screen. The result is an H264 video, which is lossy. The noise you see may therefore be the H264 compression artifact.
  • When a video file is converted to GIF, the color palette is reduced to 256 colors. Dithering can also cause the pixel colors to change periodically. Kap uses FFmpeg’s default dithering algorithm, which is sierra2_4a.

…so let’s fix this.

Graph after

This looks much better, but how?

The basic idea is that we identify the segments inside the animation, and for each segment, we assign a single color to be used for the duration of that segment. A new segment will be created when the new frame’s color exceeds the threshold (RGB-Euclidean distance ≥ 18, arbitrarily chosen) compared to the color of the frame at the beginning of the segment. This is done for each pixel in the animation.

As a result, there is substantially less noise.

Raw GIF frames after

You can find the code that does this segmentation on GitHub. It is written in JavaScript and uses the Jimp library to process the animation data.

Finally, I performed a lossy optimization (using Gifsicle) and reduced the palette to 48 colors, resulting in the final 986 KB image.

Final thoughts

This has been a fun project and I got to learn about how screen recordings are captured in Kap, how GIF files work and how they are compressed, and how to use the Jimp library to work with pixel data inside image files. I also got to publish a web-based GIF file inspector.

There are many places that can be improved further. The optimization script is not optimized at all and runs very slowly (about 10x slower, compared to FFmpeg), due to its inefficiency (both time and space). There are still some noise in the final GIF image. But my curiosity has been satisfied, so I’ll leave it here.

Top comments (4)

Collapse
 
vid0vid0 profile image
Vid0Vid0

...and now convert gif to webp and get any size you want:
gif2webp.exe -m 6 -mixed -min_size 986kb.gif -o 450kb.webp
gif2webp.exe -m 6 -mixed -min_size -q 1 986kb.gif -o 100kb.webp

Collapse
 
dtinth profile image
Thai Pangsakulyanont

Thanks for your comment! Before I began this journey I also considered alternative formats.

I hope Safari will support WebP someday…

Collapse
 
jayroh profile image
Joel

Ask and you shall receive :)

macrumors.com/2020/06/22/webp-safa...

Collapse
 
meatboy profile image
Meat Boy • Edited

Love your analyse of the noise 😀