utilime

Small tools that finish the job.

Compress a GIF

Three levers, each giving up something different, with the numbers for choosing between them.

Drop a file hereor click to choose · it stays on your device

How it works

Four numbers decide how large an animated GIF is: how many pixels wide the picture is, how many frames it holds, how many colours the palette carries, and how much of the picture changes from one frame to the next. The first three are controls on this page. The fourth is a property of the footage rather than a setting, and it is the one that decides which of the other three is worth pulling.

Palette size moves the number gently

Cutting the palette is close to linear in the bytes it saves. Measured on a 200×150 gradient with everything else held fixed: 8 colours produced 1,121 bytes, 16 produced 1,849, 32 produced 2,459, 64 produced 3,864, 128 produced 5,494 and 256 produced 8,291. Each halving takes off around a third, so this is a fine adjustment rather than a rescue.

Where it costs nothing is flat material. A recording of an interface — a toolbar, a form, a chart — often held about thirty distinct colours to begin with, so 32 is indistinguishable from 256 and the file is a third of the size. Where it costs a great deal is anything with a gradual tonal ramp across it. Skin bands first, in blotches across a cheek, and once banding appears no other setting hides it.

Scale is quadratic, so try it first

Halving the width halves the height with it, which removes three quarters of the pixels. No other control on this page moves the size that far, and it is the one to reach for before the others. The practical limit is legibility rather than appearance: text in a screen recording stops being readable somewhere below 75%, and a caption baked into the picture is usually what decides how far the scale can go.

Resampling here is an area average, described in the code as a box filter: each output pixel is the alpha-weighted mean of the source pixels underneath it. That choice is not only about sharpness. LZW pays for long runs of one palette index, and averaging a block of noisy pixels into one flat pixel manufactures those runs, so a downscaled GIF usually compresses better per pixel than the original did.

The three levers against each other, on one real file

Those gradient figures isolate one variable. On real footage the levers interact, so here is one public file measured through this page. Wikimedia's "Rotating earth (large)" is 1,001,718 bytes holding 44 frames of 400×400. At 200×200 and 256 colours it comes out at 352,259 bytes. At the full 400×400 and only 16 colours, a far uglier setting, it is 473,103.

Half the width beat a sixteenth of the palette and looked better doing it, which is the ordering this page argues for, in one comparison anyone can repeat. Keeping every third frame at full size gave 326,854 bytes. Combining all three — 132×132, 32 colours, every third frame — gave 35,681, or 3.6% of what went in.

One result deserves warning about. Left at 400×400 with 256 colours, that same file comes back at 1,071,674 bytes: 7% larger than it arrived. The source was carefully optimised before it was published, and a fresh global palette is not always a better fit than the one its author chose. The size delta on the result panel reports increases as readily as decreases, so this is never something to discover later.

Dropping frames is linear, and it shows

Keeping every second frame removes half the frames and close to half the data. The delays of the dropped frames are added to the frame that is kept, so a clip that ran 3.96 seconds still runs 3.96 seconds and the animation does not speed up — a mistake worth naming, because a decoder given the original delays on half the frames plays the whole thing at double speed.

What is actually lost is temporal resolution. A slow pan, a fade, a progress bar creeping across a screen: every second frame is enough and nothing looks wrong. A hand waving, a cursor flicking between windows, a cut between shots: the result stutters, and the stutter draws more attention than the file size ever did. Motion is the test, not duration.

The saving that is not a control

The largest single saving on this page has no setting attached to it. GIF has no way to describe a frame as a prediction of the frame before it; there is nothing in the 1987 specification resembling a motion vector. The only inter-frame compression available is to draw less. Each frame is written as the bounding box of the region that changed, and pixels inside that box that did not change are set to the transparent index so the frame beneath shows through.

The effect decides everything on the right footage. Ten frames of 160×120 in which a single 10×10 square moves encode to 1,595 bytes in total, 160 bytes a frame. The same ten frames with every pixel changed every time encode to 47,318 bytes. Identical dimensions, identical palette, identical encoder: a factor of about thirty, settled entirely by how much of the picture stood still.

That gap explains why a screen recording collapses to almost nothing and a clip off a phone refuses to. A recording of a window is a static background with a cursor moving over it. Handheld footage carries camera shake, which shifts every pixel in the frame by some fraction, so there is no unchanged region to skip and no long runs left for the compressor. When a GIF from a camera will not shrink, the levers here are all there is, and converting it to video is usually the better answer.

Reading the result

The line under the download reports the frame count, the output dimensions, the palette size and the running time, so what the settings gave up is visible without opening the file. The running time is the one to check against the source: if the frame arithmetic had gone wrong, the duration would be the first place it appeared.

The file stays on this machine. There is no GIF encoder in any browser, so the format is implemented in JavaScript that ships with this page, and the decode, the resample and the re-encode all run in a Web Worker inside the tab.

Questions

Where does the compression actually happen?

On your own processor, in this tab. The way to satisfy yourself is to load this page, disconnect the machine from the network, and then compress a GIF. It still works, which is not something a service that uploads your file can do. Nothing carrying the image is sent anywhere at any point.

I turned everything down and it is still enormous. What now?

Look at whether the picture holds still. GIF's only inter-frame saving is skipping the part of the frame that did not change, so footage where every pixel moves — anything handheld, anything with a moving background, anything with film grain over it — gets none of it and pays full price for every frame. Scale is the only lever with real force left in that case, and past a certain point the honest move is to convert the animation to MP4, where motion between frames is what the codec is built to describe.

Does keeping every second frame make the animation run faster?

No. When a frame is dropped its delay is added to the frame that is kept, so the total running time is preserved and the animation plays at the same speed with a lower frame rate. Checked by decoding the tool's own output and summing the delays: a 44-frame source at 90 ms a frame totals 3,960 ms, and at every third frame the output is 15 frames totalling 3,960 ms.

Should dithering be on?

Usually not, which is why it sits under the fine-tuning disclosure and defaults to off. Floyd–Steinberg error diffusion scatters the quantisation error into neighbouring pixels, and that scattering is exactly what LZW cannot compress. Measured on a 200×150 gradient at 256 colours it took the file from 8,291 to 17,674 bytes, an increase of 113%, while mean channel error moved from 4.20 to 4.87 — larger and, on that measure, slightly worse. It earns its cost only where the eye can see banding at a small palette, which the mean error does not capture.