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 atyuva420p, ProRes 4444 atyuva444p10le, or QuickTime RLE atargb. 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 withffprobe, forceformat=yuva420p(orrgba) 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.
| Codec | Container | Alpha pixel format | Encoder 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` | none | not 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 manage | Your own FFmpeg | FFmpeg Micro |
|---|---|---|
| Encoder build with libvpx and prores_ks | You compile or pin it | Managed |
| Alpha-safe filter chain | You write and debug it | One composition request |
| Long renders and timeouts | Your worker's problem | Submit a job, poll or get a webhook |
| Where it runs | Your server or container | REST 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 0when 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
zoompanon an alpha stream. It outputsyuv420p, so a Ken Burns move flattens your logo. Convert back withformat=yuva420pright after. - Padding or rotating without transparent defaults.
padneedscolor=black@0androtateneedsfillcolor=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.
ffprobeis the only honest check. - Mismatched frame rates.
overlaysyncs 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.
You might also like

Choosing a RenderIO alternative for n8n? Look past the node
Looking for a RenderIO alternative for n8n? Compare per-command quotas, clip duration caps, and webhook gating against usage-based FFmpeg API pricing.

YouTube Shorts Video Settings That Survive the Re-Encode
The YouTube Shorts video settings that survive re-encoding: 1080x1920, H.264 High, 8-15 Mbps, 48 kHz AAC, and one FFmpeg command for any source aspect ratio.

Ezgif Alternative: Batch GIF Conversion Past the 100 MB Cap
Ezgif alternative for batch jobs: the real upload caps, the FFmpeg palettegen command behind the site, and how to convert a whole folder of videos to GIF.
Ready to process videos at scale?
Start using FFmpeg Micro's simple API today. No infrastructure required.
Get Started Free