nray.dev home page

How To Use Video Formats for Animated Content [7 Steps]

Despite the popularity of animated GIFs on the web, they're often humongous in file size and slow to load. Because of this, Lighthouse and PageSpeed Insights audits will flag any animated GIF that's over 100 KiB with the warning message "Use video formats for animated content."

PageSpeed Insights report showing a failing `Use video formats for animated content` audit.

What's the solution?

  1. Replace all animated GIFs with optimized videos.
  2. Never use animated GIFs again.

In this post, I'll show you how to cut the size of a 2.5 MB GIF down to a 69 KB video that also improves load and Largest Contentful Paint (LCP) times.

Why does this matter?

Animated GIFs are usually much bigger than videos, which can result in the following:

  • Longer load times
  • Less bandwidth to download other assets on the page
  • More cellular data that mobile users pay for
  • Slower LCP

How do you fix this? Let's run through an example.

Step 1: Measure the baseline GIF

Let's start with an animated GIF from this demo page. It has a Homer Simpson animated GIF above the fold. There's nothing else that captures embarrassment and awkwardness in one easy-to-use image. What's not to love?

Unfortunately, PageSpeed Insights doesn't find it amusing.

PageSpeed Insights report showing a 75 performance score, 14.2 second Largest Contentful Paint, and a `Use video formats for animated content` warning.

Let's see how it fares with WebPageTest using the following settings:

  • Test Location: Virginia, EC2
  • Browser: Chrome on emulated Motorola G (gen 4)
  • Connection: 4G (9 Mbps, 170ms RTT)
  • Number of Tests to Run: 9
WebPageTest baseline showing 3.646 LCP.
WebPageTest baseline showing 2.7 second load time for the animated GIF.
The animated GIF takes 2.7 seconds to load.

The baseline results show:

  • LCP: 1.3 seconds*
  • Page Weight: 2,442 KB
  • Load time: 3.616 seconds

Step 2: Convert to MP4 and WebM video formats

Next, let's convert the animated GIF to a video. But which format should you pick? MP4 has the best browser support, but the less supported WebM format usually results in smaller file sizes.

FormatBrowser support
MP4> 96.7% (see caniuse)
WebM> 95.8% (see caniuse)

You'll need to decide whether only serving WebM offers enough browser support for your audience. Or you can offer both formats and let the browser decide — that's the approach we'll take in this guide.

Option 1: Use FFmpeg

FFmpeg is a popular command-line tool for converting video. To convert the GIF to MP4, use the following command:

Terminal window
ffmpeg -i homer-simpson.gif -vf "crop=trunc(iw/2)*2:trunc(ih/2)*2" -b:v 0 -crf 25 -f mp4 -vcodec libx264 -pix_fmt yuv420p homer-simpson.mp4

And to convert to WebM:

Terminal window
ffmpeg -i homer-simpson.gif -c vp9 -b:v 0 -crf 41 homer-simpson.webm

The preceding commands decrease the file size by 95% for MP4 and by 97% for WebM.

GIF (KB)MP4 (KB)WebM (KB)
2.5 MB122 KB80 KB

If you only have a few videos on your site, you can get great results by manually converting GIFs with the preceding commands. But I recommend using automation for anything more than that.

Option 2: Use an image CDN

Because video and image optimization can be a pain at scale, I prefer to delegate these responsibilities to an image CDN service like Cloudinary. Although image CDNs are typically not free, they can save a lot of hassle with media management and produce great results.

After uploading the image to Cloudinary, you can dynamically convert it to MP4 by replacing the .gif file extension with .mp4, making sure to replace the <cloud_name> and <file_name> placeholders with your respective Cloudinary info:

https://res.cloudinary.com/<cloud_name>/image/upload/<file_name>.mp4

Similarly, use the .webm extension in the URL to convert to WebM:

https://res.cloudinary.com/<cloud_name>/image/upload/<file_name>.webm

By converting the GIF to video with Cloudinary, the file size reduces by 96% when converted to MP4 and by over 97% when converted to WebM.

GIF (KB)MP4 (KB)WebM (KB)
2.5 MB88 KB71 KB

Step 3: Replace the img element with a video element

Instead of using the <img> element to serve a GIF, replace it with the <video> element, which can serve different formats via the source element.

<video>
<source src="/homer-simpson.webm" type="video/webm" />
<source src="/homer-simpson.mp4" type="video/mp4" />
</video>

The source elements allow the browser to serve the WebM format if it supports it. If it doesn't, it will fall back to MP4.

GIF offers the ability to autoplay, loop, and play silently, which you can replicate with the autoplay, loop, and muted attributes for video:

