GIF: Graphics Interchange Format for Animation & Graphics
GIF (Graphics Interchange Format) was introduced in 1987 as a platform-independent image format. While GIF has been superseded by PNG (lossless graphics), WebP (animation), and modern video, GIF remains ubiquitous for simple animations, memes, and web compatibility. Understanding GIF structure is essential for legacy web content and optimizing animated graphics.
GIF File Structure: Header & Logical Screen Descriptor
Every GIF file follows a strict binary structure:
G I F 8 9 a (signature — 'GIF89a' for animated, 'GIF87a' for static)
[Logical Screen Descriptor — 7 bytes]
[Global Color Table — optional, up to 768 bytes]
[Data stream — images, text, extensions]
; (trailer — marks end)
Logical Screen Descriptor (7 bytes):
Byte 0-1: Canvas Width (16-bit little-endian)
Byte 2-3: Canvas Height (16-bit little-endian)
Byte 4: Packed Fields
Bit 7: Global Color Table Flag (1 = present)
Bits 6-4: Color Resolution (bits per pixel - 1)
Bit 3: Sort Flag (1 = sorted by luminance)
Bits 2-0: Global Color Table Size (2^(N+1) colors)
Byte 5: Background Color Index (into global palette)
Byte 6: Pixel Aspect Ratio (0 = square pixels)
Example (256×256 GIF with 256-color palette, no sorting):
Width: 00 01 (256 pixels, little-endian)
Height: 00 01 (256 pixels)
Packed: F7 (11110111 binary)
bit 7=1 (global color table present)
bits 6-4=111 (8 bits per pixel = 256 colors)
bit 3=0 (not sorted)
bits 2-0=111 (size = 2^(7+1) = 256)
Background: 00 (color 0)
Aspect: 00 (1:1 ratio)
Color Palette: Global & Local Tables
GIF uses indexed color—every pixel is an index (0–255) into a color table:
Global Color Table (GCT):
256 entries × 3 bytes (RGB) = 768 bytes maximum
Entry 0: [R0 G0 B0]
Entry 1: [R1 G1 B1]
…
Entry 255: [R255 G255 B255]
Local Color Table (LCT, per image):
[Image Descriptor]
[Local Color Table — optional, overrides global]
[Image Data]
Used when an image needs a different palette than global (e.g., first frame uses warm colors, second uses cool colors).
Transparency:
[Graphic Control Extension]
Byte 0: Transparency Flag (1 = transparent color specified)
Byte 1: Transparent Color Index (e.g., 255 = magenta is transparent)
Pixels with index matching transparent color index are rendered as transparent (only 1-bit transparency—fully opaque or fully transparent, no alpha blending).
LZW Compression: Dictionary-Based Encoding
GIF uses LZW (Lempel-Ziv-Welch) compression—a lossless algorithm optimized for palette data:
LZW encoding process:
Input: [index sequence from scanlines]
256 initial codes (one per palette entry)
Codes 256+ built dynamically as patterns emerge
Scanline: [0] [1] [0] [1] [0] [1] [2] …
Codes: [0] [1] [0] [1] [0] [1] [2] [256=01] [257=10] …
Output: [0] [1] [256] [257] [2]…
Result: frequent patterns (e.g., alternating colors in gradients) are encoded as single codes, achieving ~30–50% compression on graphics.
Animation: Graphic Control Extension & Image Descriptor
Graphic Control Extension (8 bytes):
Byte 0: Extension Introducer (0x21)
Byte 1: Graphic Control Label (0xF9)
Byte 2: Block Size (always 4)
Byte 3: Packed Fields
Bit 7-5: Reserved
Bits 4-2: Disposal Method (0=none, 1=leave, 2=restore background, 3=restore previous)
Bit 1: User Input Flag (1 = animation pauses for user input)
Bit 0: Transparency Flag (1 = transparent color follows)
Bytes 4-5: Delay Time (10ms units, little-endian)
e.g., 5 = 50ms (20 FPS)
Byte 6: Transparent Color Index
Byte 7: Block Terminator (0x00)
Image Descriptor (10 bytes):
Byte 0: Image Separator (0x2C)
Bytes 1-2: Image Left Position
Bytes 3-4: Image Top Position
Bytes 5-6: Image Width
Bytes 7-8: Image Height
Byte 9: Packed Fields
Bit 7: Local Color Table Flag
Bit 6: Interlace Flag (1 = 4-pass Adam7 interlacing)
Bit 5: Sort Flag
Bits 2-0: LCT Size
Animation structure (3-frame GIF):
[GIF Header + Global Color Table]
[Graphic Control Extension — frame 1, delay 50ms]
[Image Descriptor — image 1]
[LZW compressed image data]
[Graphic Control Extension — frame 2, delay 50ms]
[Image Descriptor — image 2]
[LZW compressed image data]
[Graphic Control Extension — frame 3, delay 50ms]
[Image Descriptor — image 3]
[LZW compressed image data]
[Trailer — 0x3B]
Interlacing: 4-Pass Progressive Rendering
GIF supports interlacing for progressive display:
Pass 1: rows 0, 8, 16, 24… (every 8th row)
Pass 2: rows 4, 12, 20, 28… (every 8th, offset 4)
Pass 3: rows 2, 6, 10, 14… (every 4th, offset 2)
Pass 4: rows 1, 3, 5, 7… (every 2nd, odd rows)
Interlaced GIF shows rough preview first, refines progressively. Tradeoff: ~10% larger file (due to additional encoding passes).
GIF vs. Modern Alternatives
| Metric | GIF | PNG | WebP | MP4 (H.264) |
|---|---|---|---|---|
| Animated | ✅ | ❌ (APNG exists) | ✅ | ✅ |
| Palette limitation | 256 colors max | Unlimited | Unlimited | Unlimited |
| Transparency | 1-bit (binary) | Full alpha (8-bit) | Full alpha | Full alpha |
| File size (animation) | 2 MB (large) | 500 KB (APNG) | 400 KB (WebP) | 100 KB (MP4 H.264) |
| Browser support | 100% | 100% | 97% | 95% |
| Patent-free | ⚠️ (LZW patent expired 2003) | ✅ | ✅ | ⚠️ (MPEG LA) |
Modern recommendation: Replace GIF with WebP (better compression) or MP4/WebM (video—far superior compression).
Creating & Optimizing GIFs
FFmpeg (MP4 → GIF, 10 FPS):
ffmpeg -i input.mp4 -vf fps=10 -c:v gif output.gif
ffmpeg -i input.mp4 -vf "scale=480:-1,fps=10" output.gif
ImageMagick (image sequence → animated GIF):
convert -delay 10 -loop 0 frame*.png animated.gif
convert -delay 10 animated.gif -resize 50% optimized.gif
Python (Pillow):
from PIL import Image
frames = [Image.open(f) for f in ['frame1.png', 'frame2.png']]
frames[0].save('output.gif', save_all=True, append_images=frames[1:], duration=100, loop=0)
Optimization (gifsicle):
gifsicle -b -O3 --lossy=80 input.gif -o output.gif
GIF remains important for memes and legacy web, but for new projects, WebP animations or video are superior.
Related conversions
Most teams that read this guide convert images in one of these directions: