ffmpegvideo-compositionalpha-channel

Your FFmpeg overlay isn't broken. Your transparent video is.

·Javid Jamae·10 min read
Your FFmpeg overlay isn't broken. Your transparent video is.

Your animated logo has a transparent background in the design tool, in the preview, and in the file browser thumbnail. Then FFmpeg composites it over the base video and the logo lands inside a solid black rectangle. The alpha channel was gone before the overlay filter ran.

Quick answer: An FFmpeg overlay transparent video command keeps transparency only when the overlay source carries an alpha channel: VP8 or VP9 in a WebM at yuva420p, ProRes 4444 at yuva444p10le, or QuickTime RLE at argb. H.264 has no alpha channel, so an animated logo exported as an MP4 composites as a black box no matter what filter you write. Check the source with ffprobe, force format=yuva420p (or rgba) into the filter chain so nothing flattens it, and if you would rather not maintain encoder builds and pixel-format rules, send the base clip and the overlay to the FFmpeg Micro composition API as one call.

The black box is an export problem, not an overlay problem

Conventional wisdom says the fix lives in the overlay filter, and most threads hand you a longer filter chain. That's partly fair, because filters do drop alpha. But the black rectangle almost always starts one step earlier: somebody exported the animation to H.264, which has no way to store per-pixel transparency. The encoder wrote opaque black where the alpha used to be, and the file still looks right on a dark background.

Once that MP4 exists, the transparency is gone for good. No filter or pixel-format conversion brings it back. You need a new export in a codec that carries alpha.

Four codecs carry alpha, each a different trade between file size and compatibility.

CodecContainerAlpha pixel formatEncoder flags
VP9`.webm``yuva420p``-c:v libvpx-vp9 -pix_fmt yuva420p -auto-alt-ref 0`
VP8`.webm``yuva420p``-c:v libvpx -pix_fmt yuva420p -auto-alt-ref 0`
ProRes 4444`.mov``yuva444p10le``-c:v prores_ks -profile:v 4444 -pix_fmt yuva444p10le`
QuickTime RLE`.mov``argb``-c:v qtrle -pix_fmt argb`
H.264`.mp4`nonenot possible

The size difference is not subtle. ProRes 4444 runs around 330 Mbps at 1080p30, so a five-second subscribe sting lands near 200 MB. The same sting as VP9 with alpha at CRF 30 is a couple of megabytes. Use WebM for flat motion graphics a pipeline downloads on every job, and ProRes 4444 for handing work between editing tools where generation loss matters.

ffprobe tells you whether the alpha survived the export

One command settles it before you write a filter chain. Ask ffprobe for the codec, the pixel format, and the WebM alpha tag:

ffprobe -v error -select_streams v:0 \
  -show_entries stream=codec_name,pix_fmt:stream_tags=alpha_mode \
  -of default=noprint_wrappers=1 sting.webm

A file that composites correctly reports:

codec_name=vp9
pix_fmt=yuva420p
TAG:alpha_mode=1

Any pixel format without the a (yuv420p, yuv444p, rgb24) means opaque pixels. Two results deserve a second look. Apple's HEVC-with-alpha .mov exports from Motion and Keynote often probe as an ordinary opaque stream, because many FFmpeg builds ignore the alpha layer; re-export those as ProRes 4444 rather than fight the decoder. A WebM showing alpha_mode=1 but probing as yuv420p usually needs the libvpx decoder forced on input with -c:v libvpx-vp9 before -i. If that returns an unknown decoder error, your build lacks libvpx, the same problem as a build missing libx264.

The overlay command that keeps the alpha channel

A working composite forces the pixel format on the overlay branch, then lets overlay choose the blend format instead of accepting its default. This puts an animated logo in the bottom-right corner from the one-second mark to the six-second mark:

ffmpeg -i base.mp4 -c:v libvpx-vp9 -i sting.webm \
  -filter_complex "[1:v]format=yuva420p,scale=320:-1[fg];\
[0:v][fg]overlay=W-w-40:H-h-40:format=auto:eof_action=pass:enable='between(t,1,6)'" \
  -c:v libx264 -pix_fmt yuv420p -c:a copy out.mp4

