Animated WebP looks perfect in Chrome and Firefox — then you open it on an iPhone and everything breaks. iOS Safari handles animated WebP inconsistently at best: transparency renders wrong, looping stalls after the first play, and performance tanks on longer animations. The fix isn't a single line of code — it's a format conversion pipeline that accounts for every quirk Apple's browser throws at you.

We ran into this on a client project — a construction company site that needed a 130-frame isometric animation as a hero section element. The animation was delivered as an animated WebP with alpha transparency. It looked stunning on desktop Chrome. On an iPhone, it was a disaster. Here's what we learned getting it to work everywhere.

Why Animated WebP Is a Trap for Production Websites

Animated WebP has broad browser support on paper. Every modern browser technically renders it. But "renders" and "renders correctly with acceptable performance" are different things. Animated WebP uses VP8 keyframe compression under the hood — the same codec from 2008. Each frame is essentially a standalone lossy or lossless image, and the decoder has to process every one sequentially with no inter-frame prediction.

That matters when your animation has 50+ frames. A 130-frame animated WebP we were working with weighed in at over 12MB. The same content encoded as WebM with VP9 — a proper video codec with inter-frame compression — dropped to under 2MB. That's not a marginal win. Google's own WebP documentation positions animated WebP as a GIF replacement (64% smaller than equivalent GIFs), but it was never designed to compete with actual video codecs for longer animations.

The rule of thumb we landed on: if your animation is under 10 frames or purely decorative (a loading spinner, a subtle UI flourish), animated WebP is fine. Anything longer belongs in a video format.

When to Use WebM, MP4, Lottie, or Animated WebP

We spent time early in this project evaluating format options, and the decision tree is more nuanced than most blog posts suggest. Here's how we think about it now:

Animated WebP — best for short, simple loops under 10 frames. Works everywhere, no JavaScript required, behaves like an image element. Falls apart on longer animations due to file size and decode performance.

WebM (VP9) — best for longer animations where you control the background color. Massive compression advantage over animated WebP. Plays natively in a <video> tag. But iOS Safari does not support VP9 alpha transparency, which is the landmine we'll cover next.

MP4 (H.264) — the universal fallback. Every browser, every device. No alpha transparency support at all, but if your animation sits on a solid background, this is the safest bet. Slightly larger files than VP9 but zero compatibility headaches.

Lottie (JSON-based vector animation) — ideal for UI animations, icons, and illustrations that were designed in After Effects. Tiny file sizes, infinite scalability, full transparency support. But it only works for vector content — it can't handle raster imagery, photographs, or complex rendered scenes.

We chose WebM VP9 for this project because the animation was raster-based (ruling out Lottie), too long for animated WebP (130 frames), and we could control the background color (making the alpha transparency limitation manageable).

Why iOS Safari Breaks WebM Alpha Transparency

This is the one that cost us hours. We converted the animated WebP to WebM with VP9 encoding, preserved the alpha channel, and tested on desktop — flawless. Transparent background rendered exactly as expected in Chrome, Firefox, and desktop Safari.

Then we opened it on an iPhone. The transparent regions rendered as solid black. The animation itself played fine — correct timing, correct frames — but every pixel that should have been transparent was opaque black instead.

The reason: iOS Safari supports WebM video playback but does not support alpha transparency in the VP9 codec. Apple added WebM support in Safari 15, but the alpha channel decoding path was never implemented on iOS. Desktop Safari on macOS Big Sur and later handles it correctly. iOS does not. There's no flag to enable it, no workaround within the format itself.

We considered serving HEVC with alpha (which iOS does support natively via the <video> tag), but the encoding toolchain is more complex and browser support outside Apple's ecosystem is poor. That meant maintaining two separate video files and a detection/fallback system — added complexity for a single section of one page.

How We Fixed It: Baking the Background Into Every Frame

The solution we landed on was straightforward once we stopped fighting the transparency limitation: remove the alpha channel entirely and composite each frame onto the target background color before encoding.

