Introducing Video.js HTTP Streaming (VHS)

“How do I get my video to play with Video.js?”

This is one of the most frequent questions we get when working on Video.js. And it’s a good question.

If someone checks out a copy of Video.js, their content may play on one browser and not another. To get cross browser support for a specific type of content, they must stumble upon a relevant source handler (for instance, videojs-contrib-hls for HLS content). Only after including the source handler will their video play across all major browsers.

We want Video.js to be easy to use, and that isn’t the easiest setup. So we decided that it’s time to address the issue.

This was the motivation behind integrating Video.js HTTP Streaming (nicknamed VHS) inside of Video.js 7 by default.

What is VHS?

VHS is the successor to videojs-contrib-hls. It is a source handler forked from the videojs-contrib-hls repository. While videojs-contrib-hls was originally designed to add HLS playback on all browsers, we realized that the engine could also play other formats just as well.

To prove this, we added a DASH manifest parser, and with a few minor changes, VHS played DASH content with the same codebase and API as it used for HLS.

Even with the changing landscape of video technologies, Video.js will be ready. If a new streaming format gains popularity, only a few code changes will be necessary to add support to VHS. One engine and API for all formats.

Why is VHS included by default in Video.js?

By including VHS by default in Video.js, you no longer have to worry about what browser supports what streaming technology.

Video.js was built to abstract away differences in video APIs and features between web browsers, creating one simple API as close to the web standards as possible, and filling in feature gaps where possible. Over time, one area where browsers have diverged is in their support of different media formats. Some browsers may support native playback of DASH, others of HLS, and some support neither.

Previously, this would be managed by including source handlers and plugins, but we understand the importance of having a simple setup. By including VHS by default in Video.js, you no longer have to worry about what browser supports what streaming technology. It should just work.

What if I want to opt-out of using VHS?

If you would rather include a different source handler for HLS or DASH playback, Video.js still allows you to do that. Video.js core is the Video.js you know and love, without the inclusion of VHS. Everything should work as it used to.

Contributions and Feedback Welcome

As always, we’d love your contributions and feedback. The best way to reach us for comments, questions, requests, contributions, or just to say hello are the VHS GitHub page and the #playback channel on the Video.js slack.

Video.js 7.3: Responsive Layout, Fill Mode, createLogger

Another month, another Video.js release: v7.3.0. This release brings a long wanted feature: Responsive Layout.
In addition, Fill Mode has been promoted to a first-class feature and createLogger was added to make debugging and logging easier.

This is currently out as a pre-release and will be promoted to latest in about a week. Please try it out and report any issues you find.


I’d like to thank everyone that was involved in making changes that landed in this release. Any and all changes are really appreciated.

Responsive Mode

Responsive Mode will make the player adjust UI components to the size of the player. It uses the playerresize event that was added in v6.7.0 of Video.js, which allows us to know when the player changes sizes.

Responsive mode will set and change certain breakpoint classes like vjs-layout-small when the player size changed. These can be configured. Depending on which class is currently on the player, the control bar and other UI elements can adapt. For example, with vjs-layout-small, the time controls will not show because the time tooltips on the progress bar are available and the captions button is more important. At a larger size, both can be shown without a problem.

You can find out how to enable Responsive Mode and more in our docs page. There is also an example playground in the sandbox folder in the repo.

Fill Mode

Fill Mode allows the Video.js player to resize dynamically, but stay contained within the bounds of the parent container. This is analogous to Fluid Mode, but sometimes you the container is already being sized properly.

Fill Mode is not a brand new mode, the class vjs-fill has been available in Video.js for quite a while. This finally makes it a first-class feature to go along with Fluid Mode.


This is a new method on videojs.log that allows you to create a new logger with a specific name. It then creates a chain of names to make it easier to track which component logged this particular message. In particular, this can help plugin authors to log messages and then filter out only the messages that are associated with their plugin.

createLogger returns a function with the same API as videojs.log. You can see it in action below.


