How to Build a Bare-Bones GIF Encoder from Scratch

This article explains how to construct a minimal, fully compliant GIF encoder from the ground up by writing raw binary data directly to an output stream. You will learn the anatomy of the GIF format—specifically the GIF89a standard—including how to assemble the file header, screen descriptor, color palette, image descriptor, LZW-compressed data blocks, and terminating byte without relying on third-party image processing libraries.

1. The GIF Header

Every GIF file begins with a 6-byte signature indicating the file format and version. The standard versions are GIF87a and GIF89a. For modern compatibility, use the ASCII bytes for GIF89a:

0x47 0x49 0x46 0x38 0x39 0x61  // "GIF89a"

Write these 6 bytes directly to the start of your binary buffer.

2. The Logical Screen Descriptor

Immediately following the header is the 7-byte Logical Screen Descriptor, which defines the canvas dimensions and global display properties. GIF uses 16-bit little-endian integers for dimensions.

For a 2-color palette (size exponent 0), the packed byte with a GCT present is typically 0x80 (10000000 in binary).

3. The Global Color Table

If the GCT flag is set to 1, the palette must immediately follow the Logical Screen Descriptor. The color table consists of uncompressed, 3-byte RGB entries.

The number of entries is determined by 2^(N+1) from the packed fields byte. For a minimal 1-bit palette (2 colors):

0x00 0x00 0x00  // Index 0: Black (R, G, B)
0xFF 0xFF 0xFF  // Index 1: White (R, G, B)

This yields 6 bytes total. The color count must strictly match the declared power of two; pad unused slots with zeros if necessary.

4. The Image Descriptor

Each individual frame requires an Image Descriptor block (10 bytes) that marks the start of frame data:

5. LZW Raster Data Compression

GIF encodes pixel index streams using a modified LZW (Lempel-Ziv-Welch) compression algorithm with variable code lengths.

  1. LZW Minimum Code Size (1 byte): Declare the starting code bit-length. For a 2-color image, this value is 2 (the minimum allowed value by the GIF specification is 2, even for 1-bit palettes).
  2. Special Codes:
    • Clear Code: 2^(Minimum Code Size). For a size of 2, this is 4.
    • End of Information (EOI) Code: Clear Code + 1 (i.e., 5).
  3. Code Stream Emission:
    • Start code size is Minimum Code Size + 1 (e.g., 3 bits).
    • Always emit the Clear Code first.
    • Read pixel indices, match sequences in the dictionary, and output code words.
    • Increment the bit size by 1 whenever the dictionary size exceeds the current bit-length capacity (e.g., reaching code 8 requires switching to 4-bit codes).
    • Emit the EOI Code after processing all pixels.
  4. Bit Packing: Pack variable-length codes into a continuous stream of 8-bit bytes, filling least-significant bits (LSB) first.
  5. Sub-blocks: Group the packed byte stream into chunks of no more than 255 bytes. Prefix each chunk with a 1-byte length indicator. Terminate the entire compressed block with a 0x00 byte (a sub-block of length zero).

6. The GIF Trailer

To finalize the file, append the GIF trailer byte:

0x3B  // ASCII ";"

This single byte signals the end of the GIF data stream to decoders. Once written, close the binary stream; the resulting output is a valid, standalone GIF file.