How to Make High-Quality GIFs Using FFmpeg
Generating standard GIFs with FFmpeg often results in severe color
banding and heavy pixelation because the GIF format is restricted to a
maximum palette of 256 colors. By default, FFmpeg relies on a generic,
static color palette that rarely matches the colors in your source
video. To create sharp, high-quality GIFs, you must use the
palettegen filter to extract a custom color palette
specifically tailored to your video frames, and then apply that palette
using the paletteuse filter.
The Problem with Default Conversions
Running a simple command like
ffmpeg -i input.mp4 output.gif creates poor results because
FFmpeg forces the video into an arbitrary 256-color map. The
palettegen and paletteuse filters solve this
via a two-stage process:
palettegen: Scans the input video, determines the 256 most dominant and visually significant colors across the frames, and generates a small PNG image containing this custom color map.paletteuse: Takes the original video and the generated palette, mapping the video pixels to the custom palette using advanced dithering algorithms to hide color transitions smoothly.
Method 1: The Single-Command Approach
You do not need to generate a physical palette file on your disk. You
can use FFmpeg's -filter_complex pipeline with the
split filter to generate and apply the palette in a single
run.
ffmpeg -i input.mp4 -filter_complex "[0:v] fps=15,scale=640:-1:flags=lanczos,split [a][b];[a] palettegen [p];[b][p] paletteuse" output.gifHow this command works:
fps=15: Reduces the frame rate to 15 frames per second to reduce file size.scale=640:-1:flags=lanczos: Resizes the width to 640 pixels, automatically calculates the proportional height (-1), and applies the high-quality Lanczos resampling filter.split [a][b]: Duplicates the resized video stream into two separate streams, labeled[a]and[b].[a] palettegen [p]: Sends stream[a]intopalettegen, creating the color palette stream labeled[p].[b][p] paletteuse: Combines stream[b](the video) and stream[p](the custom palette) to generate the final GIF output.
Method 2: The Two-Step Approach
If you prefer to inspect the generated palette or want granular control over multi-step processing, run the generation in two distinct steps.
Step 1: Generate the Palette
ffmpeg -y -i input.mp4 -vf "fps=15,scale=640:-1:flags=lanczos,palettegen" palette.pngStep 2: Render the GIF using the Palette
ffmpeg -i input.mp4 -i palette.png -filter_complex "[0:v] fps=15,scale=640:-1:flags=lanczos [x]; [x][1:v] paletteuse" output.gifFine-Tuning for Better Quality and Smaller File Sizes
Both palettegen and paletteuse support
configuration flags to optimize quality depending on the scene
content.
1. Handling Dynamic
Scenes with stats_mode
By default, palettegen calculates a single global
palette for the entire video clip (stats_mode=full). If
your video features drastic scene changes or varying color schemes, you
can generate a new palette for every single frame:
ffmpeg -i input.mp4 -filter_complex "[0:v] fps=15,scale=640:-1:flags=lanczos,split [a][b];[a] palettegen=stats_mode=single [p];[b][p] paletteuse=new=1" output.gifstats_mode=single: Recomputes the color palette for each frame.new=1: Tellspaletteuseto acquire a new palette for each frame. Note that this option significantly increases file size.
2. Controlling Dithering
Dithering blends colors to prevent banding, but it can introduce
background noise and bloat file sizes. You can adjust the dithering
algorithm inside paletteuse:
Bayer Dithering (Lower file size, crosshatch pattern):
paletteuse=dither=bayer:bayer_scale=3bayer_scaleranges from0to5. Higher values reduce noise and file size at the cost of slight banding.Floyd-Steinberg (Default, smoother gradients):
paletteuse=dither=floyd_steinbergNo Dithering (Smallest file size, best for flat graphics or screen recordings):
paletteuse=dither=none