Green Screen Remover for GIF: Why Soft Edges Turn to Dashes

A GIF stores one transparent colour, not an alpha channel. Measured on a 4-pixel test matte, 6,535 of 10,134 soft pixels disappear — and the browser keyer had already hardened most of the rest. What to export instead, with the exact commands.

Short answer: a GIF stores one fully transparent colour, not an alpha channel, so each edge pixel is either on or off. Against a perfect 4-pixel matte, 6,535 of 10,134 soft pixels vanish and 3,599 harden. Keying at 22%/3% first leaves only 468 partial pixels, so most of the jagged hair is the keyer, not the GIF.

What a GIF can and cannot store

A GIF is an indexed image: at most 256 colours, with one palette entry reserved as the transparent colour. There is no alpha channel. Every pixel is either fully opaque or fully transparent. A chroma key produces the opposite: a band of intermediate alpha wherever hair, motion blur or defocus mix subject and backdrop. That band is exactly what a GIF throws away.

The keying maths is not the problem. The same pixels that key cleanly to a PNG cannot be written as a GIF without snapping each one to 0 or 255. The table below compares the four formats people actually use for a transparent loop, all built from the same eight keyed frames on this machine.

FormatAlphaColoursMeasured size, 8 frames
GIF1-bit: one transparent index256 max34,822 bytes
APNG8-bit per pixel24-bit colour102,704 bytes
WebM (VP9)8-bit per pixel24-bit colour (YUV + alpha)17,288 bytes
PNG sequence8-bit per pixel24-bit colour158,908 bytes (8 files)

All four: 480×360, 12 fps, 8 frames, same keyed RGBA input (gif-keyed-0…7.png). GIF was encoded with palettegen (256 colours, reserve_transparent) and alpha_threshold=128; APNG with -f apng; WebM with libvpx-vp9, yuva420p, CRF 30. FFmpeg 9.0.2 on this machine.

The test: a 4-pixel ramp, then a 3% key

The test clip is deliberately ordinary: a 480×360 frame with a four-pixel defocused body edge, 26 hair strands with a 1.2-pixel feather, a 38%-opaque glass pane, and a green backdrop that falls off 22% from left to right. Each synthetic frame is built from a ground-truth alpha map, so the edge a soft format could keep is known pixel by pixel.

Against that ground truth, 10,134 pixels (5.86% of the frame) are partial: alpha above 0 and below 255. That is the edge a GIF cannot represent. But the measurement has two stages, and they are usually conflated. Stage one is the browser keyer at key #00b140, tolerance 22%, softness 3%. Stage two is the container. Running both, in that order, is what a visitor to this site actually does.

Stage one is the surprise: the keyer resolves 9,666 of the 10,134 truth-soft pixels to 0 or 255 before any GIF exists. Only 468 are still partial when the encoder sees them. A 3% softness band is 0.03 of the normalised colour distance — a narrow ring near the backdrop, not a four-pixel ramp.

Three close-ups of a hair edge on a dark background. Left: the ground-truth soft matte. Middle: the same matte snapped to GIF 1-bit alpha, where many strands break into dashes. Right: the browser keyer at 22%/3%, where the strands stay solid but the edge is stepped and green.
Left: the ground-truth matte, 10,134 partial pixels. Middle: that matte snapped to GIF's 1-bit alpha — 6,535 pixels vanish and strands break into dashes. Right: the browser keyer at 22%/3% before the GIF step — only 468 partial pixels remain, so the strands stay solid but the body edge is stepped and carries a green rim.
MattePartial alphaForced transparentForced opaqueGreen-excess fringe
PNG, ground truth10,134——899
Ground truth → GIF06,5353,599899
PNG, browser keyer 22%/3%468——215
Keyer output → GIF0219249215

The two pairs answer different questions. 6,535 is the container's ceiling: what GIF costs a matte that really carries a four-pixel ramp. 468 is what this pipeline hands the encoder after the keyer has already hardened the edge. Against the ground truth, 64.5% of the soft band becomes a transparent hole and 35.5% becomes a hard opaque edge; the green-excess fringe count does not change, because snapping alpha never changes the stored colour.

That is the practical lesson. If hair looks ragged after you key at 22%/3%, the keyer did that; the GIF only finished the job. If you start from a soft matte, the GIF is the destructive step and no setting saves it. Set the key first, then choose the container — not the other way round.

Palette damage is secondary, but it is real

GIF's other limit is the 256-colour palette. It does not break the key the way 1-bit alpha does, because the green backdrop usually survives quantization as a distinct cluster. Re-keying the 256-colour GIF kept 3,175 of the 3,195 edge pixels the untouched PNG kept — a 0.6% loss. The palette was applied to the unkeyed frame and the decoded result was keyed again, so this table isolates quantisation from the 1-bit alpha step.

PaletteDitherGreen-excess edge pixelsMean alpha error (0–255)File size, frame 0
256 coloursnone4834.4230,075 bytes
128 coloursnone4174.4126,954 bytes
64 coloursnone5314.4623,373 bytes
32 coloursnone04.5714,776 bytes
16 coloursnone8384.748,868 bytes
16 coloursbayer8544.7412,995 bytes

Read the last two columns, not the fringe count alone. At 16 colours the keyed edge gains 355 more green-excess pixels than the 256-colour version and mean alpha error rises. The 32-colour row reads 0 because quantisation pushed those edge pixels away from green, not because the edge is better; its alpha error (4.57) is worse than 64 colours (4.46). Dithering at 16 colours adds 46.5% to the file and changes almost nothing. If the output must be GIF, use 256 colours and no dither.

