Skip to main content
Format Guide 7 min read

What happens when you save the same JPEG ten times?

A controlled test of fresh exports, repeated saves and a one-pixel edit between saves, with every generation, crop and measurement published.

By ImageGuide Team · Published September 5, 2026

Disclosure: ImageGuide is published by Sirv, an image CDN company. This guide mentions Sirv products. Read how we write these guides.

JPEGcompressionimage qualityartifactsbenchmarks

Ten exports from the same source produced ten identical JPEG files in our test. Ten saves through the previous JPEG did not. Moving the image one pixel before each new save changed the result much more visibly.

“Saving a JPEG loses quality” is useful advice, but it hides the difference between exporting from a master, copying a file and decoding it for another lossy encode. We tested the last two encoding workflows separately.

The source and settings

We used a navy-shirt photograph from Sirv’s public product-image example. We resized it once to 800 × 988 pixels and saved that decoded image as a PNG. That PNG fixture is the reference for this experiment.

The source photograph was already a JPEG. Converting it to PNG did not restore lost detail. It gave every branch the same fixed starting pixels.

The test ran on 5 September 2026 with Sharp 0.35.3, libvips 8.18.3 and the bundled MozJPEG build 0826579. Every JPEG used:

.jpeg({
  quality: 60,
  progressive: true,
  mozjpeg: true,
  chromaSubsampling: '4:2:0'
})

We chose quality 60 to make changes easier to inspect. It is not a recommended default for every photo. Dimensions stayed fixed throughout all three branches.

Three different meanings of another save

BranchInput to each encodeWhat it tests
Fresh exportThe same PNG source every timeRepeated independent exports
ResavedThe JPEG from the previous generationDecode and re-encode without an edit
EditedPrevious JPEG, shifted left by one pixel after generation oneRe-encoding after changing block alignment

The edit removed the leftmost column and added a white column on the right. That kept dimensions fixed and moved the content relative to JPEG’s block grid. By generation ten, the content had shifted nine pixels. It is an intentionally controlled edit, not a simulation of every image editor.

Copying or renaming a JPEG is outside this experiment. Those file operations do not themselves decode and re-encode the pixels.

The measured results

BranchGeneration 1Generation 5Generation 10
Fresh export37,043 bytes37,043 bytes37,043 bytes
Resaved37,043 bytes36,714 bytes36,713 bytes
Edited between saves37,043 bytes33,902 bytes32,391 bytes

All ten fresh exports had the same SHA-256 hash. The unedited re-encodes changed mostly in the first few generations. The generation-five and generation-ten files differed by just one byte in size, but their hashes were different. Similar size is not evidence of identical pixels.

We also measured mean absolute error against the PNG’s decoded RGB values for the two unshifted branches:

BranchGeneration 1Generation 5Generation 10
Fresh export3.23733.23733.2373
Resaved3.23733.28113.2814

This metric averages absolute channel differences on a 0-to-255 scale. It is not a perceptual quality score. We did not compute it for the edited branch because the spatial shift would confuse alignment error with compression error.

The full JSON report contains every file’s size, hash and eligible error measurement. The experiment does not support a rule that every additional save causes the same amount of damage. It also does not establish that repeated saves are harmless on other images or with other settings.

Each image below is a 220 × 220 PNG crop decoded from its JPEG. The crops use the same coordinates. The last one therefore also contains the deliberate nine-pixel content shift.

Shirt fabric, buttons and embroidered logo after the first JPEG export
First export from the PNG reference.
The same shirt region after ten unedited JPEG encodes
Ten generations with no intervening edit.
Softer shirt texture and changed edges after shifting the image between JPEG encodes
Ten generations with a one-pixel shift before each encode after the first.

Open the first JPEG, tenth unedited JPEG and tenth edited JPEG at their native size. In this example, the edited branch loses more of the fine fabric texture. Its smaller file is not an optimization success if that texture matters to the product.

Use the comparison tool for a movable comparison. The labeled images here also work without dragging a control.

Reproduce all thirty encodes

From the ImageGuide checkout with dependencies installed:

node public/experiments/2026-09/run.mjs jpeg

The script asserts that all fresh-export hashes match and that decoded buffer lengths stay constant. It writes all thirty JPEGs beside the report. Each row’s file field names its downloadable file under /experiments/2026-09/.

You can repeat the experiment on another source by replacing photo.png in a separate working copy. Keep the source dimensions at least 580 × 630 pixels for the fixed detail-crop coordinates, or adjust those coordinates in the script. Record the new source hash rather than comparing the resulting numbers as if they came from our fixture.

Keep an editable source

When a customer asks for a different crop, export it from the master or editing document. Avoid downloading a previously compressed website thumbnail and treating it as a new original.

If the JPEG is all you have, make the necessary edits together and export once. Increasing the quality setting on that last export cannot recover texture already discarded. For damaged files, the JPEG artifact guide covers repair options and their limits.

Keep the source, the export settings and the final output. That makes the next revision an independent export instead of another generation in a lossy chain.

Sources

Related Resources

Format References

Resize and deliver images with Sirv

Upload your images to Sirv, then request the sizes and formats your pages need.

Get started