Create high-quality GIF from video with FFmpeg: palette generation matters
Convert video clips to animated GIFs with proper palette generation. The two-pass technique that produces small, sharp GIFs vs the one-pass approach that produces blurry mess.
When to use this
You convert video to GIF for social-media previews, README demos, documentation walkthroughs, or content-marketing assets where the autoplay behavior of GIFs matters. GIFs are inefficient compared to MP4 (10-20× larger) but autoplay everywhere without controls. The choice: better-sharper-larger GIF (palette generation), or smaller-blurrier GIF (single-pass). For anything customer-facing, palette generation is non-negotiable.
Command variants
# Pass 1: generate palette
ffmpeg -ss 00:00:05 -t 5 -i input.mp4 \
-vf "fps=15,scale=480:-1:flags=lanczos,palettegen=stats_mode=diff" \
palette.png
# Pass 2: use palette
ffmpeg -ss 00:00:05 -t 5 -i input.mp4 -i palette.png \
-filter_complex "fps=15,scale=480:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle" \
output.gifTwo-pass produces dramatically better GIFs. ~3× smaller file size at much sharper quality vs one-pass.
ffmpeg -ss 00:00:05 -t 5 -i input.mp4 \
-vf "fps=15,scale=480:-1:flags=lanczos" \
output.gifFaster but produces visibly blurrier GIFs at larger file sizes. Use for quick previews; never for production assets.
What each parameter does
fps=15GIF framerate. 15-20 is the production sweet spot — higher rates produce visible quality issues in GIF format.
scale=480:-1Width 480px, auto-calculate height. GIFs are size-sensitive; 480px is typical max for social/README assets.
palettegen=stats_mode=diffGenerate optimized palette focused on changing regions (vs static background). Produces sharper output for motion-heavy clips.
paletteuse=dither=bayerUse the generated palette with Bayer dithering. Reduces banding artifacts in gradients.
bayer_scale=5Dithering scale. Higher values reduce banding more but introduce visible patterns. 5 is the standard balance.
What this outputs
An animated GIF. Two-pass output is typically 200-500 KB for a 5-second clip at 480p×15fps. Single-pass output is 600 KB - 2 MB at the same parameters with worse quality.
Pitfalls
- Single-pass produces unacceptably bad GIFs for any customer-facing use. Always use two-pass with palette generation.
- GIFs are 8-bit color (256 colors). Sources with smooth gradients (sunsets, soft shadows) banding visibly. Lower bayer_scale (1-3) helps but introduces visible patterns.
- Long GIFs (>10 seconds) get huge fast. For longer demos, use MP4 with autoplay attribute or WebM if browser support permits.
- High framerate (>20fps) GIFs produce minimal quality improvement at significant file-size cost. Stick to 15-18fps.
- GIF resolutions above 720p are usually wasteful — viewers don't scrutinize GIFs the way they do video. 480p is typical, 720p is the practical max.
At production scale
GIF generation is libavfilter-bound. Two-pass is roughly 2× the cost of one-pass. For social-content pipelines generating thousands of GIFs from video clips daily, batch the palette-generation pass and reuse palettes across similar clips when possible. Above ~10K GIFs/day, consider serving WebM with the autoplay attribute instead — same UX, much smaller files, but requires modern browsers.
MpegFlow models GIF generation as a 2-stage DAG (palette → output) so failed second-pass operations don't lose the palette work. The caching pattern: similar source clips can share palettes, saving compute at scale.
- FFmpeg
- gif
- video-operations
- social