Dynamically adding text tracks to HTML5 video

In the past I have written on how the track element can be used to add captions and subtitles to HTML5 video, but this, and many other examples around the web, used a static example. But what if you need to load this information dynamically? This is what I’m going to take a quick look at here.

To begin with, there is a Text Track API which does allow you to dynamically add text tracks to video, so let’s look at that first.

The addTextTrack function allows us to do just that: add a text track.

var video = document.getElementById("video"), track;
video.addEventListener("loadedmetadata", function() {
   track = this.addTextTrack("captions", "English", "en");
});

It can take 3 arguments; the kind of track, the track’s label, and the track’s language, all of which are strings.

(Oddly, there’s no removeTextTrack function).

By default, each track has its mode set to "hidden", so we need to change that to "showing" to ensure that the track is shown.

track.mode = "showing";

Now that the text track has been added, we now need to add the cues for the text track, i.e. the timestamps for each track and its contents. To do this, we take advantage of the addCue function which allows us to define and add new cues via the VTTCue interface.

track.addCue(new VTTCue(0, 12, "[Test]"));
track.addCue(new VTTCue(18.7, 21.5, "This blade has a dark past."));
track.addCue(new VTTCue(22.8, 26.8, "It has shed much innocent blood."));

Each VTTCue is defined with the start time, end time, and text contents, so the first cue will start at 0 seconds, end at 12 seconds, and will display “[Test]”.

But, we need to call addCue for each and every cue that we want to add to the text track, as, weirdly, there appears to be no way to simply tell the track where to find the WebVTT file that contains all the text tracks. Simply setting track.src = "captions/sintel-en.vtt" doesn’t appear to work on any browser that I tested, the text tracks are never actually loaded.

This seems a bit odd. If we had used the <track> element in our HTML to setup our text tracks, we would have simply set the src attribute to the relevant WebVTT file, and that would be that.

<track kind="captions" label="English" srclang="en" src="captions/sintel-en.vtt">

So, can we achieve this in some other way?

Thankfully yes, but without the Text Track API.

First of all we create a new <track> element:

video.addEventListener("loadedmetadata", function() {
   track = document.createElement("track");
});

Then we initialise it by defining some of its attributes, including, this time, its src:

video.addEventListener("loadedmetadata", function() {
   track = document.createElement("track");
   track.kind = "captions";
   track.label = "English";
   track.srclang = "en";
   track.src = "captions/sintel-en.vtt";
});

As mentioned above, a new text track’s default mode is "hidden", but here we don’t set it to "showing" until the track has actually loaded:

track.addEventListener("load", function() {
   this.mode = "showing";
});

Annoyingly, this doesn’t work for Firefox 35.0.1 which currently has all text tracks set to “disabled”, and the only way to override this value is to access the text track via the video itself.

track.addEventListener("load", function() {
   this.mode = "showing";
   video.textTracks[0].mode = "showing"; // thanks Firefox
});

And finally we need to append this track to our video:

this.appendChild(track);

And putting this all together:

video.addEventListener("loadedmetadata", function() {
   track = document.createElement("track");
   track.kind = "captions";
   track.label = "English";
   track.srclang = "en";
   track.src = "captions/sintel-en.vtt";
   track.addEventListener("load", function() {
      this.mode = "showing";
      video.textTracks[0].mode = "showing"; // thanks Firefox
   });
   this.appendChild(track);
});

You can check it in action on the working example.

So while there is a way, it does seem odd that the Text Track API doesn’t have a native method for removing text tracks, nor does it have a way to dynamically load a WebVTT file.

Perhaps it’s time to file some bugs.