[{"data":1,"prerenderedAt":313},["ShallowReactive",2],{"navigation_docs":3,"-learn-how-image-diffing-works":93,"-learn-how-image-diffing-works-surround":308},[4,41,46,89],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":40},"Learn","i-lucide-book-open","\u002Flearn","1.learn",[10,15,20,25,30,35],{"title":11,"path":12,"stem":13,"icon":14},"What visual regression testing is","\u002Flearn\u002Fwhat-is-visual-regression-testing","1.learn\u002F1.what-is-visual-regression-testing","i-lucide-eye",{"title":16,"path":17,"stem":18,"icon":19},"How image diffing works","\u002Flearn\u002Fhow-image-diffing-works","1.learn\u002F2.how-image-diffing-works","i-lucide-diff",{"title":21,"path":22,"stem":23,"icon":24},"Why screenshots flake","\u002Flearn\u002Fwhy-screenshots-flake","1.learn\u002F3.why-screenshots-flake","i-lucide-shuffle",{"title":26,"path":27,"stem":28,"icon":29},"Baselines and approval","\u002Flearn\u002Fbaselines-and-approval","1.learn\u002F4.baselines-and-approval","i-lucide-check-check",{"title":31,"path":32,"stem":33,"icon":34},"Running in CI","\u002Flearn\u002Frunning-in-ci","1.learn\u002F5.running-in-ci","i-lucide-workflow",{"title":36,"path":37,"stem":38,"icon":39},"Choosing a tool","\u002Flearn\u002Fchoosing-a-tool","1.learn\u002F6.choosing-a-tool","i-lucide-list-checks",false,{"title":42,"path":43,"stem":44,"icon":45},"Compare the tools","\u002Fcompare","2.compare","i-lucide-scale",{"title":47,"path":48,"stem":49,"children":50,"icon":53},"Manifest","\u002Fmanifest","3.manifest\u002F1.index",[51,54,59,64,69,74,79,84],{"title":52,"path":48,"stem":49,"icon":53},"The run manifest","i-lucide-file-json",{"title":55,"path":56,"stem":57,"icon":58},"The format","\u002Fmanifest\u002Fformat","3.manifest\u002F2.format","i-lucide-file-code",{"title":60,"path":61,"stem":62,"icon":63},"Reading manifests","\u002Fmanifest\u002Freading","3.manifest\u002F3.reading","i-lucide-book-open-check",{"title":65,"path":66,"stem":67,"icon":68},"Merging the manifests of a run","\u002Fmanifest\u002Fmerging","3.manifest\u002F4.merging","i-lucide-merge",{"title":70,"path":71,"stem":72,"icon":73},"Writing manifests","\u002Fmanifest\u002Fwriting","3.manifest\u002F5.writing","i-lucide-pencil-line",{"title":75,"path":76,"stem":77,"icon":78},"Converting to and from other formats","\u002Fmanifest\u002Fconverting","3.manifest\u002F6.converting","i-lucide-arrow-left-right",{"title":80,"path":81,"stem":82,"icon":83},"Reference","\u002Fmanifest\u002Freference","3.manifest\u002F7.reference","i-lucide-list",{"title":85,"path":86,"stem":87,"icon":88},"Schema reference","\u002Fmanifest\u002Fschema","3.manifest\u002F8.schema","i-lucide-braces",{"title":90,"path":91,"stem":92,"icon":14},"Viewer","\u002Fviewer","4.viewer",{"id":94,"title":16,"body":95,"description":301,"extension":302,"links":303,"meta":304,"navigation":305,"path":17,"seo":306,"stem":18,"__hash__":307},"docs\u002F1.learn\u002F2.how-image-diffing-works.md",{"type":96,"value":97,"toc":290},"minimark",[98,102,107,144,151,155,158,162,198,201,205,212,216,219,255,259,262,266],[99,100,101],"p",{},"Two images of the same size are compared pixel by pixel. For each position the comparator asks: are these two colours \"the same\"? Count the positions where the answer is no, draw them in red, and you have a diff image and a mismatch count. Everything a tool adds on top is about making that question less naive.",[103,104,106],"h2",{"id":105},"colour-distance","Colour distance",[99,108,109,110,114,115,118,119,123,124,131,132,137,138,143],{},"The first refinement is that ",[111,112,113],"code",{},"rgb(120, 120, 120)"," and ",[111,116,117],{},"rgb(121, 120, 120)"," are not a difference anyone can see. Comparators compute a distance between the two colours and treat anything under a ",[120,121,122],"strong",{},"per-pixel threshold"," as equal. ",[125,126,130],"a",{"href":127,"rel":128},"https:\u002F\u002Fgithub.com\u002Fmapbox\u002Fpixelmatch",[129],"nofollow","pixelmatch",", the engine inside Playwright, Vitest and jest-image-snapshot, converts to the YIQ colour space, where luminance differences weigh more than hue differences, because that matches how eyes work. ",[125,133,136],{"href":134,"rel":135},"https:\u002F\u002Fgithub.com\u002Fgemini-testing\u002Flooks-same",[129],"looks-same"," uses CIEDE2000, a perceptual colour difference formula. ",[125,139,142],{"href":140,"rel":141},"https:\u002F\u002Fgithub.com\u002Frsmbl\u002FResemble.js",[129],"Resemble.js",", behind BackstopJS, offers tolerances per channel.",[99,145,146,147,150],{},"The per-pixel threshold is the ",[111,148,149],{},"threshold"," option in pixelmatch-based tools (0 to 1, default 0.1) and the \"ignore colors\" or \"ignore less\" modes in Resemble. Raising it hides subtle colour shifts; lowering it makes anti-aliasing visible.",[103,152,154],{"id":153},"anti-aliasing","Anti-aliasing",[99,156,157],{},"Text and diagonal edges are drawn with partially transparent pixels along their border, and the exact values depend on the font engine, the GPU and the browser version. Two renders of the same text can differ in hundreds of pixels that no one can tell apart. pixelmatch detects likely anti-aliased pixels (a pixel that sits between two very different neighbours) and ignores them by default; odiff and looks-same do the same. This single feature is why pixel-level comparison works at all for text-heavy UIs.",[103,159,161],{"id":160},"the-image-level-threshold","The image-level threshold",[99,163,164,165,168,169,172,173,176,177,176,180,176,183,186,187,190,191,194,195,197],{},"After per-pixel decisions, the tool has a count of differing pixels. The second threshold says how many are allowed before the comparison fails: as an absolute number (",[111,166,167],{},"maxDiffPixels"," in Playwright, ",[111,170,171],{},"failureThreshold"," in pixels for jest-image-snapshot) or as a share of all pixels (",[111,174,175],{},"maxDiffPixelRatio",", ",[111,178,179],{},"allowedMismatchedPixelRatio",[111,181,182],{},"errorThreshold",[111,184,185],{},"misMatchThreshold"," in percent for BackstopJS). The ",[125,188,189],{"href":48},"manifest"," records this share as ",[111,192,193],{},"comparison.diffRatio"," next to the ",[111,196,149],{}," it was checked against.",[99,199,200],{},"A ratio is portable across viewports, a pixel count is not: 100 pixels is a lot in a 200×100 icon and nothing in a full page. Most teams end up with a small ratio (0.1% to 1%) and a handful of exceptions.",[103,202,204],{"id":203},"dimension-mismatch","Dimension mismatch",[99,206,207,208,211],{},"If the actual image has a different size, there is nothing to compare. Tools either fail outright (Playwright, with a message about the expected size) or compare the overlapping region and report the difference (Resemble reports ",[111,209,210],{},"isSameDimensions"," and the delta). A size change usually means content moved below the fold or a scrollbar appeared; it is a real finding, not noise.",[103,213,215],{"id":214},"perceptual-comparison","Perceptual comparison",[99,217,218],{},"Pixel comparison with anti-aliasing detection is good enough for most UIs. Where it is not, the alternatives are:",[220,221,222,239,245],"ul",{},[223,224,225,228,229,234,235,238],"li",{},[120,226,227],{},"SSIM",", the ",[125,230,233],{"href":231,"rel":232},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FStructural_similarity_index_measure",[129],"structural similarity index",", compares local patterns of luminance, contrast and structure rather than single pixels. jest-image-snapshot offers it as ",[111,236,237],{},"comparisonMethod: 'ssim'","; it is more tolerant of compression and sub-pixel shifts, and slower.",[223,240,241,244],{},[120,242,243],{},"Hosted services"," (Applitools, Percy, Chromatic) run their own comparison on the server, often with layout-aware or AI-assisted modes that try to tell \"the text moved\" from \"the text changed\".",[223,246,247,250,251,254],{},[120,248,249],{},"Ignore regions"," mask areas that are expected to change (a clock, an avatar, an ad slot). Playwright's ",[111,252,253],{},"mask"," option, odiff's ignore regions and the stylesheet trick (inject CSS that hides the element before the screenshot) all do this.",[103,256,258],{"id":257},"reading-a-diff-image","Reading a diff image",[99,260,261],{},"Most tools draw differing pixels in a highlight colour over a faded copy of the baseline. jest-image-snapshot and Roborazzi write a three-panel composite (baseline, diff, actual) in one file; Playwright writes three separate files and shows them with a slider in its report. When you build your own review step, a slider between baseline and actual is what reviewers reach for first; the diff image is for finding the one changed pixel in a sea of same.",[103,263,265],{"id":264},"further-reading","Further reading",[220,267,268,274,282],{},[223,269,270,273],{},[125,271,130],{"href":127,"rel":272},[129],": 150 lines, readable in ten minutes, and the reference for how most tools decide.",[223,275,276,281],{},[125,277,280],{"href":278,"rel":279},"https:\u002F\u002Fgithub.com\u002FdmtrKovalenko\u002Fodiff",[129],"odiff",": the same ideas, compiled, for when pixelmatch is the slow part of your run.",[223,283,284,289],{},[125,285,288],{"href":286,"rel":287},"https:\u002F\u002Fplaywright.dev\u002Fdocs\u002Ftest-snapshots#options",[129],"Playwright's screenshot options",", the clearest documentation of the two thresholds.",{"title":291,"searchDepth":292,"depth":292,"links":293},"",2,[294,295,296,297,298,299,300],{"id":105,"depth":292,"text":106},{"id":153,"depth":292,"text":154},{"id":160,"depth":292,"text":161},{"id":203,"depth":292,"text":204},{"id":214,"depth":292,"text":215},{"id":257,"depth":292,"text":258},{"id":264,"depth":292,"text":265},"Pixel comparison, colour distance, anti-aliasing detection, perceptual metrics, and what the two thresholds every tool exposes actually control.","md",null,{},{"icon":19},{"title":16,"description":301},"LcnOn8eQIXtQSHM4vJ1-x2HA15QQGZ_dZHyHxlJRkG4",[309,311],{"title":11,"path":12,"stem":13,"description":310,"icon":14,"children":-1},"A screenshot of your UI compared against one you approved earlier, so that unintended visual changes fail the build and intended ones get a review.",{"title":21,"path":22,"stem":23,"description":312,"icon":24,"children":-1},"Fonts, animations, GPU rasterisation, scrollbars, timing and the one fix that removes most of it, rendering in a pinned environment.",1791993875572]