Smartify Your Video Embeds!

A comprehensive guide to embedding and customizing YouTube, Vimeo and self-hosted videos in web pages

Edoardo Nosotti
Aug 25, 2020 · 11 min read
Photo by Jon Flobrant on Unsplash

Video content is taking over the Internet. The trend began long ago and the most recent stats confirmed the predictions were correct. TikTok, a social network entirely based on video content, grew so much and so rapidly that it quickly became a “threat” to long-stading giants such as Instagram and even rasied concerns in the US government. Fueled by the widespread availability of high-speed internet access, also “on the run” with 4G (and soon 5G) mobile data connections, and cheap or free video hosting platforms, video content has become so pervasive in websites that it is even used to animate backgrounds.

Video embeds though are not easy ones to tame. Especially when you need to embed video hosted on a third-party platform, such as YouTube and Vimeo. It is great to piggyback on their virtually-infinite storage and bandwidth, but it comes at a price: <iframe>s.

<iframe>s got a bad rap for many reasons. They once used to pose a threat to the security of websites, because the lack of sandboxing in some old browsers let scripts cross the boundaries of the <iframe>s and possibly execute malicious code. Also, wiring the browsers’ history controls (back/forward) to <iframe>d content could be a mess. Nowadays, the countermeasures built into modern browsers make it difficult at times to properly handle <iframe>s and their content.
When it comes to embedding videos from external sources, the two complaints I hear the most are about the lack of “responsiveness” of the embeds and customization of cover images (also know as “poster” images).
The <video> elements also need some time to be mastered.

Please note that you can find the source code for some of the examples shown below in my GitHub repository and try some live demos here. The code is also available on JSFiddle.

1. Responsive videos with CSS or JS

At the time of writing, if you try to generate HTML code to embed a YouTube video from their pages, it offers this code:

<iframe width="560" height="315" ...></iframe>

The “IFrame Player API” docs also only propose “pixel” dimensions.

Vimeo instead does offer a “Resposive” size option, which adds a couple of interesting things to the embed code:

<div style="padding:56.25% 0 0 0;position:relative;">
<iframe src="https://player.vimeo.com/video/..."
style="position:absolute;top:0;left:0;width:100%;height:100%;"
frameborder="0" allow="autoplay; fullscreen" allowfullscreen>
</iframe>
</div>
<script src="https://player.vimeo.com/api/player.js"></script>

using position: relative; and padding: 56.25% 0 0 0; on a “wrapper” element, the video element is correctly constrained into the right proportions (assuming that all videos are in 16:9 format). The <iframe> and its wrapper <div> will fill the width of their container. This means that if you want your video to cover 50% of the viewport width, you need to furtherly wrap it into a container:

<div style="width: 50%;"><div style="padding:56.25% 0 0 0;position:relative;">
<iframe src="https://player.vimeo.com/video/..."
style="position:absolute;top:0;left:0;width:100%;height:100%;"
frameborder="0" allow="autoplay; fullscreen" allowfullscreen>
</iframe>
</div>
<script src="https://player.vimeo.com/api/player.js"></script>
</div>

Note: I am not fond of inline styles and you should be not too, I am using them in the code snippets for the sake of simplicity.

I tried to remove the <script> and tested the video in Chrome, FireFox and Safari on a desktop computer. It still worked. So I am not sure what this script does (I did not try to debug it, yet…).

So I applied the same sizing trick to the YouTube video:

<div style="width: 50%;">
<div style="padding:56.25% 0 0 0;position:relative;">
<iframe src="https://www.youtube.com/embed/..."
frameborder="0" allow="accelerometer; autoplay;
encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
style="position:absolute;top:0;left:0;
width:100%;height:100%;"
>
</iframe>
</div>
</div>

…and it worked! This is how you can make your 16:9 videos responsive with pure CSS.

With FitVids.js

Another viable option to make your videos responsive is FitVids.js. It is a JavaScript plugin which will recalculate and resize the videos at runtime.
Using FitVids.js is a piece of cake:

