Embed html videos with correct background color
The problem
How to integrate a video with a solid background color seamlessly into a website with the same background color.
We came to realize that on most screens there was a color deviation and we couldn’t globally match both colors. After trying several encodings and compressions of our .mp4 video, we could still see a slight difference in background color compared to the CSS color.
The usual solution would be to use a Codec which supports alpha channel (webm) or use an additional alpha mask to make the mp4 video transparent. But in this case we needed full browser compatibility, low processing power and couldn’t ask our client to generate alpha mask videos for each video he would upload on his CMS.
The solution
Some research showed that the problem comes from each users GPU. Those can be set to support the full RGB range (0 – 255) or a limited range (16-235). This limited range is causing this shift in color interpretation and makes it impossible to match a videos color to a CSS value globally.
Instead of playing the video directly in the video tag, we render the video on a canvas (there the color shift gets applied), pick a sample color of the first frame and adjust the background of the website accordingly. Now the video fits seamlessly into the website. Heres a demo. The heaviness of the derivation depends on your screen / gpu & I added a transition time to the background so you see the effect. The example further has an autoplay eventlistener (safari mobile doesn't support autoplay anymore) and retina support, so you can basically use it for your purpose.
To see a really basic inline example without the extra stuff go here. To download both examples click here.
Careful: As this approach changes the background color of your website slightly, we recommend to get the videos color as close as possible to the original css value to keep it unnoticed.
The Code
function drawingLoop() {
requestId = window.requestAnimationFrame(drawingLoop)
ctx.drawImage(vid, 0, 0, vidWidth, vidHeight, 0, 0, canvas.width, canvas.height);
}
function setVideoBgColor(vid, backgroundElement) {
// draw first four pixel of video to a canvas
// then get pixel color from that canvas
var canvas = document.createElement("canvas");
canvas.width = 8;
canvas.height = 8;
var ctx = canvas.getContext("2d");
ctx.drawImage(vid, 0, 0, 8, 8);
var p = ctx.getImageData(0, 0, 8, 8).data;
//dont take the first but fourth pixel [r g b a]
backgroundElement.style.backgroundColor = "rgb(" + p[60] + "," + p[61] + "," + p[62] + ")";
}
The initial idea comes from Feng (stack), but we rewrote it to fit our needs. Instead of choosing the very first pixel of the frame to pick the color, we’re taking the 16th, which seems to be a bit safer.