How to Embed an Animated WebP in HTML (the Right Way)
An animated WebP is just an image. You embed it with a plain <img> tag — no <video>, no JavaScript. This is the full reference for doing it correctly.
The simplest case: a plain <img>
This is the part that trips people up coming from video. An animated WebP is not video — it is an image that happens to contain multiple frames, exactly like an animated GIF. Browsers treat it as an image. So you embed it the same way you embed any other image:
<img src="loop.webp" alt="Product walkthrough, looping" width="640" height="360" />That is the whole thing. It autoplays. It loops (if the file was encoded with an infinite loop count, which is the default for most encoders, including the 2WebP converter). There is no autoplay attribute, no muted, no playsinline, none of the <video> ceremony — because it is not a video element. The loop behaviour is baked into the file at encode time, not controlled by markup.
If you find yourself reaching for a <video> tag to play a .webp file, stop — it will not work. <video> expects a video container (MP4, WebM), and an animated WebP is none of those. Use <img>.
Always set width and height (or aspect-ratio)
Notice the width and height attributes above. They are not optional in any layout you care about. Without them the browser does not know how much space the image will occupy until the file starts downloading, so it reserves zero height, paints the rest of the page, then shoves everything down when the image arrives. That jump is Cumulative Layout Shift (CLS) — one of the three Core Web Vitals, and one of the easiest to fail by accident.
Set the intrinsic pixel dimensions on the attributes. Modern browsers turn width and height attributes into an implicit aspect-ratio, so the box reserves the right shape even when your CSS overrides the rendered size:
<img
src="loop.webp"
alt="Product walkthrough, looping"
width="640"
height="360"
style="width: 100%; height: auto;"
/>Here width: 100%; height: auto makes the image fluid, but the 640×360 attributes still pin the 16:9 ratio so the reserved box never collapses. If you cannot put the real pixels in attributes, set aspect-ratio: 16 / 9 in CSS instead. Either way: never let an animated image render without a reserved aspect ratio.
Lazy vs eager: loading and fetchpriority
Animated WebP files are heavier than static images, so loading strategy matters more than usual. The loading attribute controls when the browser fetches the file:
<!-- Below the fold: defer until it's near the viewport -->
<img src="demo.webp" alt="Feature demo" width="800" height="450" loading="lazy" />
<!-- Above the fold and visually important: load it immediately -->
<img
src="hero.webp"
alt="Hero animation"
width="1280"
height="720"
loading="eager"
fetchpriority="high"
/>Use loading="lazy" for anything below the fold. The browser holds off fetching until the user scrolls close, which keeps a heavy loop off the critical path. Do not lazy-load an image that is part of your Largest Contentful Paint (LCP) — lazy-loading the LCP element delays it and tanks your score. For an above-the-fold hero loop, use loading="eager" (the default) and add fetchpriority="high" to bump it ahead of other resources in the fetch queue.
Two more low-cost hints worth knowing: decoding="async" lets the browser decode the image off the main thread so a large frame does not block rendering, and is a safe default for non-critical images. For the LCP hero you can leave decoding to the default, or set decoding="sync" if you want it painted in the same frame. These hints are advisory — the browser is free to ignore them — so treat them as nudges, not guarantees.
A GIF fallback for legacy environments
Animated WebP is supported in roughly 97% of browsers (see the browser support guide), but a few environments still cannot render it: some WebView-embedded mail clients, ancient Safari (before 14.1), and assorted old in-app browsers. If those matter to your audience, the <picture> element lets you serve WebP to browsers that support it and a GIF to those that do not — with zero JavaScript:
<picture>
<source srcset="loop.webp" type="image/webp" />
<img src="loop.gif" alt="Product walkthrough, looping"
width="640" height="360" />
</picture>The browser walks the <source> list top to bottom and picks the first one whose type it can decode. A browser that understands image/webp loads loop.webp; anything else falls through to the <img> and gets the GIF. The <img> is mandatory inside <picture> — it carries the alt, the dimensions, and it is the element that actually renders. Put width/height on the <img>, not the sources.
The cost is that you have to ship and host the GIF too, and GIF is the format you were trying to escape in the first place. For most public web pages in 2026 the legacy slice is small enough that a plain <img> is fine — see WebP vs GIF for where each format still earns its place.
Respecting prefers-reduced-motion
Some users set their OS to reduce motion because animation triggers vestibular discomfort or distracts them. An autoplaying loop ignores that preference unless you handle it. The cleanest, JavaScript-free way is the same <picture> mechanism, switched on a media query:
<picture>
<!-- Reduced-motion users get a static still -->
<source media="(prefers-reduced-motion: reduce)" srcset="still.png" />
<!-- Everyone else gets the animation -->
<source srcset="loop.webp" type="image/webp" />
<img src="loop.gif" alt="Product walkthrough" width="640" height="360" />
</picture>Order matters. <picture> takes the first matching source, so the reduced-motion source must come before the animated one. A user who has reduced motion enabled matches that first source and gets still.png — a single frame, no animation. Everyone else falls through to the WebP, then the GIF.
Why can't I just pause it with CSS? Because CSS has no reach into image playback. The animation-play-state and prefers-reduced-motion controls only govern CSS animations and transitions— keyframes you wrote. The frames inside a WebP are decoded and advanced by the browser's image pipeline, which runs independently of the cascade. By the time CSS is applied, the image is already cycling its own frames. There is no property — none — that pauses an animated WebP or GIF. The only ways to stop it are to not load the animated file at all (the <picture> swap above) or to replace the src with a still frame in JavaScript.
The WebP vs GIF guide touches on this constraint; the takeaway for embedding is concrete: if your audience includes a general public, ship the reduced-motion still source. It is a few extra lines and one small PNG.
Using it as a CSS background-image
An animated WebP works fine as a CSS background and still autoplays and loops:
.banner {
background-image: url("loop.webp");
background-size: cover;
background-position: center;
aspect-ratio: 16 / 9;
}The tradeoffs are real, though. A background image has no alt text, so it is invisible to screen readers — only use it for decorative loops that carry no information. It has no intrinsic width/height, so you must reserve space yourself (the aspect-ratio above) to avoid layout shift. And it does not participate in loading="lazy" or fetchpriority — background images are fetched on a different schedule and are usually not the LCP element. When the loop conveys meaning, prefer <img>.
Common mistakes
- Wrapping it in <video>. A
.webpis an image, not a media container.<video src="loop.webp">renders nothing. Use<img>. - Missing dimensions. No
width/height(oraspect-ratio) means layout shift, which fails CLS. - Empty or missing alt. Every meaningful image needs an
altdescribing what it shows. For a purely decorative loop, usealt=""(explicitly empty) so screen readers skip it — but never omit the attribute. - Lazy-loading the LCP hero. Putting
loading="lazy"on your largest above-the-fold image delays the paint the metric measures. - Expecting CSS to pause it. No CSS property controls WebP/GIF frame playback. Swap the source instead.
- A one-frame WebP that does not loop. If your animation plays once and stops, the file was encoded with a finite loop count. Re-export with an infinite loop.
Quick copy-paste snippets
Grab whichever matches your situation.
<!-- 1. Standard, below the fold -->
<img src="loop.webp" alt="Describe the animation"
width="640" height="360" loading="lazy" decoding="async" />
<!-- 2. Above-the-fold hero (LCP) -->
<img src="hero.webp" alt="Describe the animation"
width="1280" height="720" fetchpriority="high" />
<!-- 3. Fluid / responsive width -->
<img src="loop.webp" alt="Describe the animation"
width="640" height="360"
style="width: 100%; height: auto;" loading="lazy" />
<!-- 4. With GIF fallback for legacy clients -->
<picture>
<source srcset="loop.webp" type="image/webp" />
<img src="loop.gif" alt="Describe the animation"
width="640" height="360" loading="lazy" />
</picture>
<!-- 5. Accessible: still image for reduced-motion users -->
<picture>
<source media="(prefers-reduced-motion: reduce)" srcset="still.png" />
<source srcset="loop.webp" type="image/webp" />
<img src="loop.gif" alt="Describe the animation"
width="640" height="360" loading="lazy" />
</picture>Replace the alt text with a real description, set the width/height to your file's actual pixel dimensions, and you are done.
Need an animated WebP to embed? Convert a video or GIF — it runs entirely in your browser, so your file never gets uploaded.