Convert a video to GIF with FFmpeg
A GIF has 256 colors per frame, so the quality depends on the palette. The primary command generates one from the clip and applies it in a single pass; the variants shrink the file.
Verified with FFmpeg 9.0.1 on 26 August 2026. Search intent: ffmpeg video to gif.
ffmpeg -ss 2 -t 4 -i input.mp4 -filter_complex "fps=15,scale=640:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 output.gifTakes 4 s starting at 2 s, drops to 15 frames per second, scales to 640 px wide, builds a 256-color palette from those frames and encodes the GIF with it. Output: 640x360, 60 frames, 4.0 s, looping forever. 1.4 MB on the test clip.
Flags, explained
- -ss 2 -t 4
- Start at 2 s, take 4 s. GIFs get heavy fast; keep them short.
- fps=15
- Frame rate of the GIF. 10 to 15 is the usual range; 30 doubles the size for little gain.
- scale=640:-1:flags=lanczos
- Width 640, height to match, Lanczos resampling for sharper downscaling. -1 is safe here because GIF has no even-size rule.
- split[s0][s1]
- Duplicate the frames so one copy feeds palettegen and the other paletteuse in the same graph.
- palettegen
- Analyse the frames and produce the best 256-color palette (max_colors lowers that).
- paletteuse
- Quantise every frame with that palette. dither=bayer:bayer_scale=5 swaps error diffusion for an ordered pattern that compresses better.
- -loop 0
- Loop forever. -loop -1 plays once, a positive number loops that many extra times.
Variants
Classic two-pass version (palette file on disk)
ffmpeg -ss 2 -t 4 -i input.mp4 -vf "fps=15,scale=640:-1:flags=lanczos,palettegen" palette.png
ffmpeg -ss 2 -t 4 -i input.mp4 -i palette.png -lavfi "fps=15,scale=640:-1:flags=lanczos[x];[x][1:v]paletteuse" -loop 0 output.gifSame result as the single command (byte-identical here), split into a palette pass and an encode pass. Useful when you want to reuse or edit the palette.
Smaller: 10 fps, 480 px, 64 colors, Bayer dithering
ffmpeg -ss 2 -t 4 -i input.mp4 -filter_complex "fps=10,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=64[p];[s1][p]paletteuse=dither=bayer:bayer_scale=5" -loop 0 small.gifEach knob cuts size: fewer frames per second, fewer pixels, fewer colors, and an ordered dither that compresses better than the default error diffusion. 402 KB against 1.4 MB for the same 4 s.
Quick and dirty, no palette
ffmpeg -ss 2 -t 4 -i input.mp4 -vf "fps=15,scale=640:-1" -loop 0 output.gifUses the encoder's generic palette. On our flat synthetic test pattern it was even smaller (833 KB), but on real footage it produces visible banding and speckle; the palette commands above exist for that reason.
Pitfalls we hit
- Size scales with frames x pixels x colors. A 640 px, 15 fps, 4 s GIF was 1.4 MB; the 480 px, 10 fps, 64-color version was 402 KB. Going to 30 fps or 1080 px produces multi-megabyte files.
- GIF has no audio. Anything you hear in the source is dropped silently.
- The single-pass command needs -filter_complex (not -vf) because the graph has two branches. Passing it to -vf fails to parse.
- Colors are limited to 256 per frame. Gradients and dark scenes band; the palette commands minimise it but cannot remove it. For real quality at a small size, an MP4 or WebM loop beats a GIF.
How we verified it
Run with FFmpeg 9.0.1 on 26 August 2026 against a 12 s, 1280x720, 30 fps H.264 file. Primary: gif, 640x360, 60 frames, 4.000 s, 1,417,600 bytes. Two-pass: identical size. Small variant: 480x270, 40 frames, 402,004 bytes. No palette: 640x360, 60 frames, 833,024 bytes on the synthetic pattern.
The same edit as a typed operation
convert_video_to_gif takes start, duration, fps and width, runs the palette pass for you and enforces the limits that keep GIFs sane (30 s, 160 to 1280 px, 5 to 30 fps). The filter graph never has to be written by hand or by an agent.
{
"inputs": [{ "id": "main", "url": "https://example.com/video.mp4" }],
"options": { "start": 2, "duration": 4, "fps": 15, "width": 640 }
}FAQ
Related commands
- ffmpeg trim videoTrim a video with FFmpegRead
- ffmpeg resize videoResize a video with FFmpegRead
- ffmpeg compress videoCompress a video with FFmpegRead