A new method, player.log was added which uses createLogger behind the scenes. It logs the player ID in addition to VIDEOJS:

var player = videojs('myid');

// VIDEOJS: myid: foo

You can also go further and create a sub logger:

var player = videojs('myid');
var mylog = player.log.createLogger('my-plugin');

// VIDEOJS: myid: my-plugin: foo

If you want to log a warning or error for your custom plugin:

var player = videojs('myid');
var mylog = player.log.createLogger('my-plugin');

// VIDEOJS: myid: my-plugin: WARN: foo

// VIDEOJS: vid1: my-plugin: ERROR: bar

Video.js 7.1 and 6.11: Autoplay and Fullscreen changes

It’s been a while since the last release post. The 7.1.0 and 6.11.0 releases of Video.js are full of great features from contributors and maintainers.
The two big changes are related to autoplay – when will we stop talking about autoplay? – and fullscreen. Though, there’s plenty more in there.

Video.js 7.1.0 and 6.11.0 are out as next and next-6 respectively on npm.

To get 7.1.0 via npm:

npm install video.js@next

And 6.11.0 via npm:

npm install video.js@next-6


We’ve been keeping track of changes happening around autoplay in browser vendors. We’ve also done a lot of work to make Video.js work as well as possible with the new autoplay policies. However, there were still some use cases that weren’t accounted for. To address those, we decided to extend the existing Video.js autoplay option to include a few new values: muted, play, any. The current boolean options will work as they do now and unknown options will be treated as they do now.

  • muted - this will mute the video element before calling play() on loadstart.
  • play - this will call play() on loadstart. This is similar to the autoplay attribute but with a call to play instead of using the attribute.
  • any - this will call play() on loadstart but if it fails, it will try and mute the player and try play()ing again.


There are two fullscreen related changes in these releases.

The first one is one that was often request and it’s finally here: double click to toggle fullscreen. Double clicking inside the player area – outside of the control bar and modal dialogs – will enter or exit fullscreen playback.

The second change is to disable the fullscreen toggle if fullscreen is not available, for example, inside of an iframe without allowfullscreen attribute

Fullscreen available
Fullscreen unavailable

Notable Changes

  • Make audio tracks that are of “main-desc” kind show up with an Audio Description icon
  • Run our CSS through autoprefixer which reduces the output size a bit.
  • Make the mute toggle show up if muting is possible even if changing volume programmatically isn’t. For example, on iOS.
  • Make setSource be option for middleware.


I’d like to thank everyone that contributed to this release!

Video.js 7 is here! 🎉

I’m happy to announce that Video.js 7 is now out! It brings to Video.js support for HLS and experimental DASH support via the Video.js HTTP Streaming (VHS) project. Video.js 7 also drops support for some older Internet Explorer browsers only keeping support for IE11.

While it’s out, it’s still in pre-release for a short while and only available under the next tag on npm. Some time next week, we’ll flip the switch and make it latest.

changes for removing IE8 code
This release is pretty exciting because it means we were able to remove a bunch of code for old browsers and we can now start using newer web platform features that we had previously been avoiding like the :not() css selector. It’ll also free us to look forward at new features and not be held back by needing to support old browsers like IE8.

Getting Video.js 7

You can get it from npm via

npm install video.js@next


npm install video.js@7.0.0

From GitHub Releases,

Or from our or another CDN



VHS and Video.js

Video.js will be bundling VHS by default for ease of use for new users. However, some people don’t want VHS or are using another plugin. For this, we have a separate build of Video.js which doesn’t include VHS.

You can get it from our CDN or another CDN



Finally, expect a more detailed post about VHS in the weeks to come.

What the future holds

Video.js hasn’t really had a roadmap for a long time. With this release, we think it’s time to take a step back and create a roadmap.

The roadmap will take into account currently open issues and internal and external feature requests. In addition, we’re going to look at currently available features and see if it’s worth refactoring them with the more modern web in-mind.

Later down the line, we’d also want to make a roadmap for a Video.js 8. Given that this release only adds VHS and removes old browsers, there’re plenty of already deprecated features that it’s time to remove.

