A cut-out presenter floating over a product page, person only, no rectangle, the page showing through, is the kind of request that reaches a frontend team sooner or later. The obvious deliverable is a transparent WebM, and the obvious way to decide whether the browser can handle it is canPlayType. I ran both through three engines before I'd estimate it, and the feature check turned out to be the part that tells you nothing.
The file
I made the test file with a browser-side video matting tool, because the source footage wasn't supposed to leave my machine, and after a compliance cleanup I once sat through, that's the first filter I apply. Input was "Tarun speaking 01" from Wikimedia Commons (CC0), re-encoded to H.264 at 464×832. With the background set to Transparent, the tool greys out MP4 and switches the format to WebM on its own, with a note that transparent backgrounds need WebM VP9 alpha.
ffmpeg reports VP9 Profile 0, alpha_mode: 1 on the stream, Opus 48 kHz audio, same resolution and all 511 frames. One trap before you even get to a browser: the stream line says yuv420p. ffmpeg's built-in VP9 decoder ignores the alpha. Force -c:v libvpx-vp9 before the input and you get yuva420p. If your pipeline sanity-checks transparency by reading pix_fmt from a default probe, it will say the alpha isn't there when it is. The file came out at 5.47 MB, where the export panel had estimated about 2.86 MB (both in the 1024-based MB the page uses), so don't size a CDN budget from that number.
Three engines, one probe
I used Playwright's bundled browsers: Chromium 149, Firefox 151 and WebKit 26.5. That WebKit is Playwright's build, not Safari, and I haven't tested real Safari on macOS or iOS. The probe seeks to 1 s, draws the frame into a canvas and reads the alpha of two 40×40 corners that are ceiling in the source, so nothing there should be opaque:
async function cornerAlpha(v, t = 1.0) {
v.currentTime = t;
await new Promise(r => v.addEventListener('seeked', r, { once: true }));
const c = document.createElement('canvas');
c.width = v.videoWidth; c.height = v.videoHeight;
const ctx = c.getContext('2d', { willReadFrequently: true });
ctx.drawImage(v, 0, 0);
const maxA = d => Math.max(...d.filter((_, i) => i % 4 === 3));
return [ctx.getImageData(0, 0, 40, 40).data, ctx.getImageData(c.width - 40, 0, 40, 40).data].map(maxA);
}
Chromium and Firefox returned a maximum alpha of 1 and 0 for the two corners, so the transparency survived decoding. WebKit returned 255 and 255, and the pixels are black. It plays the file without complaint and throws the alpha away. All three answered "probably" to canPlayType('video/webm; codecs="vp9"'). There's no error event and no warning, just a black box where the page should be.

Clip: Wikimedia Commons "Tarun speaking 01" (CC0), tested 2026-09-14.
The black isn't WebKit inventing a colour. The YUV stored under transparent pixels in this file is black, so anything that drops the alpha channel shows black. Which leads to the second way this breaks.
Converting it breaks it the same way
The usual reflex when a format doesn't play somewhere is to convert to MP4. ffmpeg -i transparent.webm -c:v libx264 -pix_fmt yuv420p -c:a aac converted.mp4 gives a clean H.264 file with black corners, and my probe reads 255 in every engine, because yuv420p has nowhere to put alpha. A default ffmpeg -i transparent.webm out.gif is also black: 511 frames, no transparent palette entry. A GIF can keep transparency if you ask for it explicitly and decode with libvpx:
ffmpeg -c:v libvpx-vp9 -i transparent.webm \
-vf "fps=15,split[a][b];[a]palettegen=reserve_transparent=1[p];[b][p]paletteuse=alpha_threshold=128" keep.gif
The corner pixel of that file is fully transparent. It's 1-bit transparency though, so every semi-transparent pixel at the hair and shoulders gets forced to either solid or gone. Those soft edge pixels are exactly the part of an overlay people's eyes go to.
What I'd ship
Since detection can't see the problem, the decision has to rest on which engines you serve. The WebM overlay is fine for Chromium and Firefox. For anything WebKit-based I'd ship a separate MP4 with the background already composited in the page colour. The same tool exports that directly as a solid-colour H.264/AAC file, so it's a second export and not a hand-built fallback. What I still don't have is a result from real Safari, and until I do, WebKit gets the MP4. The acceptance list I'd attach to that ticket:
- Decode one frame with
-c:v libvpx-vp9and confirmyuva420p; don't trust the default pix_fmt line. - In each target engine, draw a frame at a known background spot into a canvas and read alpha.
canPlayTypedoesn't count as a check. - Include a WebKit run, and write down that it isn't Safari.
- Any conversion step (MP4, GIF, a transcoding upload) gets the same corner check afterwards.
- Look at hair and shoulder edges on the real page background, not on a checkerboard.
The tool I used for the export is at https://imging.ai/.
Top comments (0)