The concept is simple. Take every frame of the source animation, layer it onto a solid-color canvas that matches the section background of the website, and export the result as a new animation with no transparency. The final WebM has no alpha channel to decode, so iOS Safari renders it identically to every other browser.

The tricky part was the frame-level processing. Our source animated WebP had 130 frames, each with slightly different content. We built a pipeline that extracts every frame, composites it onto the background color, and then feeds the processed frames into the video encoder. The pipeline also handled a secondary problem: the source animation had a baked-in 6-pixel border artifact on all four edges that needed to be cropped from every frame before compositing.

Processing 130 frames through extraction, cropping, compositing, and re-encoding took some careful orchestration — but the output was a single WebM file under 2MB that played identically on every browser and device we tested. No feature detection, no fallback files, no conditional logic.

The iOS Safari Video Loop Bug That Nobody Warns You About

With the transparency issue solved, we deployed and tested on iOS again. The animation played perfectly — once. Then it stopped. The loop attribute was set on the <video> element, and it worked on every other browser. iOS Safari simply ignored it after the first playback completed.

This is a known but inconsistently documented iOS Safari behavior that affects short-form video loops. The loop attribute is supposed to restart playback automatically when the video reaches its end. In practice, iOS Safari sometimes fails to trigger the restart — especially on shorter videos, on pages with multiple media elements, or when the video enters and leaves the viewport.

The fix is a JavaScript fallback using the ended event. Instead of relying solely on the native loop attribute, you attach an event listener that manually resets the video to the beginning and calls play when playback ends:

// Conceptual pattern — not the full production implementation
video.addEventListener('ended', () => {
  video.currentTime = 0;
  video.play();
});

We kept the loop attribute on the element as well, so browsers that handle it correctly use the native path. The JavaScript fallback only fires if the ended event triggers — which shouldn't happen on a properly looping video. It's a belt-and-suspenders approach that covers the iOS edge case without adding overhead on browsers that work correctly.

Why Frame-Level Processing Matters More Than Format Choice

The biggest insight from this project wasn't about which format to choose — it was about the pipeline between formats. Converting animated WebP to WebM isn't a single ffmpeg command. The source animation had artifacts (that 6-pixel border), transparency that needed to be removed and replaced, and 130 frames that each needed individual processing before encoding.

We built the extraction and compositing layer in Python using Pillow for frame manipulation, then handed the processed frames to ffmpeg for VP9 encoding. The separation matters: image processing libraries are better at per-frame pixel manipulation (cropping, compositing, color correction), while video encoders are better at inter-frame compression and codec-level optimization. Trying to do both in a single tool leads to worse results in both areas.

This two-stage pattern — frame-level processing followed by video encoding — is something we now reach for on any project that involves converting between animated image formats and video formats. The format choice matters, but the processing pipeline between source and output is where the real quality and compatibility wins happen.

The Cross-Browser Animated Media Checklist

After shipping this, we distilled our testing process into a short checklist we run on every project that involves animated content on the web. Animated WebP, WebM, auto-playing video sections — all of it goes through these checks before deployment:

Test on real iOS devices, not simulators. The alpha transparency bug and the loop bug both behave differently in Xcode's simulator than on actual hardware. We caught neither in simulated testing.

Measure file size against the animated WebP source. If your converted video isn't at least 60% smaller than the animated WebP, something is wrong with your encoding settings. VP9 inter-frame compression should deliver dramatic savings on any animation longer than a few frames. This same principle applies to cutting image bandwidth with server-side processing — format conversion at the pipeline level consistently outperforms client-side optimization.

Always include the JavaScript loop fallback. Even if the loop attribute works today, iOS Safari's behavior here has been inconsistent across versions. The ended event listener costs nothing and prevents a class of bugs that only surface on real devices in production.

Decide on transparency early. If you can composite onto a known background color, do it. You eliminate an entire category of cross-browser bugs and reduce file size. Only preserve alpha if the animation genuinely needs to float over dynamic or user-controlled backgrounds.

Animated content on the web looks simple until you need it to work everywhere. The gap between "works in Chrome" and "works on every device your users actually have" is where the real engineering happens — and it's almost always a pipeline problem, not a format problem.