FFmpeg's two-pass GIF encoding pipeline — using the palettegen and paletteuse filters — is the most powerful free approach to high-quality GIF compression. Understanding every parameter in this pipeline gives you precise control over the trade-off between file size and visual quality.
Why Two Passes?
GIF's 256-colour limit means that before you can encode a frame, you need to decide which 256 colours best represent all the colours in your animation. A naive approach picks colours from the first frame only, which produces poor results for animations where the colour content changes significantly between frames. The two-pass approach solves this: the first pass (palettegen) analyses every frame to build an optimal global palette, and the second pass (paletteuse) uses that palette to encode the animation.
The Basic Two-Pass Command
The standard two-pass FFmpeg GIF command looks like this:
The scale=480:-1 scales the width to 480px and calculates the height automatically to preserve the aspect ratio. The flags=lanczos uses the Lanczos resampling algorithm, which produces sharper results than the default bilinear scaling.
palettegen Parameters
The palettegen filter accepts several parameters that control how the palette is built:
| Parameter | Default | Range | Effect |
|---|---|---|---|
| max_colors | 256 | 4–256 | Maximum palette size. Reducing to 64 or 128 significantly shrinks file size. |
| stats_mode | full | full / diff / single | full: analyse all pixels. diff: only changed pixels. single: first frame only. |
| reserve_transparent | 1 | 0 or 1 | Reserves one palette slot for transparency. Set to 0 if no transparency needed. |
The stats_mode=diff option is particularly useful for animations where most of the frame is static and only a small area changes — for example, a loading spinner on a white background. By analysing only the pixels that change between frames, it builds a palette optimised for the animated region rather than wasting palette slots on the static background.
paletteuse Parameters
The paletteuse filter controls how the palette is applied to each frame, including the dithering algorithm:
| Dither Option | Quality | File Size | Best For |
|---|---|---|---|
| none | Lowest | Smallest | Simple graphics, flat colours |
| bayer | Medium | Small | Email, screen recordings |
| sierra2 | High | Medium | Photographic content |
| sierra2_4a | Highest | Largest | Maximum quality, web pages |
| floyd_steinberg | High | Medium-large | General purpose |
The diff_mode Parameter
The diff_mode parameter in paletteuse controls whether the filter encodes only the rectangular region that changed between frames (rectangle mode) or the full frame. Setting diff_mode=rectangle can significantly reduce file size for animations with small moving elements on a static background, because unchanged pixels don't need to be re-encoded.
Practical Recipes for Common Use Cases
Maximum quality, web page: Use max_colors=256, stats_mode=full, dither=sierra2_4a, 15 fps, scale to 100% of intended display size.
Balanced quality/size, social media: Use max_colors=128, stats_mode=full, dither=sierra2, 10 fps, scale to 480px wide.
Minimum size, email: Use max_colors=64, stats_mode=diff, dither=bayer:bayer_scale=5, 6–8 fps, scale to 600px wide.
Screen recording / UI animation: Use max_colors=32, stats_mode=diff, dither=none, 10–15 fps, scale to actual display size. Screen recordings have very few colours and benefit enormously from dither=none because flat UI colours compress perfectly without dithering noise.
Automating with a Shell Script
For repeated use, wrap the two-pass command in a shell function: