We've all seen GIF files being used. They're the internet's go-to for animated imagery. I've never actually looked into the specification until recently, and I've found that it's a pretty simple file format. This makes GIF a perfect candidate for what’s becoming a recurring “Demystifying” series here.
In this article, we're going to dive into how GIF files are encoded, and some interesting quirks of the format.
GIF: Some Notes
The GIF file format has a few notable properties that should be noted before going into details about the format.
-
- The format is not designed for true-color images.
- The format supports a color palette of up to 256 colors.
- GIF images follow one of two specifications.
GIF89ais the one usually referred to (this is the specification that supports animation).GIF87ais the older specification, which is commonly used in still images today.
In this article, we'll be covering GIF87a.
Images
This is one of the most interesting features of GIFs. A single GIF file may have many images in it.
Essentially, an image is a separate array of pixels. This could be a frame of an animation, or even a section of displayed picture. The interesting part is that although each color table is limited to 256 colors, a picture could be split into images of 256 colors, and the images could be tiled onto the displayed picture.
This allows many more than 256 unique colors to be displayed in a picture, even though the palette has a 256 color maximum (although this is something rarely seen used).
In the common use of animated GIFs, each frame of the animation is typically a separate image.
My Example File
In all of the following sections, I'm going to use this example image.

The reason I chose such a small picture is because it's going to have a small file size, which will make it easier to understand the raw data of the file.
The Header
Like most image file formats, the GIF specification defines some characteristics of the header.
First, the file must start with 47 49 46 (which is GIF in ASCII). Following this, there must be the version of the standard being followed (such as 38 37 61, 87a in ASCII).
In the example image, we can see that the file starts with 47 49 46 38 37 61, as we expect.

