Skip to main content

HTML Track Element

Audio and video are about sound and pictures. But people also need words: the words being spoken, a translation, a description of what is on screen, or a list of chapters to jump to. All of these are text that is timed to the media.

The <track> element attaches such text to a <video> or <audio>. The text lives in a separate file, in a simple format called WebVTT, and the browser shows it, or makes it available to your script, at exactly the right moment.


What You Will Learn​

  • What <track> is, and where it goes
  • The five kinds: subtitles, captions, descriptions, chapters, and metadata
  • The attributes src, kind, srclang, label, and default
  • The basics of a WebVTT file
  • Reading and reacting to cues with JavaScript
  • Cross-origin rules, security, and common mistakes

The Basics​

index.html
<video controls width="640" height="360" src="lesson.mp4">
<track
kind="captions"
src="lesson.en.vtt"
srclang="en"
label="English"
default
>
<track
kind="subtitles"
src="lesson.hi.vtt"
srclang="hi"
label="हिन्दी"
>
</video>
lesson.en.vtt
WEBVTT

00:00.000 --> 00:03.000
Welcome to the lesson.

00:03.500 --> 00:06.000
[keyboard clicking] Let's open the editor.

<track> is a void element (no closing tag), and it goes inside <audio> or <video>, after any <source> elements.

The Five Kinds​

kindWhat it holdsShown by the browser?
subtitlesA translation of the speech, for people who can hear but do not know the languageYes, as text over the video
captionsThe speech plus important sounds ("[door slams]", "[music]"), for people who cannot hearYes, as text over the video
descriptionsText describing what is on screen, for people who cannot see itNot drawn: meant to be read out by a screen reader or a script
chaptersTitles for sections, for navigationDepends on the browser. Often used by your own script
metadataData for your script (for example, "show this button now")Never shown

If you leave out kind, it defaults to subtitles. The next two lessons, Captions and Subtitles, cover the two most common kinds in detail.

Attributes​

AttributeWhat it doesNotes
srcThe address of the WebVTT fileRequired
kindThe kind of textSee the table above
srclangThe language of the text, as a language code (en, hi, es, ar)Required for subtitles
labelThe name shown in the player's menuWrite it in its own language ("Español", "हिन्दी")
defaultTurns this track on unless the visitor chose anotherOnly one per kind group

Tips:

  • Always add label. Without it, the menu may show an empty or technical name.
  • Write each label in the language it names, so a visitor can find their own language, even if the page is in another one.
  • Use only one default. If several tracks have it, browsers use the first one.
  • The file name is yours to choose, but a clear pattern such as lesson.en.vtt and lesson.hi.vtt keeps things tidy.

A WebVTT File in 60 Seconds​

WebVTT (.vtt) is a plain text format. A tiny file looks like this:

lesson.en.vtt
WEBVTT

NOTE This is a comment. Viewers do not see it.

intro
00:00.000 --> 00:03.000
Welcome to the lesson.

00:03.500 --> 00:06.000 align:start position:10%
[keyboard clicking]
Let's open the editor.

00:01:02.000 --> 00:01:05.500
Lines of an hour or more use <b>hh:mm:ss.ttt</b>.

The rules:

  1. The first line is WEBVTT.
  2. Each cue is a block, separated from the next by a blank line.
  3. An optional identifier line may come first (like intro).
  4. The timing line is start --> end. Use mm:ss.ttt or hh:mm:ss.ttt, with a dot before the milliseconds (not a comma, which is SRT format).
  5. One or more lines of text follow. A cue can have several lines.
  6. Text may use a few tags such as <b>, <i>, <u>, and <c.name>, and the voice tag <v Asha>.
  7. Save the file as UTF-8, so any language shows correctly.

Try It: Check a WebVTT File​

Edit the file, and see what the browser would read. Then load the file with mistakes, and fix them.

script.js
// Read the cues of a track after it has loaded
const track = document.querySelector("track");
track.track.mode = "hidden"; // load the cues without drawing them

track.addEventListener("load", () => {
for (const cue of track.track.cues) {
console.log(cue.startTime + " to " + cue.endTime + ": " + cue.text);
}
});

