DEV Community

NullPointerZen
NullPointerZen

Posted on

Your GIF didn't band because you compressed it too hard

A colleague sent me a gradient banner and said my compression tool had wrecked it.

I pushed the quality slider to maximum and re-ran it. The banding was still there. That's when I realised we'd both been looking at the wrong control.


The rule nobody reads

Every GIF frame carries a palette of at most 256 colours. Every pixel in that frame has to land in one of those slots.

Here's the way I explain it now: you've been handed a box of 256 pencils and asked to copy a photograph. The sky might go through a thousand shades of blue, but you own five or six blue pencils — so you pick the nearest one each time. The smooth gradient comes back as a block of dark blue, a block of mid blue, a block of light blue, with the steps between them clearly visible.

That's banding. It isn't "compressed badly", it's "ran out of pencils".

And here's the part that matters: how many pencils you have is unrelated to how hard you press. The quality parameter controls pressure — how much detail the encoder discards. The palette controls how many pencils exist. Max out quality and you still have those same 256.

Four runs at default settings

I built four clips with deliberately different visual properties and ran them all through ImgIng's animation workshop (imging.ai) on defaults. I picked that tool because the frame work runs in the browser — I had the Network tab open across six files and a dozen-plus exports and saw zero POST requests, which you can reproduce yourself rather than taking my word for it.

Footage Size · frames Auto-selected palette Size change
Spinner (three colours) 120×120 · 8 16 colours 2.0 KB → 1.5 KB
UI screen recording (flat palette) 720×405 · 36 64 colours 79.8 KB → 21.3 KB
Gradient banner 480×270 · 20 224 colours 425.2 KB → 391.6 KB
Full-frame noise 480×320 · 24 256 colours 2.62 MB → 2.63 MB

The palette isn't a fixed setting — it's allocated against what the footage actually needs. Three-colour spinner: 16 is plenty. Flat UI: 64 covers it. Gradient banner: 224 and still tight. Noise: maxed out and still short.

The banding threshold sits on row three. 224 is close to the ceiling, but a continuous gradient needs far more than that, so steps are guaranteed. And look at the size column — it only dropped 7.9%. You paid the visual cost and got almost none of the benefit. That's the worst outcome of the four, and it's the one people actually hit, because gradient banners are everywhere.

So why does lowering quality seem to cause it

Because in a lot of tools those controls are wired together. Drag quality down and the tool quietly reduces the palette along with it. You observe "compressed harder → more banding" and read it as cause and effect.

The real chain is: fewer colours → banding. Quality was just the thing that happened to move the palette.

Easy to verify: find a tool that exposes the colour count on its own. Leave quality at maximum, take the palette from 256 to 64, and watch it band anyway.

Dithering, and when to leave it off

After reducing colours, an encoder can scatter small amounts of noise across neighbouring pixels so the steps break up visually. That's dithering.

The cost is concrete: dithering destroys frame-to-frame consistency. Large areas that were pixel-identical between frames — and therefore free, thanks to differencing — get perturbed into slightly different values. Differencing stops working and the file grows.

Rule of thumb I've settled on: dither at low palettes, don't at high ones. Low palettes have obvious banding and a lot to gain; high palettes band mildly and you'd be trading away differencing for very little.

What to actually do

In this order. Don't start by touching parameters.

  1. Classify the footage first. Flat colours, UI, icons, line art → palette reduction is nearly free. If it bands, the tool set the colour count too low; raise it.
  2. Gradients, live action, noise → change format. This material is going to band in GIF no matter what. Animated WebP and APNG have no 256-colour ceiling.
  3. Stuck with GIF anyway (because it has to play somewhere that only takes GIF)? Then accept a trade: tolerate the banding, or shrink dimensions so it's less visible. Two options, pick one.
  4. Stop expecting the quality slider to fix banding. Covered above — no causal link.

Limits

The four numbers come from footage I generated to sit at the extremes, so they won't predict what palette your clip gets — same category of footage with a different colour scheme or size lands somewhere else entirely. The mechanism is the transferable part: palettes are allocated to need, and banding shows up where need exceeds allocation.

Also, that automatic allocation is this tool's behaviour, not a universal one. Plenty of tools just hand every frame 256 colours. If what you see elsewhere doesn't match these numbers, check whether it's picking palettes at all.

Top comments (0)