Video with transparency in Chrome, Edge, Firefox, Safari, iOS and Android, circa 2022 (updated in late 2024)
The narration of this post was created with Bespoken plugin for Craft CMS.
This post on using video with a transparent background will probably not age well. At least, I hope that is the case! Ideally, in the future, you can use a single .webm
video file with an alpha channel and it will work everywhere.
But, as I write this post in January 2022, and have updated in 2024, after much research and trying different approaches, I have a <video>
tag that will play a video with a transparent background across a wide variety of browsers.
- Chrome
- Firefox
- Safari on Desktop
- Safari on iPhone
- Android
I assume that the other browsers based on Chromium will also work, although I've not tested them.
If you're trying to get a video with transparency working, let me show you what I've done. I hope this saves you some time.
Start with good source material
I'm lucky in that I'm starting with a nice, short, professionally produced video with an alpha channel. If your source video doesn't have an alpha channel, you need to solve that problem first.
My source video was uncompressed and enormous. I used Adobe After Effects to trim and resize it to meet my needs. The video had no audio channel on the video. This was perfect for my needs. Not having audio allows me to autoplay the video using Javascript.
I also resized the video dimensions. Tip: web video does not have to be the traditional video aspect ratio.
As shown in the following screenshot, I exported that video using Apple ProRes 4444 XQ. This will result in a file that is not usable on the web, but it will be ready for the next step in the process.
My hardware
Before going further, you should know I use an Apple Silicon Mac, a MacBook Pro. If you're on any other platform, your results may vary. I use the hardware acceleration option set to "OSX VideoToolbox" in one of the video conversions we'll discuss below. The option doesn't exist on a PC. This doesn't mean you need a Mac, but I don't have the hardware to test on other platforms. (Since writing this post, I've spoken with a few PC users who were unsuccessful in their attempts in generating the H265 video. I've helped a few of these people create their video conversions.)
Shutter Encoder
After many tests, I chose Shutter Encoder as the best encoder for this process. I tried FFmpeg, Handbrake, Adobe Media Encoder, and several other online video encoding services. None of them worked as well as Shutter Encoder, which is free. If you find the app useful, you should support the author. I did because I like the software I use to stick around.
Update, 10MAY2022: When I originally wrote this post, I used Shutter Encoder version 15.7 for the encoding process. I worked on encoding another video, as described here, using Shutter Encoder version 15.9, which failed. Based on what I've read, there is some issue with FFMPEG in versions after 15.7. Reinstalling version 15.7 allowed me to continue to convert videos successfully. I hope the bug is fixed in future versions of the software.
Update, 25MAY2023: I recently created a video with an alpha channel, as described in this post. I'm happy to report that Shutter Encoder version 17.1, the current version as of May 2023, successfully encodes the alpha channel. Based on the changelog, it looks like this was fixed in Shutter Encoder version 16.8.
Update, 29OCT2024: I don't regularly make H265 videos, so I only revisit this topic occasionally. As of today, in October 2024, the most current version of Shutter Encoder is 18.5. Unfortunately, that version no longer correctly produces the H265 video with the alpha channel. I do not know at what version number it broke. Fortunately, you can download older versions of Shutter Encoder. By using version 17.1, Shutter Encoder will still work to create videos with an alpha channel that works in Safari.
Creating a webm version of the video
I've made two versions of the video. The first is a .webm
version. Shutter Encoder allows you to preserve the alpha channel when you create your webm. Let's review the options I used.
Add your source video in the Choose files area, then select VP9 as your function. Under the Advanced features area, choose Enable alpha channel.
Now click the Start function button and generate your .webm
video.
Creating an H.265 version of the video, i.e., not an H.264 video
Now that Safari added support for .webm
, you might be wondering why we're making another version of the video. Unfortunately, in my tests, Safari's .webm
video support doesn't include support for the alpha channel. A black background is shown where you'd expect the transparency to be used.
Why not use an .mp4
with an alpha channel? We will do that, but it's not the traditional H.264 video you may be familiar with.
After some online research, you may incorrectly discover the .mp4
video file does not support an alpha channel. This was my assumption, too, but I was conflating an mp4 file and an H.264 video file as the same thing. There is a difference, though. H.264 is the codec, the piece of software that converts the uncompressed video into a stream of compressed bits. The file type .mp4
(or .webm
, or .avi
, etc.), contains the stream of video and/or audio.
In other words, the .mp4
file can use the H.264 codec. The H.264 codec doesn't support having an alpha channel. But there is a newer codec called H.265. Spoiler alert: it supports the alpha channel.
Using Shutter Encoder again, make the H.265 version of the video. Let's review the options I used.
There are three options in the screenshot above.
After you've added your source video, select H.265 as your function. Under the Advanced features area, the Enable alpha channel option should be checked automatically. Also, turn on the Hardware acceleration option. In my tests, the hardware acceleration was the only way to get Shutter Encoder to create the alpha channel successfully. Depending on your computer, you may achieve different results when using hardware acceleration.
Now click the Start function button and generate your .mp4
video.
The video tag for your HTML
Let's look at <video>
tag I'm using.
The code is not read in this audio file. See the original article for the code block.
<video muted playsinline id='ambientVideo'>
<source src="~@/assets/my_video_H.265.mp4" type='video/mp4;codecs=hvc1'>
<source src="~@/assets/my_video_VP9.webm" type="video/webm">
</video>
This video tag is for a small animation that plays inline, and it has no audio. You can see those parameters at the beginning of the video tag. I also have an ID of ambientVideo
. I will use this to trigger the animation with an intersection observer, which I'll describe at the end of this post. (Read more about the options for the video tag in the MDN Web Docs.)
The two most important things are the order of the source tags and the type of each source.
The .mp4
source tag is the first tag. Browsers will see this as the first source, and if they can play it, that's the one they'll choose to use. In my tests, Safari on desktop and on mobile will use this. Be sure you've got the type set to include the codecs
portion. Without that, you'll find web browsers which think they know how to play a .mp4
try to play the video but are unsuccessful because they are not aware of the hvc1 codec.
Next, include the link to the .webm
source. This is what most browsers will end up displaying.
In case you're wondering, the ~@/assets
path is how I refer to the root of my public folder. You need to use the path to the video that makes sense in your environment.
Bonus: Play video when it appears on the screen
In my video tag, I've got an ID of ambientVideo
.
I've posted the Javascript file I'm using in this gist: ambient-video-play-on-enter.js
Since you are here already, I'll share the code in this post, too.
The code is not read in this audio file. See the original article for the code block.
var ambientVideo = document.getElementById("ambientVideo");
var io = new IntersectionObserver(
entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
ambientVideo.play();
} else {
ambientVideo.pause();
}
});
},
{
root: null,
rootMargin: "0px",
threshold: 0.5
}
);
// after confirming the element exists, look for the #ambientVideo when visible in viewport
if (ambientVideo) {
io.observe(ambientVideo)
}
In the first line, I look for my video tag with the ID of ambientVideo
. Next, I create a new IntersectionObserver. This code could be made to be more reusable, but it works for this purpose.
What does this code do?
Basically, we tell the video element to play when it enters the frame. When it leaves the frame, it is paused.
Check out the Intersection Observer options if you want to customize when the Intersection Observer triggers. For example, my threshold setting of 0.5 means I want to detect the visibility of my video when 50% of it is visible. You may need different settings.
To make it work, I have the Intersection Observer that I create start observing, looking for the ambientVideo
video element.
Good luck!