Three details do real work. format=yuva420p on the overlay branch stops FFmpeg's format negotiation from picking an opaque format when linking the filters. format=auto on overlay matters because the filter's default is yuv420, which subsamples chroma at the blend and gives colored edges a soft fringe. And eof_action=pass handles an overlay shorter than the base, the usual case with a five-second sting on a ten-minute video. The default, repeat, freezes the sting's last frame for the rest of the render.

To loop a short animation across the whole base clip, put -stream_loop -1 before the overlay's -i and add shortest=1 so the output ends with the base.

Assets that ship the alpha as a separate matte need alphamerge

Plenty of stock animation packs ship no real alpha channel. You get a fill file and a matte file, or one wide file with the fill on the left and the black-and-white matte on the right. The alphamerge filter rebuilds a single stream with alpha by reading the matte's luma as the alpha value.

With separate files:

ffmpeg -i base.mp4 -i fill.mp4 -i matte.mp4 -filter_complex \
"[1:v]format=yuva420p[fill];[2:v]format=gray[mask];\
[fill][mask]alphamerge[fg];[0:v][fg]overlay=0:0:format=auto:shortest=1" \
  -c:v libx264 -pix_fmt yuv420p out.mp4

With one 3840x1080 side-by-side file, split the input first, because a filter chain cannot consume the same label twice:

ffmpeg -i base.mp4 -i fill_and_matte.mov -filter_complex \
"[1:v]split[l][r];[l]crop=1920:1080:0:0,format=yuva420p[fill];\
[r]crop=1920:1080:1920:0,format=gray[mask];\
[fill][mask]alphamerge[fg];[0:v][fg]overlay=0:0:format=auto" \
  -c:v libx264 -pix_fmt yuv420p out.mp4

If the composite has a dark halo around every edge, the matte is premultiplied rather than straight. Add alpha=premultiplied to the overlay options and the fringe goes away.

PNG sequences composite the same way, with one frame-rate trap

Numbered RGBA PNGs are still the most reliable transparent-animation handoff, and overlay treats the folder like a video input. The trap is that -framerate is a demuxer option, so it has to come before -i. Put it after and FFmpeg ignores it, plays the sequence at 25 fps, and your six-second animation runs five seconds long over a 30 fps base.

ffmpeg -i base.mp4 -framerate 30 -start_number 0 -i frames/frame_%04d.png \
  -filter_complex "[1:v]format=rgba[fg];\
[0:v][fg]overlay=(W-w)/2:(H-h)/2:format=auto:eof_action=pass" \
  -c:v libx264 -pix_fmt yuv420p -c:a copy out.mp4

Flattening to H.264 at the end is fine, and expected

Alpha only has to survive until the overlay filter runs. After the blend the output is a normal opaque frame, so encoding the final file as H.264 with -pix_fmt yuv420p is correct rather than a compromise. That flag isn't optional: libx264 will happily write yuv444p and produce a file that some browsers and players refuse to show.

You need an alpha-capable output only when the composite is an intermediate that a later step will overlay again. Then encode -c:v prores_ks -profile:v 4444 -pix_fmt yuva444p10le for an editing handoff, or -c:v libvpx-vp9 -pix_fmt yuva420p -auto-alt-ref 0 for something a web pipeline will fetch.

Inside an automation the cost shows up as infrastructure: an FFmpeg build with libvpx compiled in, enough disk for a 200 MB ProRes intermediate, and a worker that can sit on a long render without a platform timeout killing it. That's the same wall the assembly step hits in AI avatar video pipelines. The alternative is to keep the FFmpeg knowledge and drop the infrastructure:

What you manageYour own FFmpegFFmpeg Micro
Encoder build with libvpx and prores_ksYou compile or pin itManaged
Alpha-safe filter chainYou write and debug itOne composition request
Long renders and timeoutsYour worker's problemSubmit a job, poll or get a webhook
Where it runsYour server or containerREST API, n8n, Make, Zapier, or an AI agent via MCP