Updates from the Video.js 7 roadmap

Video.js 5.x

As of this release, Video.js 5.x is now officially deprecated. This means that we will no longer work on it or back-port fixes to it but it’s still available for usage if you’re still using it. We do urge you to update to the latest 5.x, or better yet, upgrade to 6.x or 7.

Build Tools

In the roadmap post, we talked about potentially switching to webpack from Rollup for better support of importing VHS into Video.js. However, while doing this work, we found out that the filesize of Video.js will increase by about 20%, which isn’t a trivial amount, not to mention that the filesize is already going to increase from the default inclusion of VHS.

Eventually, after much work, we were able to figure out how to bundle VHS using Rollup as well and we are continuing to use Rollup for our builds.

Google Analytics

Video.js 7’s CDN builds will no longer send any data to Google Analytics via our stripped down pixel tracking.

Video.js 6 and below CDN builds will continue sending data, unless you opt out with window.HELP_IMPROVE_VIDEOJS = false, but versions 6.8 and above will honor the Do Not Track option that users can set in their headers before sending the data.

We are continuing to investigate our options for collecting our CDN logs in an open manner and will post updates when we have any.

Autoplay Best Practices with Video.js


  • Never assume autoplay will work.
  • Using the muted attribute/option will improve the chances that autoplay will succeed.
  • Prefer programmatic autoplay via the method, avoiding the autoplay attribute/option.


There has been a lot of chatter about autoplay lately and we wanted to take a few minutes to share some best practices when using autoplay with Video.js.

This post is meant to be a source of information - not an editorial or positional statement on autoplaying video. A lot of users hate autoplay because it’s annoying or it consumes precious bandwidth. And a lot of publishers providing content free of charge rely on autoplay for the preroll ads that finance their businesses. Browser vendors (and open source library authors) have to weigh these concerns based on the interests of their users, publishers across the web, and their own business. And users have to choose the browser that best reflects their preferences.

This post is not about ways to circumvent autoplay policies. That’s not possible and no one should waste their time attempting it. We think it’s uncontroversial to say that actively circumventing the browser’s built-in user experience or user preferences is harmful.

Autoplay Policies in the Big Browsers

I’m not going to go into exhaustive detail on each browser’s specific algorithms because they are subject to change and it would defeat the core point of this post: never assume autoplay will work.


As of Safari 11, which shipped in September 2017, Apple has updated their autoplay policies to prevent autoplay with sound on most websites.


Back in September 2017, Google announced that Chrome’s autoplay policy would change in April 2018 with Chrome 66, subject to a series of rules you can read about in the linked article.


With Firefox, Mozilla has taken the position of not having a firm autoplay policy for the time being. That may change in the future. That said, Firefox today does allow users to disable autoplay with a few configuration changes.

IE11 and Edge

Microsoft’s browsers have no specific/known autoplay policy - at the time of writing, autoplay just works in IE11 and Edge.

Refresher on How to Request Autoplay in Video.js

Note how the title of this section uses the word “request.” That’s intentional because it should be thought of as a request. Remember, never assume autoplay will work.

One of Video.js’ strengths is that it is designed around the native <video> element interface; so, it will defer to the underlying playback technology (in most cases, HTML5 video) and the browser. In other words, if the browser allows it, Video.js allows it.

There are two ways to enable autoplay when using Video.js:

  1. Use the autoplay attribute on either the <video> element or the new <video-js> element.

    <video autoplay src="https://path/to/source.mp4"></video>
    <video-js autoplay src="https://path/to/source.mp4"></video-js>
  2. Use the autoplay option:

    videojs('player', {autoplay: true});

By default, you can defer to the default behavior in Video.js. If autoplay succeeds, the Video.js player will begin playing. If autoplay fails, the Video.js player will behave as if autoplay were off - i.e. it will display the “big play button” component over the poster image (or a black box).

If that works for you, your job is done!

TIP: If you want to use autoplay and improve the chances of it working, use the muted attribute (or the muted option, with Video.js).

