I changed one pixel. The PNG grew by five bytes.

A 64-by-64 gray square took 98 bytes as a PNG. I changed one pixel from 127 to 128, saved it again, and got 103 bytes.

The picture grew by five bytes because one pixel changed by one.

This sounds like an argument with arithmetic, so I tried two more images. A horizontal gradient grew from 171 to 175 bytes after the same one-pixel edit. A field of seeded random noise stayed exactly 4,228 bytes, although eight byte positions in the file changed.

64×64 image       before   after   size change
solid gray            98     103       +5
horizontal gradient  171     175       +4
seeded noise         4228    4228        0

All three edited images still differed from their originals by exactly one decoded pixel value. The extra changes happened in the container and its compressed data, not on the canvas.

A PNG is not a tiny spreadsheet

If an image file stored every grayscale pixel in a neat row, the experiment would be dull. A 64-by-64 image would contain 4,096 pixel bytes, and changing one pixel would change one of them.

PNG does more work. Under the PNG specification, an encoder turns pixels into scanlines, adds a filter choice to each row, filters those rows, and compresses the result into the image-data stream. The file also stores lengths, chunk labels, and integrity checks.

I deliberately used a plain version of that recipe: 8-bit grayscale, no interlacing, the “None” filter for every row, one image-data chunk, and zlib’s highest compression level. That produced 4,160 bytes before compression: 4,096 pixels plus one filter byte for each of 64 rows.

Only one of those 4,160 bytes changed in each test. Then compression got a vote.

Why the changed pixel costs space

PNG uses DEFLATE inside its zlib stream. DEFLATE combines references to repeated byte strings with Huffman coding. Repetition is why the solid square collapses from thousands of scanline bytes to a 98-byte file.

The edited pixel interrupts that extremely cheap description. The compressor has to account for one exceptional value and then resume the repetition around it. In this particular encoding, that explanation cost five bytes. The gradient had different repetition and paid four.

The noisy image was already expensive. Its input offered few useful repeated strings, so one nearby value did not change the file length. It still changed the compressed stream and its checks, but not by adding space.

That is also why “one pixel changed” cannot predict “one file byte changed.” The pixel is the fact we care about. The PNG is a compact set of instructions for reconstructing all the facts, plus enough structure to notice damaged instructions. Edit one fact and the best instructions may need a different shape.

Lossless does not mean locally editable

Nothing was lost here. I decompressed every result and verified the scanlines byte for byte. Lossless means the decoder can recover the original pixels exactly. It does not promise that a small visual edit will stay confined to a small patch of the file.

The exact byte counts are not universal. Another encoder can choose different row filters, compression settings, chunk boundaries, or even a different legal strategy for the same picture. This test used Python 3.14.7 and zlib 1.3.2, and it says only what those controlled files did.

Still, the five extra bytes make a useful little peephole into compression. A PNG does not remember the square as 4,096 independent gray cells. It remembers how to rebuild the square efficiently. One pixel broke the easy story, and the file needed five more bytes to explain itself.

Sources