<script src="path/to/jquery.min.js"></script>
<script src="path/to/jquery.fitvids.js"></script>
<script>
$(document).ready(function(){
$('body').fitVids();
});
</script>

You can also restrict it to a specific group of videos, targeting their containers:

$('.video-container-name').fitVids();

Bring Your Own JavaScript version

If you want a better understanding of how this works, or just a simpler version of it for you to customize, I have built a couple of JS alternatives based on jQuery. This is the simplest version:

function resizeVideos(elements) {
$(elements).each(function() {
var width = $(this).width();
var height = parseInt((width * 9) / 16);
$(this).height(height);
});
}
$(function() {
resizeVideos('iframe,video');
});

it uses the elements selector (set to get every <iframe> and <video> in the snippet above) to search for videos in the page, then it recalculates their height according to their current width. The code above also assumes that the videos are in 16:9 format. Selectors can be more sophisticated than the ones shown above, jQuery offers some powerful options.

If you want something more flexible in terms of video proportions, here is a more sophisticated version:

function resizeVideos(elements) {
$(elements).each(function() {
var aspectRatio = $(this).data("aspect-ratio");
aspectRatio = aspectRatio || "16:9";
var ratioXY = aspectRatio.split(":");
var width = $(this).width();
var height =
parseInt((width * parseInt(ratioXY[1])) / ratioXY[0]);
$(this).height(height);
});
}
$(function() {
resizeVideos('iframe,video');
});

In this case, the embeds can be resized using the data-aspect-ratio="..." data attribute to constrain their proportions as shown here:

<iframe
src="https://player.vimeo.com/video/..."
width="100%"
data-aspect-ratio="16:9"
frameborder="0"
allow="fullscreen"
allowfullscreen
></iframe>

If the aspect ratio is not specified, it defaults to 16:9. See it live or in JSFiddle.

2. Background videos

If used wisely, a background video can add a bit of coolness to your pages.

Videos can’t be put in a CSS background. With CSS positioning and z-index you can get around that limitation. Note the following <div>s in my repository intro page:

<div class="content">
...page content...
</div>
<div class="video">
<video autoplay playsinline loop muted preload>
<source src="assets/bg_video.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
</div>
<div class="overlay"></div>

The <video> element must have the autoplay and muted options enabled. autoplay is required for the background to play automatically on page load, muted is just mandatory for “autoplaying” background videos (see paragraph 3 below for details).

Here’s the CSS (note the comments):

/* This is necessary for elements with 
height: 100%; to actually fill the page */

html, body {
height: 100%;
}
/* This page only has the background video,
no need to use more refined selectors. */

video {
width: 100%;
height: 100%;
/* This is necessary for the video to
stretch and fill the container, otherwise
it will be wrapped with "bands". Not cool. */

object-fit: fill;
}
/* All of the containers will be "absolutely positioned"
and cover all the window */

