Skip to main content
Filesvo

How to Convert MP4 to GIF with FFmpeg

To convert an MP4 to a GIF with FFmpeg properly you need two passes, not one: the first builds a colour palette from your own video, the second encodes the GIF using it. The single-line command everybody copies skips the palette entirely and falls back to a fixed palette that is the same for every video ever made, which is why the result comes out banded. The tool below runs the two-pass version in your browser, so nothing is uploaded and there is nothing to install.

Convert your MP4 to a GIF

or drag and drop a .mp4 / .m4v file here — up to 100 MB

This tool runs the open-source FFmpeg engine in your browser. First use downloads it once (a 32 MB file, roughly 10 MB compressed in transit); after that it loads from your browser cache. Your files are never uploaded. The engine is ffmpeg.wasm, distributed unmodified under the GPL-2.0-or-later.

Processed on your device — files are never uploaded to Filesvo.

FFmpeg's default GIF palette gives blue only four levels

A GIF frame can hold at most 256 colours. That limit is the format, not FFmpeg, and there is no way around it. What FFmpeg decides is which 256 — and with no palette supplied it uses a fixed cube of 8 red levels × 8 green × 4 blue, evenly spread across the whole colour space regardless of what is actually in your video. Those numbers were read out of the palette of a GIF produced by FFmpeg 9.0.1, not recalled: red and green step 0, 36, 72, 108, 144, 180, 216, 252; blue has 0, 85, 170, 255 and nothing in between.

Four blue levels is the whole story. Measured across five test clips, the one-pass GIF was wrong about blue by 18.46 out of 255 on average, against 9.32 for red and 6.94 for green — so blue is off by nearly three times as much as green. That is why skies, water, dusk shots and dark blue interfaces are the ones that come out visibly striped while a green field looks acceptable.

Building the palette from the video instead removes the problem rather than reducing it: the same clips came out using a median of 255 distinct colours against 128, with average colour error falling from 11.58 to 0.64 out of 255. The cost is file size, which is covered below — it is real, and it is worth it.

One pass against two, measured

Five two-second clips — a gradient sweep, SMPTE colour bars, a Game-of-Life pattern, a Mandelbrot zoom and a test pattern — each converted both ways at 10 fps and 480 px wide with FFmpeg 9.0.1, then compared frame by frame against the source frames. Figures are medians across the five. They are synthetic clips, chosen so the numbers are reproducible; your own footage will land somewhere inside this range rather than outside it.

What was measuredOne pass → two passes
Distinct colours in the finished GIF128 → 255
Average colour error, all channels (of 255)11.58 → 0.64
Red channel error9.32 → 0.78
Green channel error6.94 → 0.46
Blue channel error18.46 → 0.68
Blue levels the palette can offer4 → up to 256
Where the palette comes fromfixed cube → built from your video
File size1.00 → 1.83× (median)

The size penalty is a median, not a rule. The colour-bars clip went the other way — 11.2 KB with the palette against 15.5 KB without it — because a palette matched to a handful of flat colours reproduces them exactly and leaves nothing to dither. Footage with smooth gradients pays the most; footage with flat areas can pay nothing at all.

The commands, and the one people copy

The command that circulates is a single line: ffmpeg -i in.mp4 -vf "fps=10,scale=480:-1:flags=lanczos" out.gif. It works, it is fast, and it produces the banded result described above, because nothing in it tells FFmpeg which colours the video contains.

The correct version is two commands. First build the palette: ffmpeg -i in.mp4 -vf "fps=10,scale=480:-1:flags=lanczos,palettegen" palette.png. That writes a 16×16 PNG — 256 pixels, one per colour, a few hundred bytes — holding the palette chosen for your video.

Then encode against it: ffmpeg -i in.mp4 -i palette.png -filter_complex "fps=10,scale=480:-1:flags=lanczos[x];[x][1:v]paletteuse" out.gif. The filter chain has to be repeated identically in both commands — the palette is built from the frames after scaling, so a palette generated at one size and used at another describes colours that are no longer there.

Those are exactly the two passes the tool on this page runs, with the frame rate and width you choose fed into both. If you would rather not install a 100 MB toolchain to make one GIF, that is the point of it.

Getting the size down without going back to banding

GIF has no motion compression worth the name: every frame is stored close to whole, so the file grows almost linearly with the number of frames. Frame rate is therefore the strongest control you have, and the cheapest — going from 15 fps to 10 fps removes a third of the file and is barely perceptible on most footage.

Width is the next one, and it compounds: halving the width quarters the pixels. A GIF that will be viewed inside a chat window or a README does not need to be 640 px wide. Length is the third — trim before converting rather than after, because there is no way to remove a second from a finished GIF without re-encoding it.

Reach for those three before reaching for the palette. Dropping back to one pass to save space is a bad trade: it buys roughly a third off the file and costs an eighteen-fold increase in colour error. If the file is still too big after trimming, trim the video first and convert the part you actually need.

When a GIF is the wrong format entirely

A GIF is usually the largest possible way to send a short video. The same clip as an MP4 is routinely five to ten times smaller, carries real colour rather than 256 of them, and plays everywhere — GitHub, Slack, Discord, Twitter and every browser autoplay a muted looping MP4 exactly as they would a GIF.

Use a GIF when the destination genuinely cannot take video: an email client, an old forum, a documentation build that only accepts images, or a context where the file must animate with no player. Everywhere else, convert the GIF back to MP4 and send that instead — the saving is usually dramatic.

If the source is a long recording rather than a clip, compressing the video will beat any GIF you can make from it, at any frame rate.

Why this runs in a browser at all

FFmpeg here is the real FFmpeg, compiled to WebAssembly and downloaded once into the page — about 32 MB, roughly 10 MB over a compressed connection — then cached. It runs on your device, so the video is never uploaded, never queued behind anyone else's job and never stored.

The trade is speed. WebAssembly runs slower than a native build and gets one browser tab's worth of memory, so a two-minute 4K source will be slow and may not finish at all. For the short clips GIFs are made from, the difference is small enough not to matter.

It also means the two passes are not optional or hidden behind a quality setting. The palette is generated and applied every time, because the alternative is a result that looks wrong for a reason nobody would guess from looking at it.

Frequently asked questions

Why does my GIF look banded or striped when the video looked fine?
Because a GIF holds 256 colours and, with no palette supplied, FFmpeg uses a fixed cube with only four levels of blue. Skies, water and dark interfaces band first. Generating a palette from the video with palettegen and applying it with paletteuse fixes it.
What does palettegen actually do?
It reads the whole video, counts which colours actually occur, and writes the best 256 of them into a 16×16 PNG. The second pass then encodes the GIF using that palette instead of the generic one, so the colours available match the colours present.
Do I have to repeat the fps and scale filters in both commands?
Yes. The palette is built from the frames as they exist after scaling and frame-rate reduction. Generating it from full-size frames and applying it to scaled ones describes colours that resampling has already changed.
Why is the two-pass GIF bigger than the one-pass GIF?
An accurate palette plus dithering produces more local variation, which compresses worse than flat banded areas. Across five test clips the median was 1.83 times larger, though one clip came out smaller. Lower the frame rate or the width to recover the difference.
Should I just use an MP4 instead?
Almost always, yes. GitHub, Slack, Discord and every modern browser autoplay a muted looping MP4, and the file is typically five to ten times smaller with full colour. Use GIF only where video genuinely cannot go, such as email.
Is my video uploaded to a server?
No. FFmpeg is compiled to WebAssembly and runs inside this page on your own device. The video is read from disk by the browser, converted locally, and the GIF is handed straight back to you.