Beyond that, there are some general practices that may be useful when dealing with autoplay: detecting whether autoplay is supported at all, or detecting whether autoplay succeeds or fails.

Detecting Autoplay Support

Similar to detecting successful or failed autoplay requests, it may be useful to perform autoplay feature detection on the browser before creating a player at all. In these cases, the can-autoplay library is the best solution. It provides a similar Promise-based API to the native method:

7 {
if (obj.result === true) {
// Can autoplay
} else {
// Can not autoplay

Programmatic Autoplay and Success/Failure Detection

For those who care whether or not an autoplay request was successful, both Google and Apple have recommended the same practice for detecting autoplay success or rejection: listen to the Promise returned by the method (in browsers that support it) to determine if autoplay was successful or not.

This can be coupled with the autoplay attribute/option or performed programmatically by calling, but we recommend avoiding the autoplay attribute/option altogether and requesting autoplay programmatically:

<video-js id="player" src="https://path/to/source.mp4"></video-js>
var player = videojs('player');

player.ready(function() {
var promise =;

if (promise !== undefined) {
promise.then(function() {
// Autoplay started!
}).catch(function(error) {
// Autoplay was prevented.

NOTE: It’s important to understand that using this approach, the Video.js player.autoplay() method will return undefined or false. If you expect your users or integrators to rely on this method, consider the following section.

Programmatic Autoplay with the autoplay Attribute/Option

When building a player for others to use, you may not always have control over whether your users include the autoplay attribute/option on their instance of your player. Thankfully, combining this with programmatic autoplay doesn’t seem to have a significant effect on the behavior of playback.

Based on our experimentation, even if the browser handles the actual autoplay operation, calling after it’s begun playing (or failed to play) does not seem to cause current browsers to trigger an extra "play" event in either Chrome or Firefox. It does seem that Safari 11.1 triggers "playing" and "pause" events each time is called and autoplay fails.

Still, if you have full control, we recommend avoiding the autoplay attribute/option altogether and requesting autoplay programmatically.

NOTE: Even if using the autoplay attribute/option with programmatic autoplay, the player.autoplay() method will return undefined until the player is ready.

Example Use-Case

At Brightcove, one thing we’ve done to improve the user experience of our Video.js-based player is to hide the “big play button” for players requesting autoplay until the autoplay either succeeds or fails. This prevents a periodic situation where the “big play button” flashes on the player for a moment before autoplay kicks in.

While our actual implementation has more complexity due to specific circumstances with our player, the gist of it is the same as this functional JSBin demonstration.


There may be decision points in your code related to whether autoplay succeeds or not; however, the practical reality is that all the paragraphs before this boil down to a single, essential concept:

Never assume that your request to autoplay will succeed.

Keep that in mind and you’re golden. Even if the browsers stop allowing autoplay entirely, our hope is that recommending this approach will be somewhat future-proof.

videojs-contrib-ads 6

A major update to videojs-contrib-ads has been released. In this post, we’ll take a look at what videojs-contrib-ads offers and what has improved in version 6.

What is contrib-ads?

Videojs-contrib-ads is a framework for creating Video.js ad plugins. Seamlessly integrating ad support into a video player can be a daunting task, especially if you have other plugins that may be effected. For example, playing ads may result in additional media events. Imagine an analytics system that listens to “loadstart” events: it would start seeing double when extra loadstart events result from preroll ads. contrib-ads has a feature called Redispatch that makes sure ads do not trigger extra media events. This keeps things as simple as possible without breaking other plugins. Additional benefits of using contrib-ads are listed in the readme.

Version 6

Version 6 of contrib-ads is a maintenance release that includes a major code refactor. The core of the project’s behavior is handled by a state machine that advances through various states as the player plays content, preroll ads, midroll ads, and postroll ads. The initial state machine was designed to implement a specific set of functionality when the project was first conceptualized. Over the years, the project became much more fully-featured and many new scenarios and edge-cases arose. In this refactor, the state machine has been updated to precisely match the modern feature-set. This has resulted in a more maintainable and reliable codebase.

How it works

Ad mode is strictly defined as if content playback is blocked by the ad plugin.

contrib-ads has a concept called ad mode. Ad mode is strictly defined as if content playback is blocked by the ad plugin. This does not necessarily mean that an ad is playing. For example, if there is a preroll ad, after a play request we show a loading spinner until ad playback begins. This is considered part of ad mode. The public method can be used to check if we are in ad mode.

In version 6, the state machine was refactored to match this strict definition of ad mode. There are two types of states: content states and ad states. If contrib-ads is in an ad state, it is in ad mode.

Here is a diagram of the new states. Blue states are content states and yellow states are ad states:

For a history of how the state machine has evolved over time, there is detailed information in this Github issue. In this article we’re going to focus on how it works in version 6.

Let’s walk through the states for a simple preroll scenario. contrib-ads begins in the BeforePreroll state. This is considered a content state because content playback hasn’t been requested yet and so ads are not blocking playback. The ad plugin can asynchronously request ads from an ad server during this time, even though the plugin is in content mode. The ad plugin triggers the adsready event when it’s ready. Contrib-ads knows that once the play button is pressed, ads are already prepared.

Now the user clicks play. At this point ad mode begins and contrib-ads moves forward to the Preroll state. The Preroll state shows a loading spinner until ad playback begins. The Preroll state knows that the ad plugin is ready and that play was clicked, so triggers readyforpreroll to inform the ad plugin that it’s time to play an ad. Once ad playback begins, the control bar turns yellow and will not allow seeking. If the ad takes too long to begin playing, a timeout will occur and content will resume without further adieu. When the ad is complete, contrib-ads restores the content video. Only when content playback results in a playing event does ad mode end.

Now we’re in the ContentPlayback state. At this point, the ad plugin could play a midroll ad, causing a brief foray into the Midroll state, or content could continue without interruption.

When content ends, normally we would see an ended event right away. Instead, contrib-ads sends a contentended event, which is the ad plugin’s chance to play postroll ads. Perhaps the ad plugin has decided not to play postroll ad, so it responds by triggering nopostroll. contrib-ads knows that now we’re really done, so an ended event is triggered.

Finally, since content has ended for the first time, we now transition to the AdsDone state. There will be no more ads, even if the user seeks backwards and plays through the content again.


Ads may not be your favorite part of watching video online, but contrib-ads makes sure that they don’t break your site and that content plays even if ads fail. If you’re interested in learning more or contributing to the project, check it out on Github!

Video.js 7 Roadmap

Today, I’m excited to announce the roadmap to Video.js 7! While this is a major version update, there is very little that is actually breaking. The two main changes are the addition of videojs-http-streaming, VHS for short, and the removal of support for older versions of Internet Explorer.


Video.js HTTP Streaming

VHS used to be videojs-contrib-hls, but as MPEG-DASH became more popular, we realized that we wanted to support it as well and that we can share a lot of the code between HLS and DASH implementations. Look for more details in an announcement post for VHS soon!
VHS will ship with Video.js by default because one of the guiding principles of Video.js is to make it easy for users to just put it on the page and have a player that works across browsers. With HLS and DASH becoming so popular, we thought it was about time for a plug and playable Video.js for the most common streaming formats.
In addition to having it be included by default, we’ll make sure to provide builds that exclude VHS for those that don’t need it or are using another playback engine like HLS.js.

Old IE


Video.js has a long history of making best effort to support IE, starting with the Flash fallback originally created for IE8. When Video.js 5 was released, we had a lot of code in place to support IE8 and when Video.js 6 came around, with Flash on its way out, we moved Flash support to a separate project, videojs-flash. Now, based on usage data, we are planning to remove support for IE8, IE9, and IE10.
According to the data we’ve gathered, IE in total has a share of about 4%. Of those 4%, IE8, IE9, and IE10 combined make up barely 5%, and IE11 has around 91% usage. This means that combined IE8, IE9, and IE10 are barely 0.002% of the total usage of Video.js. Based on this incredibly small footprint, we believe it isn’t worth the substantial effort required to maintain support for these browsers. The good news is, for those concerned, Video.js 6 isn’t going away anytime soon and will be available for your old IE support needs.
In addition to the statistics, our tests currently take between 5 and 10 minutes to run on IE8. Removing this will allow us to greatly decrease the duration of our test suite. Even worse, sometimes the tests timeout and retry for up to 40 minutes but when they restart they pass on the first try. Not having to deal with these types of issues will allow us to increase our testing infrastructure and provide a better product.

Thanks Browserstack for sponsoring browser-based testing for Video.js!

Video.js 5.x

When Video.js 7 is released it’ll be the end-of-life for Video.js 5.x from a support perspective. Of course, we are not going to unpublish this version from npm or remove the files from the CDN, so you are free to continue using it. However, we urge you to update at your earliest convenience as we will no longer be accepting fixes.

Google Analytics

Thanks Fastly for sponsoring

Video.js provides CDN hosted files for Video.js, sponsored by Fastly – thanks Fastly! These files currently have a stripped down pixel tracking for Google Analytics (GA) that sends limited information to a GA account we own. We did a terrible job communicating that this is happening and how users can opt out – set window.HELP_IMPROVE_VIDEOJS to false before loading in Video.js – or use another CDN like unpkg or CDNJS. Recently we made a change that makes this tracking pixel honor the Do Not Track flag set by users in the browser, however, this isn’t going to affect previously released versions of Video.js

In addition, starting with Video.js 7.0 (and probably back ported to newer versions of 6.x) we will no longer include this tracking pixel in our CDN builds. Instead, we are investigating our options for using the CDN logs via Fastly. We expect to share more details as we get closer to the Video.js 7 release date and have a better idea of what we will be doing.

Build Tools

Currently, Video.js uses rollup to combine all the Video.js files into two files for different build systems. One that excludes dependencies, for use in bundlers, and another that includes dependencies for a UMD file that can be used on a page as a <script>. We are looking to transition our build and test tooling from rollup and browserify to Webpack 4.0 to provide more flexibility in our builds and to do a better job at being able to build with VHS (as many people remember the infamous issue 600 on the contrib-hls repo).
In addition, we’ll be looking into allowing custom builds when bundling Video.js. For example, if one doesn’t want VHS or doesn’t want visible controls, a surprisingly large part of the player, they can set VJS_VHS=false VJS_CONTROLS=false when they’re bundling their application to keep these pieces out of the build.


We are still working on a lot of what was mentioned above but our hope is to have the first pre-release of 7.0 before the end of March.

We’re pretty excited about this not-so-major update and hope you will be too!

videojs-vr Is Now Under The Video.js Org

Brightcove and Video.js have adopted the videojs-vr plugin as a first class plugin.
It’s out now with support for Video.js 6!

Where did it come from?

This plugin has a long history and has had many maintainers.
It started as slawrence/videojs-vr. From there it became metacdn/videojs-vr. Then there was a private collaberation between, Brightcove, and HapYak. Eventually we decided to treat this plugin like a first class citizen. So we updated it to the advanced plugin API, fixed a few long-standing issues, documented other issues along the way, and moved it to the Video.js Organization.

How do you know a video is 360/VR?

On desktop players

In a non-browser environment this question is easily answered, because there is a generally agreed upon standard. The video itself should have metadata indicating that the video is a 360/VR video and how to display it. Even outside of the browser though it is a bit tricky because most programs (even ffmpeg) don’t support the insertion of this metadata. In fact a lot of them will strip said metadata as they think it is invalid. That is where a project called Spatial Media Metadata Injector comes in. Spatial Media Metadata Injector injects 360/VR metadata into the video without changing anything else.
That allows players like VLC to know that a video is 360/VR.

How does videojs-vr do it?

The browser does not expose video metadata in an API so we would have to parse it ourselves, which isn’t really an option. So in videojs-vr we have a projection option that can be passed in during plugin initialization.

The first and default setting for projection is 'AUTO'. Setting projection to 'AUTO' tells videojs-vr to look at player.mediainfo.projection. player.mediainfo.projection will have to be set by some external plugin/script which is told by a server that the current video is 360/VR. The player.mediainfo.projection of a video can be any of the following:

  • '360', 'Sphere', or 'equirectangular': The video is a sphere
  • 'Cube' or '360_CUBE': The video is a cube
  • 'NONE': This video is not a 360 video, the videojs-vr plugin should do nothing. this would not have to be set as it will be assumed if no projection exists.
  • '360_LR': Used for side-by-side 360 videos
  • '360_TB': Used for top-to-bottom 360 videos

Otherwise, the projection can manually be set on plugin initialization to any of the above values. The plugin can then be disposed and re-initialized for each video with a different setting.

What happens after after we know a video is 360/VR?

Using the current projection value we determine what we need to show on our three.js canvas. From there we use the polyfilled (via webvr-polyfill or real VRDisplay API to set up controls. If the device has a VIVE, Oculus, etc plugged in we use that. If not we display the video in 360 mode where the mouse/keyboard/accelerometer control the video. On mobile devies there is also a button to go into cardboard mode which will show the video as two smaller videos separated by a line. This is so that the video can be viewed using pseudo VR headsets (such as google cardboard) and a phone. While the video is playing we use animation frames to animate the VideoTexture on the three.js canvas. That allows us to show the correct portion of the video based on the controls that the user is inputting.

Why is it so huge?

We all agree that the plugin is huge, but due to the fact that all browsers do not natively support the VRDisplay API, we have to include a lot of big dependencies in our project. Mainly the size comes from the following:

  1. three.js which we use to create the VR/360 canvas, and implementing it on our own would be painful
  2. webvr-polyfill which polyfills VR features for browsers that don’t currently support VR (outside of experimental flags and builds)


Every object from three.js and webvr manager (from webvr-boilerplate) is available on the instance of the VR plugin that will is returned by player.vr().

Known Issues

  • Multiple players on the same page will share controls, panning in one video will pan in the other.

The Future

  • Implementing player level controls rather than window controls. This will allow individual videos to be controlled separately based on the current focus.
  • A menu so that multiple VR displays can be used rather than just the first one that we find.

To see the code, submit an issue, suggest a feature, or submit a pr

See the github repo videojs-vr

Video.js 6.7.1 released

Video.js 6.7.1 was released this week. This comes a month and a half since the first 6.6.0 release (which we forgot to blog about) and only a week since the latest patch release (6.6.3, which is now latest).

6.7 has two awesome new features and a couple helpers:

  • a working playerresize event
  • a new mediator type for middleware
  • getPlayer and getAllPlayers helper on videojs.


Recently, we also switched over all our online properties to run on Netlify. This is great because it gives us HTTPS via Let’s Encrypt but also allows us much better automation for the website, docs website, and blog. Since the docs website is tied to the main Video.js repo and has a build per PR, we also had Netlify generate an example page for the PR based on the sandbox examples. Here’s the example page for a recent PR.


This new event will fire each time the player is resized. It will fire when going fullscreen and when exiting fullscreen and when resizing the player via the dimension methods or if the player is in fluid mode and the window is resized.
It uses the new ResizeObserver in Chrome 64 and wherever it is available. If it isn’t available, a polyfill can be passed in or it will use its fallback. The fallback uses an absolutely positioned, hidden iframe that’s the size of the player and then retriggers the iframe’s resize event on a debounced handler.
Also, I wanted to note that the resize player event does not refer to the size of the player itself but rather to the native resize event, which is triggered when the videoHeight or videoWidth has changed.

Mediator type for middleware

This is the first major feature for middleware since they were released in 6.0. The main reason for the mediator is for middleware to be able to cancel requests to play(). Thus, they are currently limited to play() and pause() calls currently. Once we iron out the details we hope to enable it for further functionality.
The mediator middleware allows you to intercept the calls to the mediator methods, like play(), and then prevent the calls from occuring on the tech. This is important for methods like play() because while calling pause() immediately after play() may succeed in “cancelling” playback, it also has some unintended side-effects in a few cases. The mediator middleware can decide whether play() should go through, thus eliminating the need to call pause(). Afterwards, all middleware will be notified whether the call was terminated or it will be given the return value. In the case of play(), it will be the play promise itself.

Here’s a simple example:

videojs.use('*', (player) => ({
setSource(src, next) {
next(null, src);

callPlay() {
// return this value to terminate the method call
return videojs.middleware.TERMINATOR;

play(terminated, playPromise) {
if (terminated) {
return console.error(terminated, 'play was terminated');

.then(() => console.log('play succeeded!'))
.catch(() => console.log('play failed :('));

Player helpers

These are to make it easier to manage your players and reduce unintended side-effects. A lot of times, a player is being created automatically via data-setup but then to refer to it via code, videojs() is called. In some cases, this will actually initialize the player because it ended up running before the auto setup. Now, videojs.getPlayer() can be used instead and it will never create the player. It is the preferred way of getting the player once it has been created.
videojs.getAllPlayers() is just a nice way of getting a list of all the players that are currently available on the page.



6.7.1 (2018-01-31)

Bug Fixes

  • middleware: do a null check in mediator methods (#4913) (7670db6)

6.7.0 (2018-01-30)


Bug Fixes

  • do not patch canplaytype on android chrome (#4885) (f03ac5e)



  • Update and to include label meanings (#4874) (a345971)


  • add project and build names to browserstack (#4903) (41fd5cb)

Video.js 6.5.0 Release

November 17th marked the pre-release of Video.js 6.5.0. It comes shortly after the release of 6.4.0, which has now been promoted to latest! This is a pretty exciting release because we finally got our own element! I like to call it the I Can’t Believe It’s Not Custom Elements because it isn’t an actual custom element but it’s not a standard HTML element either. Also, a nice and smooth progress bar thanks to a first time contributor; thanks @vhmth!. A pretty major memory leak fix, and many code refactors and bug fixes.
I’d like to thank everyone who contributed and the four first time contributors to Video.js: @vhmth, @FirefoxMetzger, @EhsanCh, and @shahlabs.
This post comes a week late because it was Thanksgiving week in the US after release and I was on break, if you celebrate Turkey day 🦃, hope you had a good one!

Notable Changes

  • I Can’t Believe It’s Not Custom Elements with a <video-js> element. It works exactly like the <video> embed but without requiring you to use the <video> element and the benefits and drawbacks that come with it. It also adds the video-js class name automatically for you, so, there’s no need to add it.
  • A smooth progress bar!
    before the change
    after the change
  • After much spelunking in the code base and developer tools, we’ve plugged most of the memory leaks with retained DOM elements in Video.js!
  • Many code refactorings from our old code style to the new one by @kocoten1992!
  • Previously, you could accidentally seek or toggle playback using the middle or right clicks. Now, just go ahead and try it!
  • An accessibility fix with regards to title tooltips and menu items.
  • Better handling of play() in our new asynchronous world, especially immediately after changing the source.


Raw Changelog

6.5.0 (2017-11-17)


Bug Fixes

  • Avoid empty but shown title attribute with menu items and clickable components (#4746) (dc588dd)
  • Player#play: Wait for loadstart in play() when changing sources instead of just ready. (#4743) (26b0d2c)
  • being able to toggle playback with middle click (#4756) (7a776ee), closes #4689
  • make the progress bar progress smoothly (#4591) (acc641a)
  • only allow left click dragging on progress bar and volume control (#4613) (79b4355)
  • only print element not in DOM warning on player creation (#4755) (bbea5cc)
  • trigger timeupdate during seek (#4754) (1fcd5ae)


Code Refactoring


Performance Improvements

  • null out els on dispose to minimize detached els (#4745) (2da7af1)