Try It: Cues in Action​

Here is a real <audio> element with a real <track kind="metadata">. The page reads the cues from the track, builds the list, and highlights the active cue while the sound plays. Metadata tracks are never drawn by the browser, so everything you see comes from the script.

index.html
<audio id="sound" src="melody.wav" controls>
<track id="notes" kind="metadata" src="melody.vtt" srclang="en" label="Melody notes">
</audio>
<ol id="list"></ol>
script.js
const trackElement = document.getElementById("notes");
const textTrack = trackElement.track;
const list = document.getElementById("list");

textTrack.mode = "hidden"; // "disabled" would never load the cues

trackElement.addEventListener("load", () => {
for (const cue of textTrack.cues) {
const item = document.createElement("li");
item.textContent = cue.text; // textContent: safe
list.append(item);
}
});

textTrack.addEventListener("cuechange", () => {
const items = list.children;
for (let i = 0; i < items.length; i++) items[i].removeAttribute("aria-current");

const current = textTrack.activeCues[0];
if (current) {
const index = Array.from(textTrack.cues).indexOf(current);
items[index].setAttribute("aria-current", "true");
}
});

Track Modes​

Every track has a mode, which you can read and set from script:

ModeCues loaded?Shown on screen?Typical use
disabledNoNoOff. The file may not even be downloaded
hiddenYesNoYour script reads the cues, and cuechange fires
showingYesYesThe browser draws captions or subtitles
const video = document.querySelector("video");

for (const track of video.textTracks) {
console.log(track.kind, track.language, track.label, track.mode);
}

// Turn on the English captions
for (const track of video.textTracks) {
track.mode = (track.language === "en" && track.kind === "captions") ? "showing" : "disabled";
}

You can also create a track from script, with no file:

const extra = video.addTextTrack("captions", "English", "en");
extra.mode = "showing";
extra.addCue(new VTTCue(0, 2, "Hello from script"));

Styling Captions​

Browsers draw captions in a box at the bottom. You can adjust the look with the ::cue pseudo-element:

video::cue {
background: rgba(0, 0, 0, 0.8);
color: #fff;
font-size: 1.2rem;
}

video::cue(b) {
color: #fde047;
}

Remember:

  • Only a few CSS properties work on ::cue (colour, background, font, outline, text shadow, and similar). Layout and spacing do not.
  • Visitors' own settings come first. Many people set their caption size and colours in their device or browser. Do not fight those settings.
  • Always keep strong contrast (4.5 to 1 or better).
  • Use line and position settings in the VTT file to move captions away from important parts of the picture.

Cross-Origin Tracks​

A <track> file must come from the same site as your page, or the file's server must allow it and you must add crossorigin to the media element.

<video controls crossorigin="anonymous" src="https://cdn.example.com/lesson.mp4">
<track kind="captions" src="https://cdn.example.com/lesson.en.vtt" srclang="en" label="English">
</video>

If captions work on your computer but not on your live site, this is the first thing to check. The server holding the .vtt file needs a response header such as Access-Control-Allow-Origin, and it should send the type text/vtt.

Accessibility at a Glance​

  • Use captions for anything with speech or important sound, not subtitles. Captions include sound effects and speaker names, and subtitles do not.
  • Always add label and srclang, and write the label in its own language.
  • Make the captions match the audio: complete, accurate, and in sync (appearing within about a fifth of a second of the speech).
  • Name speakers when it is not clear who is talking (<v Asha>), and describe sounds that matter ("[phone rings]").
  • Do not hard-code captions into the picture as the only option. Separate tracks can be turned off, resized, translated, and searched.
  • Use descriptions for visual-only information, and make sure your script exposes the text to assistive technology (for example, in an aria-live region), because browsers do not read it aloud for you.
  • Check the captions menu with the keyboard.

