Convert a video to GIF
Turns any video into a high-quality animated GIF in one ffmpeg pass, generating a custom color palette instead of the default dither.
Last updated
ffmpeg -i input.mp4 -vf "fps=10,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" output.gif
How it works
GIF is limited to 256 colors total, and ffmpeg's default conversion uses a generic palette that causes visible banding and dithering on anything but flat cartoon colors. The split filter duplicates the scaled video stream into two copies: one feeds palettegen, which builds an optimal 256-color palette from the ACTUAL frames of this clip, and the other feeds paletteuse, which re-encodes the video against that palette — all in a single ffmpeg invocation instead of writing a palette.png file and running ffmpeg twice.
fps=10 drops the frame rate before anything else runs, since GIFs rarely need film frame rates and every extra frame inflates file size; scale=480:-1:flags=lanczos resizes to a manageable width (auto height, keeping aspect) using the Lanczos algorithm, which holds up much better than the default bilinear scaler when the source shrinks a lot.
Watch out for
- →Even with an optimized palette, GIF is still capped at 256 colors per frame — smooth gradients and photographic content will still show some banding compared to the source video; that's a format limit, not a settings problem.
- →Long or high-resolution source clips produce huge GIFs fast. Trim the input first with -ss/-t, or drop fps further (e.g. fps=8) before reaching for a different container like WebP or a short muted MP4.
- →-1 preserves the aspect ratio with no rounding constraint, so it can produce an odd height — fine for GIF, which has no chroma subsampling. Use -2 whenever the output is a real video codec, since those require even dimensions.
Part by part
- -i input.mp4
- The input file. Everything before it configures the demuxer; everything after configures output.
- fps=10
- Drops the frame rate to ten. GIF has no interframe compression, so every frame you keep costs full size — this is the single biggest lever on the result.
- scale=480:-1
- Sets width to 480 and lets height follow the aspect ratio. The -1 is what stops you doing the arithmetic yourself.
- flags=lanczos
- A higher-quality scaling filter than the default. It costs almost nothing here and visibly reduces the softness of downscaled text.
- palettegen
- Builds an optimal 256-colour palette from the whole clip. GIF is limited to 256 colours, and the default palette is a generic one.
- paletteuse
- Applies that palette on a second pass. The split filter feeds the same stream into both stages, which is why this runs in one command instead of two.
Common mistakes
ffmpeg -i input.mp4 output.gifIt works, and the result is two to five times larger with visible colour banding, because it uses a fixed generic palette and keeps the source frame rate. The palette pass is the whole reason this recipe is long.
ffmpeg -i input.mp4 -vf "scale=480:-1" output.gifScaling to an odd height can fail with some filters, and without fps= the frame count is unchanged, so the file stays enormous. Use -2 rather than -1 when a codec requires even dimensions.
ffmpeg -i input.mp4 -vf "fps=30,scale=1280:-1" output.gifThirty frames a second at 1280 wide produces a GIF that can exceed the source video's size. If you need that fidelity, the honest answer is that you do not want a GIF.
Questions
Why is the output still huge?
Almost always frame rate or width. Drop fps to 8 or 10 and width to 480 before anything else — those two multiply, so halving each is roughly a quarter of the size. Trimming the clip shorter beats every other optimisation.
Should I be using a GIF at all?
Often not. An MP4 or WebM of the same clip is typically five to ten times smaller and plays everywhere, and modern chat and issue trackers accept video. GIF is worth it only where autoplay-without-controls is required.
What does the split filter actually do?
It duplicates the video stream so one copy generates the palette and the other is encoded with it. Without it you would have to write a temporary palette file and run ffmpeg twice.
Can I loop it a fixed number of times?
Yes, with -loop before the output: 0 loops forever, -1 plays once, and a positive number repeats that many times. The default for GIF output is infinite looping.