Animated GIF on a page: convert to video for the bytes
GIF images used for animation, and videos with no modern codec source. squirrel finds both in the markup.
Why GIF is expensive
GIF encodes animation as a sequence of palette-indexed frames with no inter-frame compression worth the name. A video codec encodes the difference between frames instead, which is why the same clip is routinely a fraction of the size as MP4 or WebM.
A muted, looping, autoplaying <video> behaves like an animated GIF for a reader and costs a small fraction of the bytes. A WebM file usually carries VP9 or AV1, both of which compress better than the H.264 in a typical MP4, and offering an extra <source> costs nothing for a browser that cannot use it.
What squirrel checks
The rule runs on every crawled page and emits one or two checks:
animated-gifs: warns with the messageN GIF image(s) found - consider video format, listing up to ten filenames with a count of the rest. It is replaced byanimated-content, passing withNo GIF animations found, when there are none.video-codecs: emitted only when at least one<video>qualifies, at info status with the messageN video(s) could use modern codecs, listing up to five filenames.
A GIF is found by URL rather than by content: an <img src> whose lowercased value ends in .gif or contains .gif?, or a <picture> <source> whose srcset contains .gif or whose type is exactly image/gif. The rule does not fetch the file, so it cannot tell an animated GIF from a static one and both are reported.
A <video> is reported only when it has at least one <source> child and none of them declares a type containing webm, av1 or hevc, and the element’s own src contains neither .webm nor .av1. A video with a src and no <source> children is never reported.
Elements inside <noscript> are skipped: a browser with scripting enabled reads <noscript> content as plain text, so nothing in it loads or renders. See content inside noscript.
How to fix it
<video autoplay loop muted playsinline poster="/clip.jpg">
<source src="/clip.webm" type="video/webm" />
<source src="/clip.mp4" type="video/mp4" />
</video>muted and playsinline are what let it autoplay on mobile. ffmpeg -i clip.gif -c:v libvpx-vp9 clip.webm does the conversion.
The common false positive is a small static GIF, often an icon or a spacer. The rule reads the extension, so a one-frame GIF is reported alongside a real animation. Converting a static image to PNG or WebP is still worth doing, but it is not the same finding.
| Rule ID | perf/animated-content |
| Category | Performance |
| Scope | Per-page |
| Severity | warning |
| Weight | 4/10 |
Options
| Option | Type | Default | Description |
|---|---|---|---|
gif_size_threshold_kb | number | 100 | GIF size threshold in KB above which to suggest video conversion |
Configuration example
[rules."perf/animated-content"]
gif_size_threshold_kb = 100Enable / disable
Disable this rule
[rules]
disable = ["perf/animated-content"]Disable all Performance rules
[rules]
disable = ["perf/*"]Enable only this rule
[rules]
enable = ["perf/animated-content"]enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.
Related rules
- images/modern-format: WebP and AVIF for still images.
- images/image-file-size: the size of those files once fetched.
- perf/total-byte-weight: the total those bytes feed into.
- video/video-schema: the markup a real video should carry.
Performance findings ship in every audit next to the SEO, security and agent experience rules. See Core Web Vitals for how an agent works through a report.
References
Check your site
Run squirrel audit https://example.com and open the Performance section of the report. Every GIF is listed by filename, with the pages that use it. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.