Command Palette

Search for a command to run...

Hectal
PHASE 5Intermediate ~26 min· topic 4 of 7

Topic 5.4

Captions and Subtitles

In one line

The <track> element attaches timed text to a video: captions for people who can't hear it, subtitles for people who don't speak its language, written in a simple text format called WebVTT.

Think of it like this

Watching a film in a noisy train with the sound off. Captions let you follow every word and know when a phone rings or a door slams. Your friend from another country turns on subtitles in their language instead. Same film, different text, chosen by each viewer.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Captions
Text of everything heard in a video (speech, speakers, sounds), mainly for people who can't hear it.
Subtitles
A translation of the dialogue into another language.
WebVTT
The .vtt text file format for timed text that browsers understand.
Cue
One timed piece of text in a WebVTT file: a start time, an end time and the words.
track
The HTML element that attaches a .vtt file to a video.
kind
The track attribute saying what the text is for: captions, subtitles, descriptions, chapters or metadata.
srclang
The language code of a track, like en or hi.
Track mode
Whether a track is disabled, hidden (loaded but not shown) or showing.
WCAG
Web Content Accessibility Guidelines: the international rules for making websites usable by everyone.

Step by step

01Write a WebVTT file

The first line must be exactly WEBVTT. Each cue is a time range and text, separated from the next cue by a blank line. An optional name above the times (like intro) is the cue's identifier.

captions-en.vttwhole filetext
WEBVTT

intro
00:00:00.500 --> 00:00:03.000
<v Asha>Welcome to the cooking show!

00:00:03.200 --> 00:00:05.000
[pan sizzles]

00:00:05.200 --> 00:00:09.000 line:0
<v Ravi>Today we're making <i>dal</i>.

02Attach it with <track>

The track goes inside the video, after the sources. default makes the English captions appear without the viewer doing anything.

Serve .vtt files with the MIME type text/vtt. Most servers already do.

index.htmlwhole filehtml
<video controls width="640" height="360">
  <source src="show.mp4" type="video/mp4">
  <track src="captions-en.vtt" kind="captions" srclang="en" label="English" default>
</video>

03Add more languages

Each language is another <track>. The player's CC menu lists them by label. Only one starts on.

index.htmlwhole filehtml
<video controls width="640" height="360">
  <source src="show.mp4" type="video/mp4">
  <track src="captions-en.vtt" kind="captions" srclang="en" label="English (CC)" default>
  <track src="subs-hi.vtt" kind="subtitles" srclang="hi" label="हिन्दी">
  <track src="subs-es.vtt" kind="subtitles" srclang="es" label="Español">
  <track src="chapters.vtt" kind="chapters" srclang="en">
</video>

04How the browser shows a cue

While the video plays, the browser compares the current time with each cue in a showing track. Cues whose range includes the current time are active and drawn on the video; the track fires a cuechange event each time the set of active cues changes.

How the browser shows a cuediagram
Rendering diagram…

05Style captions with ::cue

Only a few properties work inside ::cue: colour, background, font settings, text-decoration, text-shadow, outline and a few more. Keep contrast high; viewers can also set their own caption style in their operating system, which should win.

style.csswhole filecss
video::cue {
  background: rgb(0 0 0 / 80%);
  color: white;
  font-size: 1.1em;
}
video::cue(v[voice="Ravi"]) {
  color: gold;
}

Try it yourself

  1. 1

    Turn on the Hindi subtitles

    In the first example, open the player's CC (captions) menu and choose Hindi. Then predict: if you add default to the Hindi track as well, which one shows? Run it and check the console (the browser turns on only the first default track of a group).

  2. 2

    Move a cue to the top

    In the first example's first cue, add line:0 after 00:03.000 (inside the data URL, before %0A). Run it: the caption now appears at the top of the video.

  3. 3

    Break the format on purpose

    In the second example, change 00:00.500 to 00:00,500 (a comma). Predict how many cues load, then run it.

Code & diagrams

Captions on a playing video New tab

The .vtt files are written straight into `src` as data URLs so the example works offline. In real pages, `src` points to a .vtt file. Only the `default` track starts showing.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

captions en English → showing
subtitles hi Hindi → disabled
Reading the cues of a loaded track New tab

`readyState` goes 0 (none) → 1 (loading) → 2 (loaded); 3 means the file failed. Cue settings like `line:0` become properties.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

readyState before load: 0
readyState after load: 2
"intro" 0.5 → 3 | <v Asha>Welcome to the show! | line: auto align: center
"" 3.2 → 5 | [pan sizzles] | line: 0 align: start
Plain text of cue 1: Welcome to the show!
Switching tracks from JavaScript New tab

This is what the player's CC menu does for you. Click the buttons and watch the console.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

en:disabled hi:showing es:disabled

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Forget the WEBVTT header

Delete the WEBVTT line at the top of a .vtt file.

terminal
$ the page shows
── what you'll see ──
No captions. The track's readyState is 3 (error) and its error event fires.

