Image Diff, Explained
After reading this you will know how a pixel-exact image comparison works, how to read a difference heatmap and per-channel error numbers, and when a visible difference actually matters.
What an image diff measures
An image diff compares two images one pixel at a time. For each position it reads the color in image A and the color in image B, subtracts them, and records the size of the difference. The result is three numbers: a heatmap showing where the images differ, a percentage of pixels that changed, and per-channel error metrics that summarize how much they changed.
Here is the hook. Suppose you take two screenshots of the same web page, one before a code change and one after. They look identical to your eye. A pixel diff can tell you that 1.8% of pixels changed, all clustered in one button that shifted by two pixels. Your eye smoothed over it; the arithmetic did not.
This is exact comparison, not perceptual comparison. It does not know that a color shift of 1 in the blue channel is invisible while a shift of 1 in a face is obvious. It reports raw differences. That literal-mindedness is the point: it catches regressions that a human reviewer misses.
When to use it, and when not to
Reach for a pixel diff when the two images are supposed to be the same, or nearly the same, and you want proof. Good cases:
- Screenshot testing: verify that a UI change touched only what you intended.
- Render regression: compare a 3D or chart render against a known-good reference frame.
- Compression checks: see exactly which regions a codec altered and by how much.
- Export verification: confirm that a "lossless" save round-trip changed nothing.
Do not use it to compare two genuinely different photos, or the same scene shot twice by hand. A one-pixel camera shake turns every edge into a difference, and the percentage explodes to something like 60% even though a person would call the photos identical. Pixel diff answers "are these bit-for-bit close?", not "do these look alike?".
A pixel diff assumes the two images are aligned. If one is shifted, scaled, or cropped differently, almost every pixel will register as changed. Align first, diff second.
The formula and the intuition
Each pixel has up to four channels: red, green, blue, and optionally alpha. Take a single channel c at pixel position (x, y). The absolute difference is:
Here A_c(x, y) is the channel value in image A (an integer from 0 to 255 for 8-bit images) and B_c(x, y) is the same channel in image B. The result d_c ranges from 0 (identical) to 255 (black versus white).
A pixel counts as "changed" when any of its channels differs by more than a threshold t. With t = 0 the comparison is exact: any difference at all flags the pixel. Raising the threshold ignores tiny differences from rounding or compression noise.
To summarize the whole image, two error metrics are common. Mean absolute error averages the per-channel differences across every pixel and channel:
where N is the total number of channel samples (pixels times channels) and the sum runs over all of them. Root mean square error squares each difference before averaging, so it punishes large single-pixel errors more heavily:
If every difference is the same size, MAE and RMSE are equal. When a few pixels differ a lot while most match, RMSE climbs above MAE. The gap between them tells you whether the error is spread out or concentrated.
A worked example
Four pixels, by hand
Take two tiny 2x2 RGB images. Ignore alpha. Image A and image B differ in only one pixel.
| Pixel | A red | B red | Difference |
|---|---|---|---|
| top-left | 100 | 100 | 0 |
| top-right | 100 | 100 | 0 |
| bottom-left | 100 | 100 | 0 |
| bottom-right | 100 | 160 | 60 |
- Count changed pixels with threshold t = 0. Only the bottom-right pixel differs, so
1of4pixels changed:25%. - Compute MAE. There are 4 \times 3 = 12 channel samples. Eleven differ by
0, one differs by60. Sum is60, so \text{MAE} = 60 / 12 = 5.0. - Compute RMSE. The squared differences sum to 60^2 = 3600. Divide by 12 to get
300, then take the square root: \text{RMSE} = \sqrt{300} \approx 17.32.
Notice the gap. MAE is 5.0 but RMSE is 17.32, more than three times larger. That gap is the signature of a single concentrated error rather than a uniform haze. If instead all twelve samples had differed by 5, both metrics would read 5.0.
Reading the heatmap and the numbers
The heatmap paints each pixel by how much it changed. Dark or transparent areas are identical. Bright areas differ. Most tools scale brightness to the difference size, so a faint gray mark is a small change and a hot spot is a large one. Look at the shape of the bright regions first:
- A sharp outline around a shape
- Something moved or resized. Edges light up because the object no longer sits on the same pixels.
- A solid filled block
- A region changed color or content wholesale, such as a swapped image or a repainted panel.
- A faint even wash across everything
- Global change: gamma, brightness, or a re-encode at a different quality. The whole frame shifted slightly.
- Scattered single-pixel speckle
- Compression or dithering noise. Usually safe to ignore with a small threshold.
Then read the summary numbers. A changed-pixel percentage of 0% means the images are bit-for-bit identical over the compared region. Anything above that needs a glance at the heatmap to decide whether the change was intended. Use MAE for a sense of average drift and RMSE to detect concentrated errors, as the worked example showed.
Use the swipe slider to align your judgment with the heatmap. Swipe to the spot the heatmap marks hot, then flip back and forth. If you cannot see the change even when you know exactly where to look, it probably does not matter for a human viewer.
Common mistakes
Three errors account for most confusing results.
Different sizes. When the two images have different dimensions, only the overlapping region (anchored at the top-left corner) is compared. If image B is one row taller, that extra row is never examined. A crop that adds pixels on the left or top shifts everything and will read as almost entirely changed.
Alpha and premultiplication. A fully transparent pixel can hold any RGB value. Two transparent pixels with different hidden reds will differ on the red channel even though both render as nothing. If your images have an alpha channel, decide whether to compare it, and be aware that invisible pixels can inflate MAE.
Color profile mismatch. The same photo saved with an sRGB profile and again with no profile can decode to slightly different values on some viewers. The diff then shows a faint uniform wash that is really a metadata issue, not a content change. Check profiles before trusting a small nonzero MAE.
Confusing exact and perceptual. A JPEG re-saved at quality 95 changes almost every pixel by 1 or 2 levels. The changed-pixel percentage at threshold 0 will read near 100%, which sounds alarming but is invisible. Raise the threshold to 3 and the number collapses toward the real edits.
Related tools on this site
If a diff shows a faint uniform wash, the cause is often metadata rather than content. The EXIF Viewer & Remover lets you see and strip the color profile and other tags before comparing. When you want to know the exact value at a hot spot in the heatmap, the Image Color Picker & Palette reads individual pixel colors. To characterize a whole region rather than a point, the Dominant & Average Color tool summarizes it. And if the difference you are chasing is text that appeared or moved, the Image OCR tool can pull the words out for a direct comparison.
Frequently asked questions
Why does my diff show 100% changed when the images look identical?
Almost always a sub-pixel shift, a resize, or a re-encode at a slightly different quality. Every pixel changed by a tiny amount, so at threshold 0 they all count. Raise the threshold to 2 or 3, or align the images, and the number should drop to the real difference.
What is a good MAE or RMSE value?
There is no universal cutoff, because the scale is 0 to 255. As a rough guide, an MAE under 1.0 usually means differences invisible to the eye, an MAE around 5 to 10 is a noticeable but small shift, and anything above 30 is a clear content change. Judge against the heatmap, not the number alone.
Does the tool upload my images anywhere?
No. The comparison runs entirely in your browser. Your images never leave your device.
Why compare only the overlapping region for different-sized images?
There is no meaningful difference to compute where one image has no pixel. Anchoring at the top-left and comparing the overlap is the most predictable choice, and it matches how most screenshots are cropped from the same origin.
Should I include the alpha channel in the diff?
Include it if transparency carries meaning in your images, for example a UI with translucent overlays. Exclude it if you only care about visible color, since transparent pixels can hold arbitrary hidden RGB values and inflate the error metrics.