Three exports that keep the edge

The key itself is the same in all three. What changes is the container that receives the alpha. Export the keyed frames as PNG first, then choose:

# 1. APNG: 8-bit alpha, one file, 102,704 bytes for this 8-frame test
ffmpeg -framerate 12 -i keyed-%04d.png -plays 0 -f apng out.apng

# 2. WebM: VP9 with alpha, 17,288 bytes for the same frames
ffmpeg -framerate 12 -i keyed-%04d.png -c:v libvpx-vp9 -pix_fmt yuva420p -crf 30 -b:v 0 out.webm

# 3. PNG sequence: exact alpha, largest, for an editor or a frame-by-frame check
#    keep the keyed-%04d.png files

APNG is the drop-in replacement when a page already expects an animated image: 8-bit alpha, 24-bit colour, works in current Chrome, Firefox and Safari. It is 2.95× the GIF here because every soft edge needs more data. WebM is the smallest of the three at 17,288 bytes — about half the GIF, 2.01× smaller — and keeps the same 8-bit alpha; it is the better choice for a looping web element where Safari support is not a hard requirement. A PNG sequence is 158,908 bytes across eight files and is not a delivery format, but it is the only one that lets you inspect every frame before encoding.

If you are starting from a video rather than a frame sequence, the keying step is on green screen remover for video; the commands there write RGBA frames, which is exactly what the three exports above expect.

If the deliverable has to be a GIF

  1. Key at full resolution, export PNG. Do not key the GIF. By the time a frame is a GIF it has already lost the colours the key needs. Use the browser keyer for a still, or the frame workflow for a clip.
  2. Bake the background. Composite the keyed PNG onto the exact colour the GIF will sit on. A soft edge blended into a known background looks better than a hard transparent edge on an unknown one, and it is the only fix GIF actually supports.
  3. Quantise to 256 colours, no dither. The measured 16-colour row gained green speckles and 46.5% more bytes when dithered. Dithering is for gradients, not for a clean keyed edge.
  4. Use alpha only when the background is fixed. If you must keep transparency, set alpha_threshold=128 and check the hair against the final page colour, not on a checkerboard.
  5. Do not animate transparency over a changing background. GIF stores frame disposal, not per-pixel alpha. A transparent hole in one frame can reveal the previous frame underneath and leave a trail.
  6. Compare the size before shipping. The same eight frames were 34,822 bytes as GIF and 17,288 bytes as WebM. If the page accepts WebM, GIF is both larger and worse for this job.

Check the export, not the preview

For an alpha-channel check with counts and pass limits, use green screen remover edge check. It reads the matte rather than the composite, so it catches a lost edge before the GIF step hides it.

Green screen remover for GIF: questions

Can I remove a green screen from an animated GIF with the browser tool?

Not directly. The browser keyer opens the first frame of a GIF as a still image; it does not decode the animation or write an animated file. For a clip, export the frames, key each one, and encode the result as APNG, WebM or GIF. The frame path and the alpha checks are on green screen remover for video. If you only need one frame from the GIF, load it in the keyer and download the PNG.

Why does my transparent GIF still have a green halo?

Because the halo pixels are mixtures of subject and green, and a GIF can only mark one exact palette colour as transparent. Every other shade, including the green-tinted edge, stays opaque. Palette quantisation can make it worse by snapping several edge shades to one green entry. Raising tolerance before the export can delete the halo, but it also deletes hair and thin edges; after the export there is no alpha left to repair. The reliable fixes are a baked background or a format with real alpha.

Is APNG better than GIF for a transparent animation?

For alpha, yes. APNG stores 8 bits of alpha per pixel and 24-bit colour, so the soft band survives. On the same eight frames it was 102,704 bytes against the GIF's 34,822 — 2.95× bigger, because the extra edge data is real. Current Chrome, Firefox and Safari display APNG. If file size matters more than Safari, WebM was 17,288 bytes with the same alpha and about half the footprint.

How do I turn a green screen MP4 into a transparent GIF?

Key the clip to RGBA frames, then encode those frames with a reserved transparent colour. The two commands are:
ffmpeg -i clip.mp4 -vf "chromakey=0x00b140:0.10:0.06,format=rgba" keyed-%04d.png
ffmpeg -framerate 12 -i keyed-%04d.png -filter_complex "split[a][b];[a]palettegen=max_colors=256:reserve_transparent=1[p];[b][p]paletteuse=dither=none:alpha_threshold=128" -loop 0 out.gif
The FFmpeg chromakey values are YUV similarity and blend, not the browser keyer's RGB tolerance; do not copy 22% into that command. Check the first frame's alpha before encoding the whole clip.

Why did the transparent part of my GIF turn black or white?

A GIF's transparent pixels have no colour of their own. When a viewer, an editor or a social platform flattens the GIF, it fills those pixels with whatever background it uses — often white, black or the page colour. That is not the GIF "losing" transparency; the transparency was only an index that the next tool is free to replace. If the final background is known, bake it in before quantising. If it is not known, use APNG or WebM, which carry real alpha.

Next step

Key a still in the browser keyer, then decide the container before you encode. For a clip, start with the video workflow; for a matte check, use the edge checker. The measurements on this page were run with the site's key formula and local FFmpeg, so the numbers are reproducible rather than copied from a tool's marketing page.