Frontend Development 12 min read

How to Reverse a Firefox Logo Animation by Manipulating APNG Files

This article explains how to reverse a clockwise‑rotating Firefox logo animation by understanding the APNG file format, identifying frame chunks, and rewriting the binary data with JavaScript to play the frames in reverse order.

Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
How to Reverse a Firefox Logo Animation by Manipulating APNG Files

Reverse a Firefox Logo Animation Using APNG

APNG is a widely supported web animation format, but its interactivity is limited. To reverse a Firefox logo that rotates clockwise, we first need to understand the APNG file structure.

Animation Principle

Frame animation works by preparing a series of static images (key frames) with slight differences and displaying them quickly; the eye perceives motion due to persistence of vision. The Firefox logo consists of 25 key frames, each shown for 50 ms.

APNG File Structure

A PNG file is a binary bitmap composed of a signature followed by a series of chunks. The basic layout is:

<code>|-- PNG Signature --|-- IHDR --|-- IDAT --|-- IEND --|</code>

Each chunk has the format:

<code>|-4: length --|-4: type identifier --|N: data (length bytes) --|-4: CRC32 --|</code>

APNG adds animation control chunks such as

acTL

(animation control) and

fcTL

(frame control) on top of the standard PNG structure.

acTL Chunk

The

acTL

chunk specifies the total number of frames and the number of loops. For the Firefox logo the hex dump shows:

00 00 00 08

– length (8 bytes)

61 63 54 4C

– "acTL" identifier

00 00 00 19

– 25 frames

00 00 00 00

– infinite loops

fcTL Chunk

Each

fcTL

chunk describes a single frame (width, height, offsets, delay, dispose and blend operations). The first frame looks like:

<code>(0) |----4: length----|----4: fcTL----|
(8) |----4: sequence----|----4: width----|
(16)|----4: height----|----4: x offset----|
(24)|----4: y offset----|--2: delay numerator--|--2: delay denominator--|
(32)|-1: dispose op-| -1: blend op-|----4: CRC32----|</code>

Key fields are the sequence number (frame index) and the delay (e.g.,

00 32 03 E8

= 50 ms).

Reversal Strategy

Identify each frame by its

fcTL

chunk and the following data chunks (

IDAT

for the first frame,

fdAT

for subsequent frames).

Reverse the order of the frame data while keeping the

acTL

frame count unchanged.

Adjust sequence numbers and recompute CRC32 for every modified chunk.

Implementation Overview (JavaScript)

The code traverses the PNG byte array, extracts each chunk, and builds a new byte array with the frames in reverse order.

<code>// Iterate over each chunk
eachChunk(bytes, (type, bytes, off, length) => {
    const dv = new DataView(bytes.buffer);
    const obj = {};
    switch (type) {
        case 'fdAT':
            obj.sequence_number = dv.getUint32(off + 8);
            obj.crc = dv.getUint32(off + 8 + length);
            break;
        case 'fcTL':
            obj.sequence_number = dv.getUint32(off + 8);
            obj.width = dv.getUint32(off + 8 + 4);
            obj.height = dv.getUint32(off + 8 + 8);
            obj.x_offset = dv.getUint32(off + 8 + 12);
            obj.y_offset = dv.getUint32(off + 8 + 16);
            obj.delay = (dv.getUint16(off + 8 + 20) / (dv.getUint16(off + 8 + 22) || 100)) * 1000;
            obj.dispose_op = dv.getUint8(off + 8 + 24);
            obj.blend_op = dv.getUint8(off + 8 + 25);
            obj.crc = dv.getUint32(off + 8 + 26);
            break;
        default:
            break;
    }
    // store or log obj as needed
});</code>

Two passes are required: the first records the positions of all frames and non‑frame chunks; the second rebuilds the PNG, swapping the frame order, converting the first

fdAT

to

IDAT

, and updating sequence numbers and CRC32.

<code>// Example of creating a new chunk with updated CRC
var makeChunkBytes = function (type, dataBytes) {
    const crcLen = type.length + dataBytes.length;
    const bytes = new Uint8Array(crcLen + 8);
    const dv = new DataView(bytes.buffer);
    dv.setUint32(0, dataBytes.length);
    bytes.set(makeStringArray(type), 4);
    bytes.set(dataBytes, 8);
    var crc = CRC32.byte(bytes, 4, crcLen);
    dv.setUint32(crcLen + 4, crc);
    return bytes;
};
</code>

After processing, the resulting PNG contains the same number of frames but plays them backward, producing a counter‑clockwise rotation of the Firefox logo.

Final Result

The reversed animation is displayed correctly, confirming that the frame order, sequence numbers, and CRC values were updated properly.

JavaScriptAPNGimage formatanimation reversalbinary manipulation
Tencent IMWeb Frontend Team
Written by

Tencent IMWeb Frontend Team

IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.