FFMPEG COMMAND

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.

the command
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.gif

Takes 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)

shell
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.gif

Same 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

shell
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.gif

Each 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

shell
ffmpeg -ss 2 -t 4 -i input.mp4 -vf "fps=15,scale=640:-1" -loop 0 output.gif

Uses 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.

MCP tool: convert_video_to_gifPOST /api/v1/tools/convert-video-to-gif
request body
{
  "inputs": [{ "id": "main", "url": "https://example.com/video.mp4" }],
  "options": { "start": 2, "duration": 4, "fps": 15, "width": 640 }
}

FAQ

Why is my GIF so big?

Every frame is stored almost fully, so size grows with frames per second, pixel count and duration. Cut the clip, drop to 10 to 15 fps, scale to 480 or 640 px and reduce colors before anything else.

Why does my GIF look grainy or banded?

You skipped the palette. The default encoder maps colors to a generic palette with heavy dithering. palettegen plus paletteuse builds a palette from your own frames, which removes most of it.

How do I make the GIF loop or play once?

The -loop output option: 0 loops forever (the default for the GIF muxer), -1 plays once, a positive number repeats that many extra times.

Should I use a GIF at all?

For chat and documentation, yes, because it embeds everywhere. For a web page, an MP4 or WebM in a muted, looping video tag is many times smaller at far better quality.

Related commands

All FFmpeg commands