How diffing works
Pixel by pixel comparison fails on real websites. One extra pixel of header height moves every element below it, so a naive diff marks the entire page as changed. WP Diff instead compares regions, an approach based on the Browserbite and CrossCheck research on cross browser testing.
The pipeline
Section titled “The pipeline”flowchart TD
A["Baseline screenshot"] --> GA["Grayscale conversion"]
B["Current screenshot"] --> GB["Grayscale conversion"]
GA --> H["Harris corners + Canny edges"]
H --> D["Dilation (10px down to 4px)"]
D --> BL["Blob analysis: contours to bounding boxes"]
BL --> SP["Split boxes larger than 350px"]
SP --> ROI["Regions of interest"]
ROI --> M{"Template match each region
near its old position"}
GB --> M
M -- "grey score below 0.95" --> FS["Flag: structure changed"]
M -- "grey score 0.95 or higher" --> CC{"Colour verification
compare matched patches in colour"}
A --> CC
B --> CC
CC -- "channel delta 14 or higher" --> FC["Flag: colour changed"]
CC -- "channel delta below 14" --> OK["Match: not flagged"]
FS --> MG["Merge nearby flagged boxes"]
FC --> MG
MG --> OUT["Changed regions outlined
on both screenshots"]
The pipeline runs in both directions. Baseline regions are searched for in the current screenshot to find removed or changed content, and current regions are searched for in the baseline to find additions.
Step by step
Section titled “Step by step”- Grayscale conversion. Structure detection works on intensity, not colour. Colour is checked separately in step 5.
- Feature detection. Harris corners find points where intensity changes sharply in every direction, which mostly means text and crisp UI detail. On its own that behaves like a text detector, so Canny edge detection is combined with it to cover photos, illustrations, flat colour blocks, and anything else with a visible boundary.
- Dilation and blob analysis. The feature mask is dilated so dense clusters merge into connected blobs. Each blob’s bounding box becomes a region of interest. The dilation size steps down from 10px until regions are reasonably small, and anything over 350px is split so changes stay localised.
- Region matching. Each region’s pixels are searched for in the other screenshot with normalised template matching, inside a window around the region’s old position. The window allows generous vertical drift and a little horizontal drift. A region that only moved still scores near 1.0 and stays quiet. This is what makes layout shifts a non event.
- Colour verification. Template matching on grayscale has a blind spot: two colours with the same luminance look identical to it. A red button turning green can survive step 4. So every grayscale match is verified in colour by comparing the mean per channel difference of the matched patches. A large delta flags the region even though its structure matched.
- Merge and report. Flagged boxes within 24px of each other merge into one region, and the result is outlined on both screenshots.
Tuning
Section titled “Tuning”Two thresholds control sensitivity, both per diff request:
| Threshold | Default | Meaning |
| --- | --- | --- |
| threshold | 0.95 | Minimum normalised correlation for a structural match |
| colour delta | 14 | Maximum mean per channel difference before a matched region is flagged |
Raise the structural threshold to catch subtler changes at the cost of more noise from font rendering differences. The colour delta mainly guards against same luminance hue changes and rarely needs adjusting.
Seeing it run
Section titled “Seeing it run”Every step of this pipeline is observable. Turn on developer mode in the plugin to see the corner masks, dilated blobs, region overlays, and per region match scores for any compared page.
References
Section titled “References”- Saar et al., Browserbite: Cross-Browser Testing via Image Processing
- CrossCheck: Combining Crawling and Differencing to Better Detect Cross-browser Incompatibilities in Web Applications