You run ffmpeg -i clip.mp4 out.gif and the result looks like it was painted with a sponge.
Gradients turn into stripes and dots. The file is also huge.
Here is why that happens, the fix, and the real file sizes I measured. I also cover what to use when you do not want a terminal.
Why a naive GIF looks bad
Three things work against you.
256 colors. A GIF frame can hold at most 256 colors. A video frame can show millions. Something has to pick which 256 survive.
A generic palette. By default ffmpeg uses one fixed palette for the whole file. It was not built for your clip, so a blue sky or an orange sunset gets squeezed into colors that were chosen for nothing in particular.
Dithering. To fake the missing colors, the encoder mixes dots of nearby colors. That gives the grainy texture you see on cheap GIFs.
Frame rate matters too. A GIF stores every frame as a full picture, so 30 fps at full size gets very big very fast.
The naive command
ffmpeg -i input.mp4 output.gif
On my 4 second, 640x360, 30 fps test clip this made a 1,828 KB file. The source MP4 was 405 KB.
The palette trick
The fix is to build a palette from your own clip first, then use it. One command does both passes:
ffmpeg -i input.mp4 \
-vf "fps=15,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
-loop 0 output.gif
What each part does:
-
fps=15keeps 15 frames a second. Try 10 for slow scenes. -
scale=480:-1:flags=lanczosmakes it 480 px wide and keeps the ratio. Lanczos keeps edges sharp. -
splitsends the frames two ways: one copy builds the palette, the other gets painted with it. -
palettegenpicks the best 256 colors for this clip. -
paletteuseapplies them, with dithering. -
-loop 0loops forever.-loop -1plays once. I checked both: with-1the file has no loop block at all.
I ran every command in this post on ffmpeg 8.0.1.
Does it help? Two test clips
I made two 4 second clips with ffmpeg itself. One is a busy test pattern. One is a smooth color gradient, the hardest case for GIF.
On the gradient clip at 15 fps and 480 px, I compared each GIF to the video frames with ffmpeg's SSIM filter (1.0 means identical):
| Gradient clip, 15 fps, 480 px | Size | SSIM |
|---|---|---|
| Naive (default palette) | 1,952 KB | 0.60 |
| Palette trick | 3,565 KB | 0.99 |
The naive one shows visible banding and dots. The palette one looks like the video.
Look at the size, though. The palette version was bigger. Better colors cost bytes. If you only want a small file, cut fps, width or length instead of the palette.
Sizes compared
Test pattern clip, 4 seconds, source 640x360 at 30 fps:
| Settings | Naive | Palette trick |
|---|---|---|
| 10 fps, 320 px | 223 KB | 362 KB |
| 10 fps, 480 px | 477 KB | 727 KB |
| 15 fps, 320 px | 323 KB | 544 KB |
| 15 fps, 480 px | 694 KB | 1,083 KB |
Two lessons:
- Width and fps move the size far more than the palette does. Going from 15 fps at 480 px to 10 fps at 320 px cut the palette file from 1,083 KB to 362 KB.
- The palette costs about 50 to 70 percent more bytes on this clip, and it buys cleaner color. On this busy clip SSIM went from 0.91 to 0.95 at 15 fps and 480 px.
These are numbers from synthetic clips. Real footage will give different sizes, so run your own.
gifski
gifski is a dedicated GIF encoder. Its site says it makes smooth GIFs with thousands of colors per frame. It is not installed on my machine, so I did not test it. If banding still bothers you after the palette trick, it is worth a try.
No terminal?
You do not have to install anything.
ezgif is a long-standing browser converter. Its page today says: up to 200 MB per file, up to 60 seconds of video at 5 fps (30 seconds at 10 fps), and many input formats including MP4, WebM and MOV. You set start, end, size, frame rate and loop count, and pick one of five encoding methods.
There are several good converters like it. Pick one whose limits fit your clip.
I use Ziztu's video to GIF tool for quick jobs. Here is why, straight from its page:
- It is free, with no account and no card.
- It takes MP4, MOV or WebM up to 100 MB and 30 minutes.
- You choose the start second and a length of 1 to 15 seconds.
- Width is 320, 480 or 720 px. Frame rate is 10, 15 or 20. Loop forever or play once.
- It builds a 256-color palette from your clip and dithers, which is the same idea as the command above.
- It shows the file size of the result before you download, so you see right away if it is too big.
- It runs ffmpeg on its server and deletes your video as soon as the GIF is made.
The limits: 15 seconds of GIF, and 10 GIFs an hour.
Your video does go to their server over HTTPS, so for anything private, use the local ffmpeg command instead.
Quick recipe
- Short clips:
fps=15,scale=480:-1, palette trick. - Smaller file:
fps=10,scale=320:-1. - Anything over about 5 seconds: think about an MP4 instead. My 4 second pattern clip was 405 KB as an MP4 (640 px, 30 fps) and 1,083 KB as a GIF (480 px, 15 fps).
Happy converting.
This post was written with AI assistance.
Top comments (0)