Security and Privacy Notes​

  • A .vtt file is public. Anyone can fetch it. Do not put private notes in NOTE comments or in hidden cues.
  • Treat cue text as text, not HTML. When your script copies cue.text into the page, use textContent, never innerHTML. Cue text can contain tags, and an untrusted file could carry harmful markup.
  • Only load tracks from sources you trust. A track from a stranger's site is content you did not write, and it appears in front of your visitors.
  • Serve over HTTPS, and with the correct type (text/vtt).
  • Caption data can contain personal details (names, health or legal information). Handle it as carefully as the audio itself.

Global Audience Notes​

  • Offer more than one language with separate track elements, each with srclang and a label in its own language. See Subtitles.
  • Save every .vtt file as UTF-8. Other encodings turn non-English letters into garbage.
  • Right-to-left languages (Arabic, Hebrew, Urdu) display correctly in WebVTT. Test them in more than one browser, and mix directions carefully.
  • Allow for longer text. A translation can be 30% longer, so keep lines short (about 40 characters) and cues readable (visitors need time to read).
  • Keep the pace reasonable: about 15 to 20 characters per second is comfortable for many readers, and fast speech may need shortened wording.
  • Do not rely on machine translation without a human check. Mistakes in captions are very visible.

Common Mistakes to Avoid​

  1. Forgetting the WEBVTT header line.
  2. A comma in timestamps (SRT style), instead of a dot.
  3. No blank line between cues.
  4. A .vtt file that is not UTF-8.
  5. subtitles where captions are needed, so sound information is missing.
  6. No srclang on subtitles, or no label.
  7. More than one default.
  8. Cross-origin tracks without crossorigin and the right headers.
  9. Waiting for cues on a disabled track, which never loads. Use hidden or showing.
  10. Putting <track> before <source>, or outside the media element.
  11. Using innerHTML for cue text.
  12. Captions that lag, overlap each other, or flash too fast to read.

Practice Time​

Try these in your editor, with a short audio or video file:

  1. Write a .vtt file with three cues, and attach it with <track kind="captions" default>.
  2. Add a second track in another language, with a label in that language.
  3. Break the file on purpose (a comma, no WEBVTT), and see what happens.
  4. Print video.textTracks.length and each track's mode.
  5. Set a track to hidden, and print its cues.
  6. Highlight the active cue in a list, using cuechange.
  7. Add ::cue styles, and check the contrast.
  8. Make the transcript list clickable, so each line jumps to that time.
  9. Add a chapters track and build a list of chapter links.
  10. Bonus: host the .vtt file on a different origin, and fix the crossorigin problem.

Quick Recap​

Click a question to check your answer.

1. What does the track element do?

It attaches timed text (captions, subtitles, descriptions, chapters, or metadata) to an audio or video element, using a WebVTT file.

2. What is the difference between captions and subtitles?

Subtitles translate speech for people who can hear. Captions give the speech and also important sounds and speakers, for people who cannot hear the audio.

3. What must the first line of a WebVTT file be?

WEBVTT. Timestamps then use mm:ss.ttt or hh:mm:ss.ttt, with a dot, and cues are separated by blank lines.

4. Which track mode lets a script read cues without showing them?

hidden. In disabled mode, cues may not be loaded at all, and showing also draws them on screen.

5. Why might captions fail on your live site?

The .vtt file is probably on another origin without the right CORS headers, or the media element lacks crossorigin. Check the Content-Type too.

6. How should you put cue text into a page with JavaScript?

With textContent, never innerHTML, so untrusted text cannot add markup.

Key Takeaways​

  • <track> adds timed text to <audio> and <video>, from a WebVTT file.
  • The kinds are subtitles, captions, descriptions, chapters, and metadata. Use captions for accessibility.
  • Always add srclang and label (in its own language), and use only one default.
  • A WebVTT file starts with WEBVTT, uses dots in timestamps, and separates cues with blank lines.
  • Use mode = "hidden" and the cuechange event to build transcripts, chapters, and other features.
  • Cross-origin tracks need crossorigin and the right server headers.
  • Use textContent for cue text, save files as UTF-8, and keep captions accurate and readable.
  • A .vtt file is public, so keep private information out of it.

Next, learn how to write good captions in Captions, or return to the Audio and Video overview.