A one-pixel image is no longer a sensible layout tool, but a tiny valid image response still has a few legitimate uses: protocol tests, placeholders, email rendering checks, privacy-conscious tracking pixels, and endpoints whose contract requires an image. A self-contained GIF can represent one visible pixel in 35 bytes or one transparent pixel in 43 bytes.
Those numbers describe complete, independently decodable images. Shorter byte sequences circulate online, but many omit an active color table, omit a rendered image entirely, or depend on tolerant decoder behavior. Saving a few bytes is not useful when the result is no longer a portable GIF.
Generate the GIF
The function below creates a one-pixel GIF in any RGB color. It uses GIF87a for the visible image and GIF89a when transparency requires a Graphic Control Extension:
<?php
declare(strict_types=1);
function minimalGif(
int $red = 0,
int $green = 0,
int $blue = 0,
bool $transparent = true
): string {
foreach ([$red, $green, $blue] as $component) {
if ($component < 0 || $component > 255) {
throw new ValueError('RGB components must be between 0 and 255.');
}
}
$header = $transparent ? 'GIF89a' : 'GIF87a';
$screenAndWhite = pack('H*', '01000100900100ffffff');
$color = pack('C3', $red, $green, $blue);
$graphicControl = $transparent
? pack('H*', '21f9040100000100')
: '';
$imageAndTrailer = pack(
'H*',
'2c00000000010001000002024c01003b'
);
return $header
. $screenAndWhite
. $color
. $graphicControl
. $imageAndTrailer;
} pack('H*', ...) converts hexadecimal text to a binary string, while pack('C3', ...) writes the three unsigned color bytes. Explicit range validation matters because silently wrapping an invalid color component would hide an input error.
The image data selects palette index 1, which is the requested RGB color. In transparent mode, the Graphic Control Extension marks that same index as transparent, so its RGB value is intentionally not displayed.
Where the Bytes Go
| Block | Bytes | Purpose |
|---|---|---|
| Header | 6 | GIF87a or GIF89a |
| Logical Screen Descriptor | 7 | Declares a 1 by 1 pixel logical screen and a global color table |
| Global Color Table | 6 | Stores white at index 0 and the requested RGB color at index 1 |
| Graphic Control Extension | 0 or 8 | Marks palette index 1 as transparent |
| Image Descriptor | 10 | Places a 1 by 1 pixel image at the origin |
| Image Data | 5 | Contains the minimum LZW code size, one data sub-block, and its terminator |
| Trailer | 1 | Terminates the GIF data stream with 0x3b |
The visible image therefore occupies:
\[6 + 7 + 6 + 10 + 5 + 1 = 35\text{ bytes}.\]
Transparency adds exactly one eight-byte Graphic Control Extension:
\[35 + 8 = 43\text{ bytes}.\]
Logical Screen and Palette
GIF stores multi-byte integers in little-endian order, so width and height are each encoded as 01 00. The packed field 0x90 enables a global color table and declares its smallest legal size: two RGB entries. A GIF color table cannot contain only one entry, even though the raster contains one pixel.
The Five Image-Data Bytes
The image data is 02 02 4c 01 00. The first 02 is the minimum LZW code size required for a one-bit image by the GIF specification. The second 02 is the data sub-block length, 4c 01 contains the packed clear, pixel, and end-of-information codes, and 00 terminates the sub-block sequence.
Send It from PHP
Build the binary string before sending headers, derive the length from the actual payload, then terminate the endpoint immediately:
<?php
$gif = minimalGif(red: 30, green: 110, blue: 190, transparent: true);
header('Content-Type: image/gif');
header('Content-Length: ' . strlen($gif));
header('Cache-Control: no-store');
echo $gif;
exit; strlen() returns the byte length of a PHP string, so it produces 35 or 43 directly. Computing the header from the payload is clearer and safer than duplicating the size formula. Headers must be sent before any output. Keep this endpoint free of a UTF-8 byte-order mark, leading whitespace, debug output, and templates; a single extra byte corrupts the binary response.
The example uses no-store for a request-specific response. For an immutable placeholder, prefer a static file served by the web server or CDN with a long public cache lifetime. Also avoid a manual Content-Length when an output handler will transform or compress the body after PHP computes its length.
Write a Static File
When the image does not change per request, generate it once and verify that the complete payload was written:
<?php
$gif = minimalGif(red: 220, green: 30, blue: 40, transparent: false);
$written = file_put_contents('pixel.gif', $gif, LOCK_EX);
if ($written !== strlen($gif)) {
throw new RuntimeException('Unable to write the complete GIF.');
} Serving that file directly avoids PHP startup and lets the HTTP server handle validators, range behavior, and caching. Runtime generation is justified only when application logic genuinely determines the response.
Verify the Result
PHP can confirm the dimensions, MIME type, and expected byte count without writing a file:
<?php
foreach ([[false, 35], [true, 43]] as [$transparent, $expectedBytes]) {
$gif = minimalGif(255, 0, 0, $transparent);
$info = getimagesizefromstring($gif);
if (
strlen($gif) !== $expectedBytes
|| $info === false
|| $info[0] !== 1
|| $info[1] !== 1
|| $info['mime'] !== 'image/gif'
) {
throw new RuntimeException('Generated GIF failed validation.');
}
} This checks the externally visible contract rather than trusting the hard-coded blocks. Decoder tests are especially useful after changing packed fields or LZW bytes.
Use It as a Tracking Pixel
A transparent GIF can record that an HTML document or email client requested a particular image URL. For example, first-party documentation can count loads of a specific page element without storing an IP address, user agent, cookie, or account identifier. The markup reserves the pixel's dimensions so it does not disturb layout:
<img
src="https://example.com/pixel.php?event=docs-footer"
width="1"
height="1"
alt=""> The endpoint below assumes that bootstrap.php provides the minimalGif() function and a PDO connection in $pdo. It accepts a bounded event name, stores only the event and server time, and returns the transparent image:
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
$event = $_GET['event'] ?? '';
if (!is_string($event) || preg_match('/\A[a-z0-9][a-z0-9_-]{0,63}\z/D', $event) !== 1) {
http_response_code(400);
exit;
}
$statement = $pdo->prepare(
'INSERT INTO pixel_event (event_name, occurred_at) VALUES (?, UTC_TIMESTAMP(6))'
);
$statement->execute([$event]);
$gif = minimalGif();
header('Content-Type: image/gif');
header('Content-Length: ' . strlen($gif));
header('Cache-Control: no-store, private');
header('X-Content-Type-Options: nosniff');
echo $gif;
exit; no-store matters because a cached response does not reach the endpoint again. Even then, the count is not a count of people who read the content. Mail privacy proxies may prefetch and cache images, security scanners may request them automatically, and clients may block remote images. Treat the result as a noisy delivery signal rather than proof of a human view.
The image format does not make collection harmless or anonymous. Disclose analytics, collect only what the stated purpose needs, honor applicable consent and retention requirements, and avoid embedding stable person-level IDs in the URL. If an image response is not part of the client contract, a normal analytics request with 204 No Content communicates the intent more accurately and avoids transferring an image.