Break #2

Load captions from another domain

Put your .vtt files on a CDN at another domain and point src at them, without crossorigin on the video.

terminal
$ Console
── what you'll see ──
Unsafe attempt to load URL https://cdn.example.com/captions-en.vtt from frame with URL https://www.example.com/. Domains, protocols and ports must match.

Myth vs fact

Myth

Captions and subtitles are the same thing.

Fact

Subtitles translate speech. Captions write down speech and sounds and speakers, for viewers who can't hear. A deaf viewer needs captions even in their own language.

Myth

Auto-generated captions are good enough.

Fact

Automatic captions often get names, numbers and technical words wrong. For accessibility they must be accurate, so review and fix them.

Myth

Captions only help deaf viewers.

Fact

Many people watch on mute (on trains, in offices), language learners rely on them, and the text helps search engines understand the video.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    WebVTT supports NOTE comment blocks, STYLE blocks with ::cue rules, REGION definitions for scrolling text (roll-up captions), and in-cue timestamps (<00:00:02.500>) for karaoke-style highlighting via the :past and :future pseudo-classes.

  • ▸

    A track that is disabled isn't even fetched: the browser loads a track's file only when its mode becomes hidden or showing. That's why cues is null on a disabled track.

  • ▸

    kind="metadata" tracks plus the cuechange event are a neat way to sync page content with a video (show a quiz at 2:30, highlight a step of a recipe), without a timer loop.

  • ▸

    Browsers let users override caption appearance in OS settings (for example Windows: Settings → Accessibility → Captions; macOS: Accessibility → Captions). Don't fight it with heavy ::cue styles.

Remember this

  1. 1

    Captions write down everything you'd hear: the dialogue, who is speaking, and important sounds like [door creaks] or [music stops]. They exist mainly for people who are deaf or hard of hearing. Subtitles translate the dialogue for people who can hear but don't understand the language. Accessibility rules (WCAG, the Web Content Accessibility Guidelines, level A) require captions for pre-recorded video with speech. Captions also help everyone watching on mute, learners of the language, and search engines.

  2. 2

    WebVTT (Web Video Text Tracks, file extension .vtt) is the text format browsers read. The file starts with the word WEBVTT on the first line, then a blank line, then cues. A cue is one piece of text with a start and end time: a line like 00:00:01.000 --> 00:00:04.000, then the text on the next line(s), then a blank line. Hours are optional (01:05.500 is fine). Milliseconds use a dot, not a comma; a comma is the older SRT format and doesn't work.

  3. 3

    <track> goes inside <video> (or <audio>, though browsers don't show text for audio), after any <source> elements: <track src="captions-en.vtt" kind="captions" srclang="en" label="English">. **src** is the .vtt file. **srclang** is the language code (like lang, Topic 0.4), required for subtitles. **label** is the name shown in the player's captions menu.

  4. 4

    **kind** says what the track is for: subtitles (the default if you leave it out), captions, descriptions (text descriptions of what is on screen, meant to be read aloud by assistive technology; browser support is limited), chapters (titles of sections for navigation) or metadata (data for your own JavaScript, never displayed).

  5. 5

    **default turns a track on at the start. Without it, all tracks start disabled** and the viewer must choose one from the player menu (the CC button). Use default on at most one track of each group. Each track has a mode you can read and change from JavaScript: disabled (not loaded, not shown), hidden (loaded, cues fire events, not shown) or showing (shown on the video).

  6. 6

    Cues can carry settings after the times, such as line:0 (put the text at the top, useful when the bottom has on-screen text) and align:start, and simple tags in the text: <v Asha> names the speaker, <i> and <b> style words. You can style cues with the CSS ::cue pseudo-element, for example video::cue { background: rgb(0 0 0 / 80%); }.

Explain it without notes

01

What's the difference between captions and subtitles, and what kind would you use for each?

02

Describe the structure of a WebVTT file and one common mistake when converting from SRT.

03

What do the three text track modes mean, and when is a track's file downloaded?

04

Why are captions required for accessibility, and who else benefits?

Practice

01

Write a WebVTT file with two cues: "Hi, I'm Asha." spoken by Asha from 0 to 2 seconds, and the sound "[phone rings]" from 2.5 to 4 seconds.

02

Attach English captions (on by default) and French subtitles to a video talk.mp4.

03

Write JavaScript that logs "Cue changed" plus the active cue's text each time the captions change on <video id="v">.

Trade-offs

  • ↔

    Burned-in (open) captions are part of the video pixels: they always show and look identical everywhere, but can't be turned off, restyled, translated or read by search engines. <track> (closed) captions are flexible and accessible but depend on the player.

Done when you can

  • I can explain captions vs subtitles.

  • I can write a valid WebVTT file with times, speakers and sounds.

  • I can attach tracks with kind, srclang, label and default.

  • I know what the disabled, hidden and showing modes do.

  • I can style captions with ::cue.