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, andmetadata - The attributes
src,kind,srclang,label, anddefault - The basics of a WebVTT file
- Reading and reacting to cues with JavaScript
- Cross-origin rules, security, and common mistakes
The Basics
- HTML Code
- Browser Output
<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>
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 | Shown? | Why |
|---|---|---|
| English captions | Yes, from the start | It has default |
| Hindi subtitles | Only if chosen | Listed in the player's captions menu |
<track> is a void element (no closing tag), and it goes inside <audio> or <video>, after any <source> elements.
The Five Kinds
kind | What it holds | Shown by the browser? |
|---|---|---|
subtitles | A translation of the speech, for people who can hear but do not know the language | Yes, as text over the video |
captions | The speech plus important sounds ("[door slams]", "[music]"), for people who cannot hear | Yes, as text over the video |
descriptions | Text describing what is on screen, for people who cannot see it | Not drawn: meant to be read out by a screen reader or a script |
chapters | Titles for sections, for navigation | Depends on the browser. Often used by your own script |
metadata | Data 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
| Attribute | What it does | Notes |
|---|---|---|
src | The address of the WebVTT file | Required |
kind | The kind of text | See the table above |
srclang | The language of the text, as a language code (en, hi, es, ar) | Required for subtitles |
label | The name shown in the player's menu | Write it in its own language ("Español", "हिन्दी") |
default | Turns this track on unless the visitor chose another | Only 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.vttandlesson.hi.vttkeeps things tidy.
A WebVTT File in 60 Seconds
WebVTT (.vtt) is a plain text format. A tiny file looks like this:
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:
- The first line is
WEBVTT. - Each cue is a block, separated from the next by a blank line.
- An optional identifier line may come first (like
intro). - The timing line is
start --> end. Usemm:ss.tttorhh:mm:ss.ttt, with a dot before the milliseconds (not a comma, which is SRT format). - One or more lines of text follow. A cue can have several lines.
- Text may use a few tags such as
<b>,<i>,<u>, and<c.name>, and the voice tag<v Asha>. - 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.
- HTML Code
- Browser Output
// 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);
}
});
No problems found. 3 cues read.
| Start | End | Text |
|---|---|---|
| 2.0 s | 4.0 s | A wobble: D, F, A, F |
| 4.0 s | 6.0 s | Climbing: C, E, G, high C |
| 6.0 s | 8.0 s | Coming home: G, E, D, C |
This is a simplified checker for learning. Real browsers are more forgiving about some details, and stricter about others.
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.
- HTML Code
- Browser Output
<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>
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");
}
});
Preparing the sound and its track…
Track mode: not loaded
Waiting for the cues to load…
This is a real track element with a WebVTT file made in memory. The page reads the cues from track.cues, and highlights the active one on the cuechange event. Press play, or click a time.
Track Modes
Every track has a mode, which you can read and set from script:
| Mode | Cues loaded? | Shown on screen? | Typical use |
|---|---|---|---|
disabled | No | No | Off. The file may not even be downloaded |
hidden | Yes | No | Your script reads the cues, and cuechange fires |
showing | Yes | Yes | The 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
lineandpositionsettings 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
captionsfor anything with speech or important sound, notsubtitles. Captions include sound effects and speaker names, and subtitles do not. - Always add
labelandsrclang, 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
descriptionsfor visual-only information, and make sure your script exposes the text to assistive technology (for example, in anaria-liveregion), because browsers do not read it aloud for you. - Check the captions menu with the keyboard.
Security and Privacy Notes
- A
.vttfile is public. Anyone can fetch it. Do not put private notes inNOTEcomments or in hidden cues. - Treat cue text as text, not HTML. When your script copies
cue.textinto the page, usetextContent, neverinnerHTML. 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
trackelements, each withsrclangand alabelin its own language. See Subtitles. - Save every
.vttfile 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
- Forgetting the
WEBVTTheader line. - A comma in timestamps (SRT style), instead of a dot.
- No blank line between cues.
- A
.vttfile that is not UTF-8. subtitleswherecaptionsare needed, so sound information is missing.- No
srclangon subtitles, or nolabel. - More than one
default. - Cross-origin tracks without
crossoriginand the right headers. - Waiting for
cueson adisabledtrack, which never loads. Usehiddenorshowing. - Putting
<track>before<source>, or outside the media element. - Using
innerHTMLfor cue text. - 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:
- Write a
.vttfile with three cues, and attach it with<track kind="captions" default>. - Add a second track in another language, with a label in that language.
- Break the file on purpose (a comma, no
WEBVTT), and see what happens. - Print
video.textTracks.lengthand each track'smode. - Set a track to
hidden, and print its cues. - Highlight the active cue in a list, using
cuechange. - Add
::cuestyles, and check the contrast. - Make the transcript list clickable, so each line jumps to that time.
- Add a
chapterstrack and build a list of chapter links. - Bonus: host the
.vttfile on a different origin, and fix thecrossoriginproblem.
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, andmetadata. Usecaptionsfor accessibility. - Always add
srclangandlabel(in its own language), and use only onedefault. - A WebVTT file starts with
WEBVTT, uses dots in timestamps, and separates cues with blank lines. - Use
mode = "hidden"and thecuechangeevent to build transcripts, chapters, and other features. - Cross-origin tracks need
crossoriginand the right server headers. - Use
textContentfor cue text, save files as UTF-8, and keep captions accurate and readable. - A
.vttfile 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.