<video autoplay loop muted playsinline style="width: 100%">
<source src="/homer-simpson.webm" type="video/webm" />
<source src="/homer-simpson.mp4" type="video/mp4" />
</video>

The inline style attribute expands the video to the width of the content, while the playsinline attribute is needed on iOS to prevent the video from entering fullscreen mode.

After doing this, the video looks nearly identical to the original GIF, but with one jarring problem — after the video loads, it shifts the text down, resulting in a Cumulative Layout Shift (CLS):

The text moves down after the video loads, resulting in a large Cumulative Layout Shift.

Loading the demo page that has a video results in a large Cumulative Layout Shift

The browser doesn't know the video's aspect ratio until after it has loaded, which causes the layout shift. What can you do about this?

Step 4: Add aspect-ratio CSS

Unlike the <img> element, you can't add width and height attributes to the video to let the browser figure out the aspect ratio. That should work, but it's broken. To get around this, you can use the aspect-ratio CSS property instead. In this example, the video's width is 480 px and height is 354 px, so the CSS should look like the following:

<video
autoplay
loop
muted
playsinline
style="width: 100%; aspect-ratio: 480 / 354"
>
<source src="/homer-simpson.webm" type="video/webm" />
<source src="/homer-simpson.mp4" type="video/mp4" />
</video>

With this CSS, the browser knows how much space to allocate for the video before it has even been started downloading. As a result, you've eliminated the layout shift.

Using `aspect-ratio` CSS prevents a Cumulative Layout Shift after the video loads.

Loading the demo page that has a video with aspect-ratio CSS prevents CLS.

Okay, now you have an optimized video that serves different formats based on browser support and doesn't cause a Cumulative Layout Shift after it loads. Let's examine the WebPageTest waterfall chart at this point.

WebPageTest waterfall showing the first frame of the video appearing after the video has loaded.

This waterfall shows that the video's first frame only appears after the entire video finishes downloading. Ideally, that should render as quickly as possible to indicate visual progress. This also affects the LCP since it's the first frame of an autoplaying video.

Luckily, there's another trick you can use for this.

Step 5: Add a poster image

Using the poster attribute with a small image, you can encourage the browser to paint the first frame faster. That instructs the browser to download and display an image while the video is loading.

Given that this image will only show initially, you should apply heavy compression to it so that it downloads as quickly as possible. Shoot for an image file size that's less than 5 KB. Remember, its only purpose is to indicate visual progress, so it's okay if the quality is low.

Using FFmpeg, you can use the following command to extract the first frame of the video as an image.

Terminal window
ffmpeg -i homer-simpson.gif -qscale:v 31 -frames:v 1 poster.jpg

If the quality is too low, raise it by lowering the qscale:v option (1 is the highest quality, 31 is the lowest quality).

You can also use Cloudinary to get the first frame of the GIF by replacing the .gif file extension with .jpg in the URL. To lower the quality and reduce the image's file size, add q_5 to the path.

https://res.cloudinary.com/<cloud_name>/image/upload/q_5/<file_name>.jpg

Next, add the image to the poster attribute:

<video
autoplay
loop
muted
playsinline
style="width: 100%; aspect-ratio: 480 / 354"
poster="/poster.jpg"
>
<source src="/homer-simpson.webm" type="video/webm" />
<source src="/homer-simpson.mp4" type="video/mp4" />
</video>

Now WebPageTest shows the first frame rendering about 100ms earlier for most runs:

WebPageTest waterfall that shows the first frame of the video rendering after the poster image has loaded and before the video has finished loading.

Step 6: Lazy-load videos below the fold

In this example, we're only replacing one animated GIF, and it's above the fold. But like offscreen images, try to lazy-load all videos below the fold for the best performance.

Step 7: Measure the impact

Let's test the page with the video and compare it to the page with the GIF.

Testing the video with PageSpeed Insights results in a perfect score. That's a 25-point improvement over the page with a GIF.

PageSpeed Insights report showing a perfect score when testing the video.

Comparing the GIF test results with the video test results from WebPageTest shows more improvements:

  • 97% decrease in file size (2,392 KB GIF down to 69 KB video with 5 KB poster)
  • 95% decrease in page weight (2,442 KB down to 122 KB)
  • 100 ms improvement in LCP
  • 65% decrease in page load time (3.616 seconds down to 1.247 seconds)
WebPageTest filmstrip showing the first frame of the video rendering earlier than the first frame of the GIF.
WebPageTest showing a 100 ms improvement in LCP.

Conclusion

Replacing animated GIFs with optimized videos can improve your website's performance, including load time, LCP, and page weight.