How to Reduce Percy Screenshot Diffs Caused by Animations
Make Percy snapshots more consistent by waiting for the right page state, controlling motion, and stabilizing changing content.
Percy diffs caused by animations are usually capture-timing differences: the same page was captured at a different frame, transition state, or loading state. Make the page ready before taking the snapshot, disable motion that is outside the test’s purpose, and keep changing data consistent. If motion itself is under test, leave it enabled and make the tested state reproducible.
Percy describes snapshot stabilization, but the sources reviewed for this guide do not establish current, version-specific syntax for configuring animation handling across Percy SDKs. The code below uses Playwright to prepare a stable page and save a reproducible local screenshot; check the current reference for your Percy SDK before using any Percy-specific CSS or snapshot option.
1. Find what is changing
Compare the diff with the page at capture time. Common sources include CSS keyframe animations and transitions, hover effects, loading skeletons, animated icons, GIFs, auto-rotating banners, and autoplay video. A CSS-only override will not necessarily stop JavaScript-driven motion, GIFs, video, canvas animation, or every animated SVG.
First decide whether the motion belongs in this test. For a static layout or styling check, stabilize it. For a test of animation behavior or its resulting states, keep the relevant motion and control when and where the snapshot occurs.
2. Wait for the intended page state
Take the snapshot after the route and component state you intend to check are visible, relevant data has loaded, and lazy-loaded content is present. Prefer observable readiness conditions—such as a component appearing or a loading indicator disappearing—to an arbitrary sleep. A fixed delay can still capture different states when network or rendering time varies.
Example using Playwright and Node.js. It waits for a page-specific readiness marker, injects a motion override, and saves a screenshot. Replace the URL and selector with those for your application. This is a runnable local screenshot example, not a claim about Percy’s current SDK configuration syntax.
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
try {
await page.goto('http://localhost:3000/products', { waitUntil: 'domcontentloaded' });
await page.locator('[data-testid="products-ready"]').waitFor({ state: 'visible' });
await page.addStyleTag({ content: `
*, *::before, *::after {
animation: none !important;
transition: none !important;
scroll-behavior: auto !important;
}
` });
await page.screenshot({ path: 'products.png', fullPage: true });
} finally {
await browser.close();
}
Install Playwright and its browser in your project using the installation steps for your environment. The readiness selector must represent the state you actually want to validate; a generic page-load event alone may occur before application data or lazy content is ready.
3. Disable only motion that is irrelevant
For a static visual check, the broad CSS rule below is a useful starting point. CSS transitions count as motion too, so disabling only keyframe animations can leave diffs behind.
*, *::before, *::after {
animation: none !important;
transition: none !important;
}
Apply this only to snapshots where motion is not part of the assertion. A global override can change the intended visual state or hide a bug in an animation-related layout. If possible, scope the override to the relevant test or region. Also pause carousels and auto-rotating content through the application’s own controls when available; CSS may not stop JavaScript timers.
4. Keep changing content stable
If the pixels change because API-backed content changes, control the data separately from animation. Mock the response with repeatable values so the component remains populated and its layout can still be checked. Control time-dependent values where they affect the page. Turning off animation will not stabilize changing text, counters, timestamps, or personalized data.
Hide or mask an element only if it is outside the intended validation scope. Live chat, notification badges, rotating promotions, and ads may be candidates when they are not part of the check. Masking a region that should reveal layout defects weakens the test.
5. Confirm Percy’s SDK-specific configuration
Percy’s published materials describe stabilization at a high level. A Percy changelog entry from 2019 announced Percy-specific CSS as a snapshot option or global SDK configuration, with an example that hid iframes and a historical version requirement. That entry is historical evidence, not confirmation of today’s syntax or behavior in every SDK. See Percy’s Percy Specific CSS changelog entry and verify the current documentation for the SDK and version in your project before shipping configuration code.
Percy’s guidance also recommends waiting for the UI to stabilize, including animations and lazy-loaded components. See Integrating Testing Library Snapshots Using Percy and How to Reduce False Positives in Visual Testing.
6. Troubleshoot diffs that remain
| Symptom | Likely cause | What to do |
|---|---|---|
| Only part of a transition differs | The capture happened at a different point in the transition, or a transition was not disabled. | Wait for the intended state and disable both animations and transitions for static checks. |
| The page changes despite the CSS override | Motion may come from JavaScript, video, GIF, canvas, animated SVG, or a pseudo-element. | Inspect the changing element and pause it through its application control or test setup. Do not assume CSS covers every motion source. |
| Text, numbers, or cards differ | Data, personalization, or time changed between captures. | Mock the response or control the relevant time and user state. Keep content present if its layout matters. |
| Lazy content is missing or shifts | The snapshot started before relevant content loaded or settled. | Wait for the content or a meaningful readiness condition. Check the page at the moment of capture. |
| Hover styling differs | Pointer or focus state may differ at capture time. | Make the intended hover/focus state explicit, or reset it for a test of the default state. |
| A Percy CSS option fails or has no effect | Configuration syntax or behavior may differ by SDK and version. | Check the current reference for the exact SDK version. Do not rely on a historical changelog example as current API documentation. |
| Diff persists after stabilization changes | The baseline may represent a different intended state, or the unstable region may still be in scope. | Inspect both captures and review any baseline update deliberately. Prefer correcting page state over increasing diff tolerance. |
7. Performance, reliability, and cost considerations
Readiness conditions make tests more reliable than arbitrary waits because they tie capture to the state under test. A condition that never becomes true can still stall a run, so give waits a sensible timeout and make failures identify the missing state. Broad CSS overrides are quick to apply but can conceal motion defects; targeted controls preserve more of the behavior being tested.
Mocking data makes content repeatable while retaining the component’s layout. Hiding content can make a snapshot simpler, but removes that region from visual validation. Review masks and baseline changes against the purpose of each test.
The cited Percy guidance does not establish a numeric animation-diff rate or a specific tolerance setting for this problem. Fix timing, motion, and data at their source before changing diff thresholds.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. For a one-off screenshot, a GET request returns an image or PDF. For Percy visual tests, keep using Percy for the comparison workflow; ScreenshotNeo is an alternative when you need a clean website capture without setting up a browser in your own script.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for options. Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up free for 1,000 screenshots a month, no card required.
FAQ
Should I disable animations in every Percy snapshot?
No. Disable motion for checks of static appearance. Keep it enabled when motion or its resulting states are what the test validates, and make the state reproducible.
Will the CSS override stop a video or GIF?
Not necessarily. CSS rules can suppress CSS animations and transitions, but media and script-driven motion may need their own pause or test control.
Should I mask every element that changes?
No. Mask only areas outside the check’s scope. If the changing content’s layout matters, stabilize its data and keep it visible.
Does Percy use the same animation setting in every SDK?
The sources reviewed here do not establish current, version-specific syntax across SDKs. Verify the reference for the SDK and version you use.