Logical Screen Descriptor
Next, the image data contains the "Logical Screen Descriptor", which contains the information about the screen and color information.
The first part of the LSD is the canvas width and height. These are measured in two byte pairs (16 bits), which allow the image to be a maximum size of 65535 wide or tall. These values are stored in little endian, meaning that the least significant byte is stored first.
Since our image is a 2 pixel square, we can specify the width of 2 with the bytes 02 00, and the height with 02 00. Combining these together (02 00 02 00), we have the canvas width and height part of the LSD. As a note, this is usually ignored by modern viewers, as this is not the image dimensions, but instead the size of a "canvas" in which multiple separate images could in theory be placed. This isn't used anymore (at least as far as I've seen), as the only use for separate images in the file would be for animation (in which all images would be the full canvas size).
Next in the file is a byte with packed data. In the example image, this byte is set to A1, or 10100001. Each bit here means something different.
- The first bit,
1, is the "Global Color Table Flag". By this being one, this says that there is a global color table (we'll get into this more later). - The next three bits,
010, is the "Color Resolution". This is only used if there is a global color table, and it allows us to compute the size of the global color table. This determines how many colors can be in the color palette. We can compute the size of the color palette with the formula2^(N+1), whereNis the number stored in the color resolution. In this case,010(or 2 in decimal), our palette supports2^(2+1)8 unique colors. - The fifth bit,
0, is the "Sort Flag". This tells the viewer of the image if the colors in the global color table are sorted from most to least importance. Although this usually means "most used color" to "least used color", this is not always the case. - The last three bits,
001, is the size of the global color table.
The next byte, 02 is the background color index. This is the index of which color in the global color table to use for the background of the image.
The last byte, 00, is the pixel aspect ratio. This is normally set to 00, and although the standard doesn't explain what it's for nor do modern viewers use it, this was likely to allow designers to represent image captures from analog television by modifying the aspect ratios of the pixels.
Putting these 7 bytes together, we get 02 00 02 00 A1 02 00.
LSD and Header Example
GIF Header & LSD:
GIF87a
Header Hex:
LSD:
LSD Hex:
Packed:
Global Color Table
After the logical screen descriptor is the "Global Color Table". This table consists of a list of RGB values for each color in the table. Each color is stored in 3 bytes, one for each red, green, and blue intensity. (No CMYK here, graphic designers!)
Since the size of our global color table was set to 001, we have to read in 3*2^(N+1) bytes, where N is the global color table size that was stored. Using the formula, we know we have to read in 12 bytes. This is 00 00 00 80 80 80 FF FF FF FF FF FF.
Now that we have the global color table data, we can split it into three byte sections it to make it more human readable.

For each color in the table, we can take each byte, and convert it into its corresponding decimal value to get the color's RGB value.

Now that we have the RGB colors that are in the palette, we are done with the global color table data.
Graphics Control Extension
The "Graphics Control Extension" is able to specify transparency settings along with animation information. Due to us having a simple example image, this isn't used, and I won't cover it here.
Image Descriptor
Because a single GIF file can have many images in it, we need an "Image Descriptor" to set up the image for placement and such on the canvas.
This is 10 bytes long, and always starts with 2C. The next two bytes are the position of the image on the canvas from the left hand side (00 00, this aligns the following image with the left side of the canvas), and the following two bytes are the position from the top of the canvas (00 00).
Following this are the image dimensions. To specify the width as 2 pixels, we use 02 00 (2 in little endian), and the height is specified with 02 00.
The last byte in the image descriptor is a packed field (binary 00000000), with an explanation below.
- The first bit is the "Local Color Table" flag. This is set to
0in the example, because the image is going to use the global color table for its colors. - The next bit is the "Interlace" flag. This is
0for us, as we don't need to render this image in stages. - The third bit is the "Sort" flag. Reference the sort flag in the Logical Screen Descriptor. This isn't used by us, as we aren't using a local color table.
- The next two bits are "Reserved For Future Use". They don't do anything, but one day the standard may use them for something.
- The last three bits are the size of the local color table. This is
000for us, as we aren't using a local color table.
Image Data
After the Image Descriptor comes the actual pixel data for the image. This data is compressed using LZW (Lempel–Ziv–Welch) compression.
The image data begins with a single byte:
LZW Minimum Code Size
This value specifies the number of bits needed to represent a color index before compression begins. For example, if the image uses up to 8 colors, the minimum code size will be 3, because 3 bits can represent values from 0–7.
Sub-Blocks
Following the minimum code size, the compressed data is stored in a series of sub-blocks:
[Block Size][Data...] [Block Size][Data...] ... 00
Each block can be up to 255 bytes long. A block size of 00 marks the end of the image data.
This block-based structure allows decoders to stream data without knowing the total compressed size ahead of time.
How LZW Works (Simplified)
Rather than storing individual pixel indices, LZW builds a dictionary of sequences:
-
The dictionary is initialized with:
-
All possible color indices
-
A Clear Code
-
An End-of-Information Code
-
-
As pixel data is read:
-
Sequences of indices are replaced with dictionary codes.
-
New sequences are added to the dictionary.
-
-
If the dictionary becomes full:
-
A Clear Code resets it.
-
The result is a stream of variable-length codes that often compress repetitive patterns extremely well—perfect for simple graphics and animations.
Why This Matters
Because GIF images frequently contain large flat-colored areas and repeating patterns, LZW compression is especially effective. This is one of the reasons GIF became popular long before modern compression formats existed.
What We Didn’t Cover (Yet)
This article focused on the core building blocks needed to understand how a basic GIF file is laid out and how an image is described at a byte level. While this covers a large portion of what’s required to parse simple, non-animated GIFs, several important features of the format were intentionally left out.
In particular, we did not cover:
-
Transparency, which is implemented through the Graphics Control Extension and allows a single color index to be treated as transparent.
-
Interlacing, a storage method that allows an image to appear progressively while loading.
-
Animated GIF structure, including looping, frame delays, and disposal methods, all of which rely on extension blocks and multiple image descriptors.
-
LZW compression internals, beyond acknowledging that image data is compressed using LZW.
Each of these topics deserves its own deeper dive, as they introduce additional structures, flags, and behaviors that build on top of the fundamentals covered here.
In a future post, we’ll take a closer look at how LZW compression actually works inside GIF files, including dictionary initialization, clear codes, variable-width codes, and how pixel indices are transformed into a compressed bitstream.
For now, you should have a solid mental model of how a simple GIF is organized on disk—and hopefully a little more appreciation for how much functionality is packed into such an old (but still widely used) file format.