Visual regression: masking dynamic regions in CI
Some regions cannot be made deterministic (third-party embeds, ads, live charts, user-generated content). Rather than fight them, mask those regions so the comparison ignores their pixels while still catching real changes elsewhere.
What this error means
Visual tests keep flagging the same widget (an ad slot, a map tile, a chart, a relative timestamp) even though the surrounding UI is unchanged. Freezing does not help because the content is external or genuinely variable.
# recurring diff on the same region every run:
# third-party ad iframe and a live "online users" counterCommon causes
Genuinely variable or third-party content
Ad slots, embedded maps, and live counters change every run and cannot be frozen from your test, so their pixels always differ.
User-generated or randomized content in the frame
Avatars, names, or charts driven by real data vary between runs, producing diffs unrelated to the code change.
How to fix it
Mask regions in Playwright
Pass mask selectors so Playwright paints over those elements before comparing.
await expect(page).toHaveScreenshot('page.png', {
mask: [page.locator('.ad-slot'), page.locator('.live-counter')],
});Use ignore regions in the visual service
Percy and Applitools support ignoring regions so the volatile area is excluded from the diff.
await percySnapshot(page, 'Page', {
percyCSS: '.ad-slot, .live-counter { visibility: hidden; }',
});How to prevent it
- Mask third-party and live regions that cannot be frozen.
- Prefer freezing over masking where the content is under your control.
- Keep masks tight so they do not hide real regressions nearby.