A backwards-compatible image container that hides modern image formats inside a valid GIF.
Original source (anim.webp):
GIFX hybrid fallback view (anim-gifx.gif):
These are embedded from:
- https://github.com/var-gr/gifx/blob/master/examples/anim-gifx.gif
- https://github.com/var-gr/gifx/blob/master/examples/anim.webp
GIFX solves a problem almost every web developer and designer has run into: GIFs are universally supported, but they look terrible. Modern formats like animated WebP and APNG are dramatically smaller and cleaner, but plenty of platforms, email clients, legacy apps, and social pipelines still choke on them.
GIFX gives you both. The file is a real, valid GIF that works everywhere. But for apps that know what to look for, the original high-fidelity payload is right there inside.
- GIF works in every client, browser, and chat app since the 90s. But 256 colours, no alpha blending, and bloated file sizes make it a poor choice for anything complex.
- WebP and APNG look great and compress well, yet plenty of tools still strip, corrupt, or refuse them. Try sending an animated WebP through an older email pipeline, a CMS with strict asset rules, or a social platform that re-encodes everything.
You end up choosing between universal compatibility and acceptable quality.
GIFX embeds the original image (WebP, APNG, PNG, JPEG, or anything else Pillow can read) as a hidden payload inside a standard GIF file.
- Old clients, viewers, and platforms see a normal GIF and display the fallback. Nothing breaks.
- GIFX-aware websites and tools detect the hidden payload and serve the original instead. Users get the better format without you maintaining two separate asset pipelines.
The GIF fallback is auto-generated for you, and you can dial the quality up or down with --scale, --colors, and --fps-divisor. The embedded payload itself is never touched – it stays pixel-perfect.
pip install PillowWrap a high-quality source image into a GIFX hybrid:
python gifx.py encode input.webp output.gifOptions:
| Flag | Default | Description |
|---|---|---|
--scale |
0.5 |
GIF fallback resolution scale |
--colors |
64 |
GIF palette size (2–256) |
--fps-divisor |
2 |
Keep every Nth frame to shrink the GIF |
--mime |
auto | Override the embedded MIME type |
Extract the hidden payload from a GIFX file:
python gifx.py decode input.gif output.webpCheck what is inside a GIFX file without extracting:
python gifx.py inspect input.gifDrop the JavaScript files into your project and add data-gifx to any image:
<img data-gifx="/assets/animation.gif" alt="Animation">import { mountGifxImages, loadGifx, parseGifx } from './gifx.js';
// Auto-replace all data-gifx images on page load
await mountGifxImages();
// Or load manually
const blob = await loadGifx('/assets/animation.gif');
img.src = URL.createObjectURL(blob);The browser script automatically falls back to the GIF if:
- the file is a plain GIF with no GIFX payload, or
- the browser does not support animated WebP (detected at runtime).
A GIFX file is structured like this:
[GIF data ... 0x3B] <-- standard GIF, ends with the normal trailer byte
[GIFX] <-- 4-byte magic signature
[version] <-- format version (1 byte)
[flags] <-- bitfield: animated, etc. (1 byte)
[mime_len] --
[payload_len] --
[mime bytes] --
[payload bytes] <-- the original image, untouched
The GIF is fully valid and self-contained. The extra bytes after the trailer are ignored by ordinary decoders, but GIFX parsers know to look for the GIFX signature immediately after 0x3B.
| File | Purpose |
|---|---|
gifx.py |
Command-line encoder, decoder, and inspector |
gifx-core.js |
Low-level parser: reads a GIFX file and returns the payload |
gifx-browser.js |
High-level helpers: loadGifx, mountGifxImages, animated-WebP feature detection |
gifx.js |
Single import path that re-exports both core and browser APIs |
A live demo is hosted on GitHub Pages: https://var-gr.github.io/gifx
The demo shows the automatic data-gifx mounting, manual loadGifx usage, and a side-by-side comparison of the original WebP versus the GIF fallback.
The <picture> element is great for responsive images, but it does not help when:
- an asset must pass through a platform that re-encodes or strips unknown formats,
- you need one file that works as an attachment, embed, or download,
- legacy consumers do not parse HTML at all.
GIFX keeps everything in one container. The fallback is literally inside the same file.
MIT. See LICENSE.