.content, .overlay, .video {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
/* The .content element will be placed above the others. */
.content {
z-index: 20;
}
/* The .overlay element is used to darken
the video and enhance contrast with the page
content and will be placed in a middle layer
between .content and .video. */

.overlay {
z-index: 15;
background-color: rgba(0, 0, 0, .3);
}
/* The video element will be placed under other elements. */
.video {
z-index: 10;
}

Here’s the final, working product.

Please do use low resolution videos for backgrounds. Networks may be fast, but traffic is sometimes metered (on the server and also client side, if the connection is 3G/4G/xG) and bandwidth may be limited (especially on shared hosting services). Also, some mobile devices out there are low end. Do not stress them with processing hi-res videos. You might as well set a default background image and do not show the video on smaller devices:

// Using JS to completely remove the element from the page
// because simply hiding the video with CSS does not work.
// The media resource would still be pulled from the server.
$(document).ready(function() {
if($(window).width() <= 575){ // "Small" devices size in Bootstrap
$('.bg-video').remove();
}
});

3. Why your “autoplaying” video is not playing

In modern browsers, videos are subject to restrictions can only “autoplay” under specific circumstances. In the past, heinous crimes have been committed by means of “autoplay”:

  • Ads assault
  • Jumpscares
  • Awkward office moments

so the browsers’ makers fought back, gradually eradicating the most vicious hacks designed to overcome the restrictions they put in place. To this date and on a general basis, you can only autoplay a <video> if:

Other policies might be defined at browser or OS level, but unless you are targeting specific platforms, do not rely on them.

So the autoplay can get tricky. Many times developers face complaints because they don’t realize that they are testing the pages on localhost or had already interacted with the domains in several ways, but the actual users did not and the autoplay does not work for them.

from imgflip.com

Autoplaying YouTube and Vimeo videos (and other “iframed” videos)

When you generate the embed code from YouTube or Vimeo, you have the choice to enable “autoplay” for the video. Note the following options that the code generators will add to the <iframe>s:

YouTube
<iframe
src="https://www.youtube.com/embed/...?autoplay=1"
...
allow="accelerometer; autoplay; encrypted-media;
gyroscope; picture-in-picture"
allowfullscreen

></iframe>
Vimeo
<iframe
src="https://player.vimeo.com/video/...?autoplay=1"
...
allow="autoplay; fullscreen"
allowfullscreen

></iframe>

As I mentioned in the introduction, <iframe>s are “sandboxed” in modern browsers to enforce security. Therefore, the “host” page must define policies to allow the pages and scripts running into the <iframe>s to use some of the browser’s and system’s features. Both code snippets include the allow="autoplay;" permission. Without this, the video elements in the <iframe> would not be allowed to autoplay. Also, allowfullscreen is present in both snippets, to allow the video players to run in fullscreen mode if the user wants them to. <iframe>s have a lot of options that can be set, be sure to review the documentation to get useful insights.

4. Custom cover images

Cover images for videos serve a very important purpose, beyond pure aesthetics: to “lure” users into actually watching the video. Therefore, just relying on the default frame shown by the video player will most often not cut it.

Setting a custom cover image on a <video> element

The <video> element is very friendly, it offers the poster attribute to let you set a custom cover image:

<video poster="https://picsum.photos/id/1069/1080/720" controls>
<source src="video.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>

Setting a custom cover image over a YouTube or Vimeo video

At the time of writing, the embed code generators on YouTube and Vimeo will not let you specify a custom cover image. Cover images for such embedded videos can be applied with a JS trick:

  1. Add a video embed with autoplay to the page.
  2. Remove the embedded video from the DOM at runtime.
  3. Replace the embedded video with an equally-sized and positioned cover image.
  4. Store the removed embed in a “data” attribute of the cover image or a variable.
  5. Set an event listener on the cover image and make it reverse the process when the cover image is “activated” by the user.

Since the video needs to be set to “auto play” for this solution to work, it is necessary to remove it from the DOM at runtime because just hiding it via CSS would not prevent it from auto-playing. It would play in the background, users would hear its audio and it would also waste some bandwidth.

You could also destroy the embed and re-create it from scratch via JS when the cover image is “activated” instead of storing it somewhere, but I do prefer the approach shown above because it works better with templates and templating systems and it is more maintainable. Many templating frameworks use static HTML pages with placehoders. This is convenient because the template can be tested and validated in the browser while it is built. Writing JS code to re-create the same HTML code built into a template would basically become duplicated code.

This is a preview of the core JS code to add cover images over YouTube or Vimeo videos:

HTML
(omitted, any embed element would do)
JS
function setCustomCovers(elements) {
$(elements).each(function() {
var customCover = $(this).data("custom-cover");
if (customCover) {
var coverElement = $('<a href="#" class="custom-video-cover"></a>');
var label = $(this).data("custom-cover-label");
if (label) {
coverElement.html(label);
}
coverElement.css('background-image', 'url(' + customCover + ')');
coverElement.width($(this).width());
coverElement.height($(this).height());
coverElement.data('video-element', $(this));
$(this).replaceWith(coverElement);
}
});

setCustomCoverEventListeners();
}

function setCustomCoverEventListeners() {
$('.custom-video-cover').click(function(e) {
e.preventDefault();
$(this).replaceWith($(this).data('video-element'));
});
}

Check out the repo and the demos to have a better understanding of this one.

5. Switching multiple resolutions

The <video> element does not support multiple resolutions out of the box, but with a little JavaScript work the <source> elements can be used to switch resolutions for videos.

A brief introduction to <source> elements

A <video> element can include multiple <source> elements. This is just for compatibility, so media files can be provided in different formats (or served via with different protocols) for the browser to find one they can reproduce. The <source> elements should be listed in order of preference, the browsers will use the first compatible one from the list.

Using <source> elements to switch resolutions

The code snippets below show how to switch between <source>s depending on the screen resolution. Devices with “medium” or smaller screens (< 768 pixel wide, according to the Bootstrap Grid System) will get “low resolution” videos.

HTML
<video controls playsinline loop preload>
<source src="hires.mp4" class="video-hires" type="video/mp4">
<source src="lowres.mp4" class="video-lowres" type="video/mp4">
Your browser does not support the video tag.
</video>
JS
function setMultipleResolutionSources() {
var remove = ($(window).width() < 768) ?
'source.video-hires' : 'source.video-lowres';
var videoElements = $(remove).parent();
$(remove).remove();
/* When changing the <source> elements,
* the <video> element must be reloaded. */
$(videoElements).each(function() {
$(this)[0].load();
})
}
$(function() {
setMultipleResolutionSources();
}

Note: given that modern phones have high resolution screens and mobile data networks are fast, I am not sure if serving low resolution videos to small devices does make any sense. I used that criteria because it was simple to show how the switch works. You might want to use more sophisticated filters to make decisions, according to the type of device. Or maybe just provide on-screen switches for the users to choose on their own.

6. Hosting and serving videos

The matter of properly hosting and serving video is worth entire books on its own, so I am just giving you a few pointers.

If you have a handful of short videos or a background video, your webserver or hosting plan will do just fine. If you are serious about serving video content or want to share big files with your users, you should consider using a CDN or at least other options to offload your webserver. Storage buckets such as AWS S3 are a viable alternative. A separate webserver with Nginx dedicated to serve static assets is also a good choice.

Adding a CDN is easier and cheaper than you think (even free!)

It is a best practice to serve all static assets (such as images, scripts and stylesheets) via a Content Delivery Network and online video playback can greatly benefit from the performance boost given by the edge locations of the CDNs. In simple terms, a CDN will offload your hosting servers and significantly improve the file transfer speed for your users. Users nowadays have incredibly high expectations and will quickly abandon your website if things don’t load in just a few seconds. Modern codecs are very efficient, but modern phones and cameras also have very high resolutions. So video files easily grow big and transfer speed becomes even more critical.
Most CDN providers offer plugins to add their services to all major CMSs with a few clicks. Without a plugin, you just need to set the base URL of your website into the CDN configuration and change the links to static assets in your pages adding the base URL of the CDN to the static assets paths.
Some CDNs also offer free plans or tiers, so why not give them a try? Some CDNs even offer “Video transcoding and adaptive streaming” in their free plan, meaning that you automatically get multiple versions of your video files, with specific optimizations for different types of devices, with each upload.
Note: I am NOT affiliated with any of the services linked above at this time.

RockedScience

Tutorials, tips and fast news on Cloud, DevOps and Code

Edoardo Nosotti

Written by

Senior Cloud Solutions Architect and DevOps engineer, passionate about AI, conversational interfaces and IoT.

RockedScience

Tutorials, tips and fast news on Cloud, DevOps and Code

Edoardo Nosotti

Written by

Senior Cloud Solutions Architect and DevOps engineer, passionate about AI, conversational interfaces and IoT.

RockedScience

Tutorials, tips and fast news on Cloud, DevOps and Code

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store