Three levers, and nothing else to turn
A GIF is not a video file wearing a different extension. The format stores every frame as an indexed-color bitmap with LZW compression layered over it, and it has no motion estimation, no inter-frame prediction and no bitrate control of any kind. Everything a modern codec uses to make a file small is simply absent.
That leaves exactly three numbers you can change, and this tool sets all three from a single Compression preset dropdown, with Light leaving the width alone and moving only the other two:
- Light keeps the original width, resamples to 15 fps and builds a 192-color palette.
- Balanced (recommended) drops to 12 fps, caps the width at 480 px and the palette at 128 colors.
- Aggressive goes to 8 fps, caps the width at 320 px and the palette at 64 colors, which is where the file gets genuinely small and the motion gets genuinely choppy.
Frame rate is usually the biggest single win, because it removes whole frames rather than shaving bits off each one. Palette size is the one people forget, and it is often the reason a flat, screen-recorded GIF shrinks further than expected.
Inside the two-pass palette that does the work
The module builds one FFmpeg filter graph and passes it as a single -filter_complex argument. Written out, the balanced preset produces fps=12,scale=min(iw\,480):-1:flags=lanczos,split[a][b];[a]palettegen=max_colors=128[p];[b][p]paletteuse. The backslash in there is not a typo: the comma inside min() has to be escaped or FFmpeg reads it as the separator between two filters.
Reading that left to right: the stream is resampled to a constant frame rate, downscaled with the Lanczos filter, then split into two identical branches. One branch is analysed by palettegen, which surveys the whole clip and picks the best 128 colors for it. The other branch waits, then gets those 128 colors applied by paletteuse. It is a two-pass technique running inside one command, and it is why the output does not suffer the muddy, dithered look you get from letting the GIF encoder fall back on a generic web-safe palette.
Wrapping the width in min() is what makes this downscale-only. A GIF already narrower than the cap keeps its own width, so nothing is ever blown up to meet the preset. The height is left as -1, meaning FFmpeg computes it from the aspect ratio, which is why only the width is genuinely bounded.
Compressing a GIF, start to finish
- Drop your file on the box above, or use Choose a file. Only
.gifis accepted here, and anything else is rejected before processing starts. - Set Compression preset. Start at Balanced (recommended) unless you already know you are fighting a hard upload limit.
- Click Compress GIF. The progress bar tracks the FFmpeg run, and Cancel stops it and clears the box.
- Read the before and after sizes printed above the download, then click the download button. If the saving is not enough, click Process another and try the next preset down.
Choosing a preset by where the GIF is going
A GIF headed for a chat bubble or a forum reply is being displayed at a few hundred pixels wide no matter what you upload, so Balanced costs you nothing visible and saves a lot. A GIF that is the actual subject of a post, like a UI walkthrough where the reader needs to see small text, is worth keeping at Light and solving the size problem another way.
If the clip started life as video, it is usually better to go back to the source than to compress the GIF. Converting the MP4 to GIF with its own frame rate and width controls gives you one lossy step instead of two stacked on top of each other. Trimming the framing helps more than most people expect too, since dead space still costs palette entries and pixels: the GIF cropper does that with a visual selection box across every frame. And a clip that is simply too long reads fine at double speed, which the GIF speed changer does by rescaling every frame’s timestamps, then rebuilding the palette the same way this page does.
When you get your original file back
Re-encoding does not always shrink a GIF. If your source is already at 8 fps and you pick Balanced, the fps=12 filter will duplicate frames to reach 12, and the result can be larger than what you started with. The module checks for exactly that: after the encode it compares the two sizes, and if the new file is not smaller it returns your original bytes instead.
The download is still named with a -compressed suffix, so the honest way to read the result is the size line, not the filename. Identical numbers mean the guard fired and this method has nothing left to give on that file.
The GIFs this cannot rescue
Photographic content is the hard case. A GIF of real video footage, with gradients and film grain, is being forced through a 256-color ceiling that was never designed for it, and dropping to 64 colors turns smooth skies into visible bands. No preset here fixes that, because the constraint is the format.
Long GIFs are the other one. Frame count multiplies everything, and a thirty-second loop at any watchable frame rate is a large file by construction. If the destination will take video at all, turning the GIF into an MP4 puts the same animation on a codec that can encode motion rather than store every frame whole, and you can squeeze the result further with the MP4 compressor. The rest of the routes are in the video tools section. Keep the GIF only when the platform genuinely requires one.