cd /news/ai-tools/a-transparent-video-went-opaque-with… · home › topics › ai-tools › article
[ARTICLE · art-159375] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=· neutral

A transparent video went opaque with no error: put an alpha check after every hop

A developer found that a VP9 WebM with alpha transparency silently decoded as fully opaque through FFmpeg 7.1's default VP9 decoder, producing a ProRes 4444 conversion (41,105,728 bytes) that played fine but had no transparency, versus 62,034,554 bytes with the correct libvpx-vp9 decoder. The engineer built three checks — an EBML walker counting BlockAdditions entries (the alpha_mode tag proved unreliable in two counterexamples), a decode probe pinning the decoder and reading alpha from frame 60 via signalstats (0/46.7618/255), and an output probe after each conversion with tolerance for lossy re-encodes — plus a deliberately opaque H.264 file that must fail the check.

by read4 min views2 publishedOct 28, 2026

A transparent video can go opaque in the middle of a pipeline without any step failing. I hit it on one 8-second, 540×960 WebM with VP9 alpha, exported from the video background removal in ImgIng (https://imging.ai/), a Beta feature that runs locally in desktop Chrome or Edge and not on phones. I converted it to ProRes 4444 for an editor. The conversion finished, the file played, and every pixel was opaque.

Everything below is one clip plus two small FFmpeg-made test files, one local run, FFmpeg 7.1. I did not test other builds or any editor's import.

The pipeline had three hops: export a WebM, decode it, write an intermediate file. Nothing reported a problem at any of them, so the only way to find the bad hop was to read alpha back after each one. The source file looks like this to ffmpeg -i:

$ ffmpeg -hide_banner -i input.webm
  Duration: 00:00:08.03, start: 0.000000, bitrate: 2386 kb/s
  Stream #0:0(eng): Video: vp9 (Profile 0), yuv420p(tv, smpte170m), 540x960, SAR 1:1 DAR 9:16, 30.30 fps, 30 tbr, 1k tbn (default)
      Metadata:
        alpha_mode      : 1
  Stream #0:1(eng): Audio: opus, 48000 Hz, stereo, fltp (default)

The stream line says yuv420p, the same thing a file with no alpha says, so it is no help. What worked was extracting the alpha plane of frame 60 and reading its min, average and max with signalstats. Through FFmpeg's default VP9 decoder those were 255, 255, 255: fully opaque. With -c:v libvpx-vp9 placed before -i they were 0, 46.7618 and 255, and about 79% of that frame's pixels are fully transparent. (Without format=rgba in the filter chain the default-decode run fails with Invalid argument, since the decoded frame has no alpha plane.) Nothing in the default path warns you.

Converting with the correct decoder gave a ProRes 4444 file of 62,034,554 bytes. Converting with the default decoder gave 41,105,728 bytes. The second is smaller and plays fine. The same pattern held for QuickTime Animation, 92,176,076 bytes with the right decoder and 85,557,735 with the default. A smaller file proves nothing here, and in the PNG written from the default decode the transparent area is black.

I ended up with three kinds of check, and each one answers a different question.

The first is a container probe at the source. The alpha_mode tag that ffmpeg -i prints is not reliable. I built two counterexamples with FFmpeg, which copies the input stream's tag onto the output. In the first, a re-encode as yuv420p kept the tag while carrying no alpha data. In the second I stripped the tag from a 2-second alpha clip with -c copy, and the alpha data was still there: 60 BlockAdditions entries and 248,054 bytes. The real file has 240 entries and 1,036,225 bytes. Counting BlockAdditions in the video blocks was the check that gave the right answer in all four files. I wrote a small EBML walker for it because this machine had no ffprobe; I did not try ffprobe's packet side data. Both counterexamples are my own FFmpeg outputs, not what ImgIng exports.

The second is a decode probe. It pins the decoder explicitly and reads alpha from a fixed frame, the way described above. Here 46.762 became my reference value, taken at the first hop and carried down the line.

The third is an output probe after every conversion. It reads alpha from the file the hop just wrote, using the decoder the next hop will use, and compares it with the reference. ProRes 4444, QuickTime Animation and a 240-file PNG sequence all read back as 0 minimum, 255 maximum, 46.762 average. A lossy VP9 re-encode at crf 30 read back 46.761, so the probe needs a tolerance, and an exact match would have rejected a good file. The default-decode ProRes and qtrle read back 255 / 255 / 255.0.

Add one hop that must fail: an H.264 MP4 at crf 18 from the same clip. It has no alpha by construction and read back 255 / 255 / 255.0. If a checkpoint ever calls that file transparent, the checkpoint is broken.

It reads one or two frames, 60 and 150, so a hop that fails only in a later part of a clip would pass. I would sample more frames on a long file. It also says nothing about how an editor imports the file. Editor import is untested, so my only claim is that ProRes 4444 and PNG sequences read back correctly in FFmpeg. The size cost is real: on this clip ProRes was about 26 times the source WebM, qtrle 38.5 times, and the PNG sequence 26.7 times.

How the matting model decides what is foreground is not something I can say from the outside.

If you have a transparent WebM and a conversion step coming, run the second command on the source first, write the number down, and run it again on whatever the next hop produces.

── more in #ai-tools 4 stories · sorted by recency
── more on @ffmpeg 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/a-transparent-video-…] indexed:0 read:4min 2026-10-28 · —