What is an Interlaced GIF?
An interlaced GIF stores image rows in four passes rather than sequentially. A decoder can therefore show a coarse version of the complete image before enough data arrives to render every row.
How Interlaced GIFs work
GIF interlacing changes only the order in which a frame’s rows enter the LZW-compressed image data. The first pass covers the full height sparsely and later passes fill the gaps. The preview that results is a decoder behavior, not something the format prescribes: GIF89a defines the row order, and a decoder may paint rows as they arrive. Firefox’s deinterlacing filter, for example, copies each received row into the neighboring rows that have not arrived yet when progressive display is enabled. The color table, dimensions, transparency rules, and final pixels are the same as for sequential storage. Libraries differ in who undoes the reordering: giflib’s DGifSlurp places the rows of an interlaced frame at their final positions using the four pass offsets, calling the line-level reader once per row in stored order.
Key facts
- 1In what order are the rows stored? Pass 1 holds every 8th row starting with row 0, pass 2 every 8th row starting with row 4, pass 3 every 4th row starting with row 2, and pass 4 every 2nd row starting with row 1. GIF89a fixes this pattern, so a decoder has about an eighth of the rows after pass 1, a quarter after pass 2, and half after pass 3 (exactly so when the height is a multiple of 8). Short frames leave passes empty: with four rows or fewer there is no pass 2 row, and a one-row frame has only pass 1. A de-interlacer has to check each pass’s starting row against the frame height before it copies a row: a public bug report against the APNG4Android GIF decoder describes it writing past its output buffer on a two-row interlaced frame because it copied a pass 2 row first.
- 2How do I tell whether a GIF is interlaced? Per frame, not per file. The Interlace Flag is one bit in the packed byte of each Image Descriptor (giflib tests it with the mask
0x40), and every image in the data stream has its own descriptor, so frames of one animation can differ. Tools do not always surface the flag: a libvips user asked for an interlaced field in the GIF header output aftergifsavegained aninterlaceoption. - 3Does interlacing add anything besides the preview? No. The reordered rows are still indexes into the active color table, compressed with LZW, and transparency is still the single index named in the Graphic Control Extension. Interlacing adds no colors and no partial transparency.
- 4Will my tools write interlaced GIFs without being asked? Some will, and not on every code path. Pillow’s handbook says its
interlacesave option is on by default unless the image is less than 16 pixels wide or high. In Pillow’s source (release 12.0.0 and the main branch as read in October 2026) only the single-frame writer applies it: passinginterlace=Falsethere switches it off, while frames written by a multi-frame save (save_allproducing more than one frame) always get a cleared Interlace Flag and sequential rows. Gifsicle leaves it to the caller:--interlaceturns it on, and--same-interlacekeeps each input’s existing setting. Set the option explicitly when a downstream consumer expects one layout, and read the flag of the frames you wrote instead of inferring it from the default.
When Interlaced GIFs matter
Interlacing matters in two places. For delivery, it changes the order in which rows arrive, which lets a decoder show a coarse version of the whole frame early. It is a row order, not a compression setting, but the reordered rows are what LZW compresses, so the encoded size is not guaranteed to match the sequential encoding. GIF89a does not say how interlacing affects size, so compare the two outputs instead of assuming they are equal or that one is smaller. For processing, interlaced files are still being produced, since Pillow interlaces single-frame GIFs by default when it saves them, so any code that reads GIF image data row by row has to put the rows back in order before it crops, resizes, or converts a frame.
Sources
- GIF89a specification: Image Descriptor (Interlace Flag) and Appendix E, Interlaced Images
- giflib source: dgif_lib.c (interlace flag and de-interlacing in DGifSlurp)
- Firefox source: image/SurfaceFilters.h (DeinterlacingFilter and progressive display)
- Pillow handbook: GIF save options, including interlace
- Pillow 12.0.0 source: GifImagePlugin.py (single-frame and multi-frame GIF writers)
- Gifsicle manual: --interlace and --same-interlace
- libvips issue 3267: Add interlaced header info for GIF format
- APNG4Android issue 258: GIF decoder can write past the output buffer for small interlaced frames