The composition API takes the base video URL and the overlay URL with position and timing, returns a job, and hands back the finished file. No servers to run, no encoder to install, and the free tier covers enough jobs to test whether your alpha asset works.

Pitfalls that produce a black box or a missing overlay

Most broken alpha composites trace back to a short list of causes, all of them silent.

  • Rendering an "intermediate" to H.264 halfway through the pipeline. That destroys the alpha even though the first and last commands are correct.
  • Leaving out -auto-alt-ref 0 when encoding VP8 or VP9 with alpha. libvpx's alternate reference frames and the alpha plane don't coexist, so the encode errors or drops transparency.
  • Running zoompan on an alpha stream. It outputs yuv420p, so a Ken Burns move flattens your logo. Convert back with format=yuva420p right after.
  • Padding or rotating without transparent defaults. pad needs color=black@0 and rotate needs fillcolor=none, otherwise the new pixels are opaque black.
  • Trusting a player's preview. VLC and QuickTime render alpha over black, so a working file and a broken file look identical. ffprobe is the only honest check.
  • Mismatched frame rates. overlay syncs on the main input's timeline, so an animation authored at 24 fps over a 30 fps base drifts. Normalize first, the same way you would before a concat.

When this approach is the wrong fit

Transparent-overlay compositing in FFmpeg assumes the animation already exists as a file. If every render needs different text, timing, or layout inside the animation, that's a motion-graphics template rather than an overlay, and a template-editor service will cost you less pain than scripting drawtext against a moving background.

The other boundary is browser playback. If you want a transparent video sitting over a web page rather than burned into a file, there's no composite step at all: Safari wants HEVC with alpha and Chrome wants VP9 with alpha, so you ship both and let the browser choose.

FAQ

Why does my transparent video have a black background in FFmpeg?

A transparent video renders with a black background in FFmpeg because the overlay file no longer has an alpha channel, usually from an H.264 or MP4 export somewhere in the chain. H.264 stores no transparency, so the encoder writes opaque black in place of transparent pixels, and the loss is permanent. Re-export the animation as VP9 WebM, ProRes 4444, or QuickTime RLE.

Can an MP4 or H.264 file store an alpha channel?

An H.264 stream cannot store an alpha channel, and neither libx264 nor any hardware H.264 encoder will produce one. The MP4 container can hold alpha-capable codecs in theory, but nothing in the common playback path supports that, so .mov with ProRes 4444 or .webm with VP9 are the practical choices.

How do I check if a video has an alpha channel?

Run ffprobe -v error -select_streams v:0 -show_entries stream=pix_fmt -of default=nw=1 yourfile.mov and read the pixel format. Formats containing an a, such as yuva420p, yuva444p10le, rgba, or argb, carry transparency; yuv420p and rgb24 do not. Media players aren't a valid test; they composite alpha over black.

Does the FFmpeg overlay filter preserve transparency automatically?

The overlay filter reads and blends the alpha of its second input automatically, but the filters ahead of it often do not. FFmpeg negotiates pixel formats between filters and picks an opaque one when a filter like zoompan or pad doesn't advertise alpha support, which silently flattens the overlay. Insert format=yuva420p or format=rgba right before the overlay.

How do I overlay a PNG sequence with transparency onto a video?

Feed the numbered PNGs as a second input with -framerate 30 -i frames/frame_%04d.png, then chain [1:v]format=rgba[fg];[0:v][fg]overlay=...:format=auto. PNG already carries RGBA, so the source needs no conversion. Putting -framerate after the input is the usual bug: FFmpeg defaults to 25 fps and the animation plays at the wrong speed.

If your pipeline needs this composite on every upload rather than once on your laptop, hand the base clip and the alpha overlay to the API and skip the encoder build. Sign up free and run the first composite against your own sting to see whether its alpha survived the export.

About Javid Jamae

Founder & CEO at FFmpeg Micro

Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.

Software EngineeringVideo ProcessingFFmpegCloud ArchitectureAPI DesignAutomation

Ready to process videos at scale?

Start using FFmpeg Micro's simple API today. No infrastructure required.

Get Started Free