DEV Community

AsyncMonk
AsyncMonk

Posted on

I tuned a demo GIF for an afternoon before checking if it could shrink at all

I spent most of an afternoon tuning a GIF that was never going to get smaller, and the only useful thing I got out of it was a table of what each setting actually costs. The demo animation at the top of my landing page was the single heaviest asset on first paint — heavier than all the scripts combined. I run this thing alone and I pay for bandwidth by the month, so every extra megabyte on the hero is a bill I hand myself once per visitor. I did not want a smaller file so much as I wanted to know which knob to turn.

Touching the live asset means re-deploying every time, so I drew my own sample instead: a fictional analytics dashboard, 1280x800, 40 frames at 100 ms each, four seconds of KPI numbers ticking up, bars growing, a pie chart unfolding and a highlight sweeping across. As a GIF that is 1,127,031 bytes. I drew a second one to test the knobs against — a fake admin screen recording, 960x600, 75 frames, with small table text, a clock ticking in the header and a blinking cursor. Both are hand-drawn fictional interfaces, not screenshots of any real product.

Four ways to shrink a screen-recording GIF and what each one costs. Self-made fictional UI animation; the interface text in the sample is Chinese. Each panel changes one setting on the same baseline output

On the screen-recording sample the baseline output was 983,087 bytes, and I changed exactly one thing per run. Dropping every second frame saved 28.8%, every third frame 42.5%, and the frames that survive are pixel-identical to the originals — nothing gets blurrier. Resizing to 70% throws away half the pixels and saves 12.3%, by which point the small table text has already gone soft; going down to 50% saves 48.5% but the text is unreadable. Trimming ten frames off the front and twelve off the end deletes nearly a third of the frames for 12.7%, because the frames you delete are the static ones that were already the cheapest in the file. And changing playback speed saves nothing at all: at 2x and at 0.5x the output was byte-for-byte identical to the baseline.

The speed one deserves a sentence of its own, because it is the trap in the whole exercise. A GIF has no global frame rate — every frame carries its own display duration, and total runtime is just those durations added up. Drop half the frames without lengthening the ones you keep and the animation plays twice as fast: my 4500 ms clip came out at 2280 ms. That is arithmetic built into the format, not a quirk of any particular tool, so any frame-dropping step has to be paired with a speed adjustment to get the original pacing back. The happy accident is that speed costs zero bytes, so putting the pacing back does not give up the 28.8%.

Resizing being such a bad deal took me longer to understand. GIF compresses with LZW, which only rewards runs of identical pixels along a single row, and a UI screenshot is compressible mainly because it is full of long flat horizontal runs. Interpolated scaling invents intermediate colours along every edge, which chops those runs into a mess of near-but-not-quite matches, and those new colours also eat into the 256-colour palette each frame gets. Half the pixels, but each pixel compresses much worse, and the two effects mostly cancel out.

Then I went back to the dashboard clip I actually cared about and none of it helped. Re-encoding it as a GIF saved 3%: 1.07 MB in, 1.04 MB out. The reason is that GIF's inter-frame diffing can only mark one rectangular dirty region per frame, and everything inside that rectangle is re-encoded in full. On the admin recording, 68 of 75 frames use a partial refresh and the dirty region averages 59% of the canvas, so there is real slack to recover. On the dashboard, with numbers changing top-left, bars growing in the middle and a highlight sweeping across, one rectangle has to swallow all of it: the dirty region averages 90.5%, which means the diffing is doing essentially nothing. That number is the thing I now check first.

So the order I had been working in was backwards. Look at what kind of picture it is, then pick the knob. Mostly-static recordings with small text: drop frames, fix the speed, and leave resizing for last. Busy animations where everything moves: stop tuning and change the container. The same dashboard clip exported as animated AVIF came out at 208,937 bytes — 1.07 MB down to 204 KB, 18.5% of the original, with the labels still legible. Animated WebP landed at 696 KB. APNG is lossless, so it went up 36%, which in hindsight is exactly what lossless should do to a file like this.

I ran the whole comparison in ImgIng's animation workshop (https://imging.ai/). Two practical reasons for that choice: the processing happens locally in the browser — I had the Network panel open for the entire run and there was not a single non-GET request, so a few dozen throwaway attempts cost me no traffic at all — and it exposes frame dropping, playback speed and per-frame duration as separate inputs, which is the only way the one-variable-at-a-time table above was possible. It also says so when it fails: the APNG attempt on the dashboard came back with a note that APNG is a poor fit for this kind of picture, and a button to re-run it as something else. Across all four exports the frame count, per-frame durations and the infinite loop flag matched the source file exactly, so switching formats did not mean re-checking the timeline.

The part I have not settled is compatibility. I have only confirmed that the AVIF output decodes and plays in three desktop browser engines; I have not tested it inside messaging apps, on older Android devices, or in embedded WebViews, and the AVIF output carries no alpha channel, so anything that needs a transparent background is out. My desktop hero is AVIF now and the mobile path still falls back to the GIF, which is not elegant, but I would rather ship the ugly fallback than find out the hard way. If you only take one number away from this: measure what fraction of the canvas is actually changing before you touch any of the settings.

Top comments (0)