Add an animated demo to your site without hurting Core Web Vitals
Reserve the demo's space before it loads, show a plain poster image first, and load the player only when it nears the viewport. In a hero, keep that first image out of lazy loading, then test with and without the demo, because no general rule can tell you the cost on your page.
Everything here comes from Google's and the W3C's documentation, read on 31 October 2026. We haven't included benchmarks because a number from our pages wouldn't tell you about yours, and the last section shows how to get your own.
The three targets, and how a demo touches each
web.dev defines a good experience at the 75th percentile of page loads, split by mobile and desktop. A page passes only if it meets all three. The poor bands below come from PageSpeed Insights.
| Metric | Good | Poor | How a demo can hurt it |
|---|---|---|---|
| Largest Contentful Paint (LCP) | 2.5 s or less | Over 4 s | A large demo in the hero loads late, or its first image waits on a script |
| Interaction to Next Paint (INP) | 200 ms or less | Over 500 ms | Heavy scripts keep the browser busy when someone taps |
| Cumulative Layout Shift (CLS) | 0.1 or less | Over 0.25 | The embed loads with no reserved space and pushes the page down |
A demo below the fold mostly risks CLS and page weight. A demo in the hero risks all three.
Choose the format first
| Format | Good for | Watch out for |
|---|---|---|
| Static image | Heroes where speed comes first | No motion |
MP4 or WebM in a <video> tag | A looping or click-to-play film you host | Needs a poster and a pause control |
| Animated GIF | Places that can't take video, such as email | Large files |
| Iframe or embed script | Interactive demos, analytics, updates without a redeploy | Extra requests and a third party |
Avoid large GIFs on web pages. web.dev's example took a 3.7 MB GIF to a 551 KB MP4 or a 341 KB WebM. That article dates from 2018 and it's one file, so treat it as a direction, not a ratio. It recommends <video autoplay loop muted playsinline> for GIF-style clips.
Reserve the space
This is the cheapest fix and it solves most demo CLS. Google's advice is to "Always include width and height size attributes on your images and video elements", or to reserve the space with CSS aspect-ratio. Ads, embeds and iframes can all push content down, and the same article notes that embed platforms "don't always reserve space for their widgets", so don't assume yours does.
- For a video:
<video width="1280" height="720" poster="demo.jpg" ...>plusmax-width:100%; height:autoin your CSS. - For an iframe:
style="width:100%;aspect-ratio:16/9;border:0". - For a script embed: put it in a wrapper with its own
aspect-ratio, so the slot exists before the script runs.
Then load the page on a slow connection and watch the content below the demo. It shouldn't move. Use transform for any motion on your own page: web.dev notes it lets you animate "without triggering layout shifts", while animating top and left can trigger them.
Give it a poster, and treat the hero differently
For a <video> element, LCP counts the poster image load time or the first frame presentation time, "whichever is earlier". So a poster helps in two ways: it fills the slot and it can be your LCP element.
If the demo is above the fold, its first image is probably your LCP element, and that changes what you should do:
- Keep the poster in your HTML, not injected by a script. web.dev wants the LCP resource discoverable from the HTML source.
- Don't lazy load it. "Never lazy-load your LCP image", says that article, and the video guide says the same of videos that are LCP elements.
- Use
fetchpriority="high"on an<img>that's likely to be the LCP element, on no more than one or two images. - Compress the poster, because a slow poster is a slow LCP.
The dependable hero pattern is a plain <img> that paints first, and a player that replaces it only after the page has loaded or when someone presses play.
Lazy load everything below the fold
For an iframe offscreen, add loading="lazy". web.dev explains that it defers offscreen iframes until the user scrolls near them. Be realistic: the Chromium research it quotes, from October 2019, found 2 to 3% median data savings across pages, and not every page has a heavy iframe. If yours does, the same article's embed figures show why it matters, such as about 500 KB for a YouTube embed on initial load. That figure isn't dated.
For a self-hosted video, preload="none" gives the best chance of avoiding the download, and poster holds the place. With loading="lazy", autoplay videos won't start until they're in or near the viewport, says the video guide.
A step further is the facade pattern: a static element that looks like the embed, which preconnects on hover and replaces itself with the real thing on click. Chrome's Lighthouse removed its facade audit in version 13, but the pattern is still valid. It's a good fit for heavier interactive demos that most visitors won't play.
Keep the page responsive
Loading fewer scripts helps INP most. Don't run more than one player on a page if you can avoid it, and pause anything that's offscreen, since an animation nobody can see still uses the CPU. A demo on the home page shouldn't load on your pricing or docs pages through a shared template.
Motion and accessibility
Performance isn't the only rule that applies to an autoplaying demo.
WCAG 2.2 success criterion 2.2.2, Pause, Stop, Hide, is Level A. Its Understanding page puts it this way: for moving content that "(1) starts automatically, (2) lasts more than five seconds, and (3) is presented in parallel with other content, there is a mechanism for the user to pause, stop, or hide it", unless the motion is essential. Animations count. A four-second clip on a loop keeps moving past five seconds, so we'd treat it as covered.
| Your demo | What you need |
|---|---|
| Autoplays and stops within five seconds | Nothing under 2.2.2 |
| Autoplays longer, or loops | A visible pause control |
| Only plays when pressed | Already meets it; give it normal controls |
| Plays on hover or when scrolled into view | The W3C page counts this as starting automatically, so treat it like autoplay |
Next, respect the prefers-reduced-motion media feature. It tells you the visitor has asked for less non-essential motion. MDN notes that scaling or panning large objects can trigger vestibular problems. For a demo, the simple answer is to show the poster with a play button:
- In CSS, hide the autoplaying version inside
@media (prefers-reduced-motion: reduce). - In script, check
matchMedia("(prefers-reduced-motion: reduce)").matchesbefore callingplay().
Two more rules from WCAG 2.2: nothing should flash more than three times in any one-second period (2.3.1, Level A), and prerecorded video with sound needs captions (1.2.2, Level A). Give the demo a text alternative too, so the point of it survives if it never plays.
Measure your own page
Compare the same page with and without the demo, because one score tells you nothing about the demo's cost.
- Get a baseline. Run the page in PageSpeed Insights. Note that it shows two things: field data from real Chrome users over the previous 28 days, and a lab run. Field data won't reflect a change for weeks, and a low-traffic page may not have any.
- Test locally. Open DevTools and the Performance panel. It shows local LCP and CLS straight away, and INP once you interact. Set the network to Fast 4G and slow the CPU, so your laptop acts more like a phone.
- Compare. Add a query flag or a preview branch that removes the demo. Load each version several times with the cache disabled, and record the middle value of each metric in a table.
- Find the cause. Check what the LCP element actually is. If it's the demo's poster, fix that first. Scroll the page and watch CLS: it should stay put when the embed loads.
- Watch real users. Add Google's web-vitals library with
npm install web-vitals, and sendonLCP,onINPandonCLSvalues to your analytics. After launch, Search Console's Core Web Vitals report shows groups of similar pages, by device, once it has enough data.
Two cautions. Lighthouse can't measure INP, so it uses Total Blocking Time as a proxy. And the web-vitals library says its APIs have "no visibility into" iframe content, so on pages with iframes its numbers can differ from Chrome's field data. If the two disagree on an embed, believe the field data and investigate the difference.
| Version | LCP | CLS | INP | Page weight |
|---|---|---|---|---|
| Without demo | ||||
| With demo, your current setup | ||||
| With demo, after fixes |
If you use a Programmable embed
Here's what the code does today. We haven't published speed measurements for it, so use the steps above on your own page.
- One-line script. It loads with
async, and the file is about 17 KB before compression. It starts the iframe when the box comes within 300 px of the viewport, or when half of it is visible for website components. The iframe also hasloading="lazy". - Reserved space. The script makes a 16:10 box, then switches to the film's true shape once its details arrive. If your film isn't 16:10, wrap the script in an element with the right
aspect-ratio, or the page can shift. - Plain iframe. It sets
aspect-ratioinline and usesloading="lazy", so the slot is reserved from the first paint. In a hero, remove the lazy attribute or show an image first. - Poster. Website components show a poster, which the script adds and lazy loads. That's fine below the fold, but in a hero it won't be discoverable in your HTML, so export a PNG still from Export and paint that first.
- Motion. Embeds pause when offscreen and hold a still frame under reduced motion, and components show a play button instead of starting.
- Pause control. A looping embed has no visible pause button. If your film runs over five seconds on a loop, use Click through steps from the Share panel, or add your own button that calls
element.programmable.send("programmable:pause")on the embed's host element.
If you want no third party at all, export the MP4 and host it yourself with the <video> pattern above. You'd lose the embed's analytics and lead capture, but it's the simplest thing to measure and control. The site builder guides cover pasting an embed into Webflow, Framer, WordPress, Shopify and Squarespace, and the marketplace has animated components you can ship as code in your own bundle.
One SEO note: Google says a watch page's main purpose is to show a single video, and a page where the video is secondary won't qualify as one. A demo on your home page is there for visitors, so don't plan on video results from it. Our guide to choosing a demo video or an interactive demo can help you decide what to embed.
Common questions
Does autoplay hurt Core Web Vitals?
Autoplay isn't a metric. What matters is the file size, whether the slot is reserved, and when the first image paints. A muted, short, well-compressed video with a poster can be fine; a large one without a poster won't be.
Is a poster image or the video counted for LCP?
For a video element, LCP uses the poster load time or the first frame presentation time, whichever is earlier. That's why a light poster in your HTML matters for a hero.
Should I lazy load a demo in my hero?
Don't lazy load the image that paints first. You can delay the player behind that image, so the visitor sees something straight away and the heavier part loads later.
Do Core Web Vitals scores count content inside iframes?
Chrome's field data and JavaScript measurement can differ here. Google's web-vitals library says it can't see into iframes, and that this can cause differences from the Chrome User Experience Report. Compare both on pages with embeds.
Sources
- web.dev: Web Vitals (last updated 31 October 2024)
- web.dev: Largest Contentful Paint (last updated 4 September 2025)
- web.dev: Optimize LCP (last updated 31 March 2025)
- web.dev: Cumulative Layout Shift (last updated 12 April 2023)
- web.dev: Optimize CLS (last updated 7 February 2025)
- web.dev: Lazy loading video (last updated 2 July 2026)
- web.dev: Iframe lazy loading (last updated 23 September 2024)
- web.dev: Replace GIFs with video (last updated 5 November 2018)
- Chrome for Developers: third-party facades (last updated 1 December 2020)
- Chrome for Developers: Performance panel (last updated 17 September 2024)
- PageSpeed Insights documentation (last updated 21 October 2024)
- Search Console Help: Core Web Vitals report
- GoogleChrome/web-vitals
- W3C: WCAG 2.2 (12 December 2024)
- W3C: Understanding Pause, Stop, Hide
- MDN: prefers-reduced-motion (last modified 10 June 2026)
- Google Search Central: video best practices (last updated 18 December 2025)