Traditional video effects such as filters, cross-fades and blends are usually applied in an editing application, such as Adobe Premiere or Apple’s Final Cut. While there are obvious advantages to such a process, any changes made in an editor are “baked in” to each video frame, and can’t be manipulated or undone once the video is exported.
Yesterday, teaching a class in HTML5 multimedia, I realized that by using CSS filters and the newer blend modes it would be possible to duplicate many of the simpler video effects directly in CSS: the <video>
element is like any other, and is equally affected by filter
and mix-blend-mode
on supporting browsers. This shifts the rendering task onto the GPU of the machine, but the result can still be surprisingly effective.
Simple Color Blends
Before I explain the complete example, let’s take the simpler case of a color overlay, frequently used as a background for text on video. In this case, I’ll use a gradient behind the video itself:
<figure>
<video>
<source src="lina.webm">
<source src="lina.mp4">
</video>
</figure>
The CSS:
figure {
background: linear-gradient(90deg,#00f 50%,transparent 50.1%);
}
figure video {
mix-blend-mode: overlay;
}
Because the gradient is behind the video, it won’t be seen by browsers that don’t support mix-blend-mode
: they will only see the video itself.
Video To Black & White
It’s also possible to affect the <video>
element with CSS filters, just like images:
video { filter: grayscale(1); }
This can be extremely useful to make quick changes to video without having to convert, edit and re-export the original.
Animated Overlays
Finally, it’s also possible to create more sophisticated effects, as shown in the example at the top of this article. With this HTML:
<figure id="fashion">
<video controls>
<source src="fashion-export.webm">
<source src="fashion-export.mp4">
</video>
<div id="vid-cover"></div>
<figcaption>Summer Sale <span>Now On</span></figcaption>
</figure>
<button id="play-button">Play</button>
And this CSS:
@keyframes overlay {
30% {
left: 0;
width: 50%;
}
50% {
background: #00f;
}
80% {
left: 80%;
width: 20%;
}
100% {
left: 60%;
width: 40%;
background: #00f;
}
}
figure#fashion {
display: inline-block;
position: relative;
font-size: 0;
margin: 0;
}
figure#fashion video {
width: 100%;
}
figure#fashion div {
width: 10%;
height: 100%;
background: #f00;
position: absolute;
top: 0;
mix-blend-mode: multiply;
left: 0;
}
video.playing ~ div#vid-cover {
animation: overlay 10s forwards;
}
figure#fashion video.playing ~ figcaption {
opacity: 1;
}
figure#fashion figcaption {
font-size: 50px;
font-family: Avenir, sans-serif;
color: white;
position: absolute;
width: 40%;
right: 0;
top: 30%;
text-transform: uppercase;
text-align: center;
opacity: 0;
transition: 3s 9s opacity;
}
figure#fashion figcaption span {
font-size: 40px;
text-transform: lowercase;
display: block;
}
And a bit of JavaScript applied to the video and associated button:
var playbutton = document.getElementById("play-button");
var fashion = document.querySelector("#fashion video");
fashion.removeAttribute("controls");
playbutton.addEventListener("click", function() {
fashion.play();
fashion.classList.add("playing");
})
The resulting combination plays the result on top of the video, as seen at the top of this article. I’ve deliberately not reset the animations at the end so that you see the result when you play the video again. One potential downside of this approach is that browsers that support CSS animations but not yet mix-blend-mode
will have a solid rectangle moving over the video, rather than one that interacts with the pixels of the movie underneath; you could avoid that by placing the moving element underneath the video, as in the first example.
Video by Maximilian Kempe, licensed under Creative Commons.
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/wavpGe