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
.vtttext 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
.vttfile to a video. - kind
- The
trackattribute saying what the text is for: captions, subtitles, descriptions, chapters or metadata. - srclang
- The language code of a track, like
enorhi. - Track mode
- Whether a track is
disabled,hidden(loaded but not shown) orshowing. - 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.
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.
<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.
<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.
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.
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
Turn on the Hindi subtitles
In the first example, open the player's CC (captions) menu and choose Hindi. Then predict: if you add
defaultto the Hindi track as well, which one shows? Run it and check the console (the browser turns on only the firstdefaulttrack of a group). - 2
Move a cue to the top
In the first example's first cue, add
line:0after00:03.000(inside the data URL, before%0A). Run it: the caption now appears at the top of the video. - 3
Break the format on purpose
In the second example, change
00:00.500to00:00,500(a comma). Predict how many cues load, then run it.
Code & diagrams
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
Console
Nothing logged yet.Expected console output
captions en English → showing
subtitles hi Hindi → disabled`readyState` goes 0 (none) → 1 (loading) → 2 (loaded); 3 means the file failed. Cue settings like `line:0` become properties.
Preview
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!This is what the player's CC menu does for you. Click the buttons and watch the console.
Preview
Console
Nothing logged yet.Expected console output
en:disabled hi:showing es:disabledBreak 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.
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.
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
NOTEcomment blocks,STYLEblocks with::cuerules,REGIONdefinitions for scrolling text (roll-up captions), and in-cue timestamps (<00:00:02.500>) for karaoke-style highlighting via the:pastand:futurepseudo-classes. - ▸
A track that is
disabledisn't even fetched: the browser loads a track's file only when its mode becomeshiddenorshowing. That's whycuesisnullon a disabled track. - ▸
kind="metadata"tracks plus thecuechangeevent 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
::cuestyles.
Remember this
- 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
WebVTT (Web Video Text Tracks, file extension
.vtt) is the text format browsers read. The file starts with the wordWEBVTTon the first line, then a blank line, then cues. A cue is one piece of text with a start and end time: a line like00:00:01.000 --> 00:00:04.000, then the text on the next line(s), then a blank line. Hours are optional (01:05.500is fine). Milliseconds use a dot, not a comma; a comma is the older SRT format and doesn't work. - 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.vttfile. **srclang** is the language code (likelang, Topic 0.4), required for subtitles. **label** is the name shown in the player's captions menu. - 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) ormetadata(data for your own JavaScript, never displayed). - 5
**
defaultturns 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). Usedefaulton 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) orshowing(shown on the video). - 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) andalign:start, and simple tags in the text:<v Asha>names the speaker,<i>and<b>style words. You can style cues with the CSS::cuepseudo-element, for examplevideo::cue { background: rgb(0 0 0 / 80%); }.
Explain it without notes
What's the difference between captions and subtitles, and what kind would you use for each?
Describe the structure of a WebVTT file and one common mistake when converting from SRT.
What do the three text track modes mean, and when is a track's file downloaded?
Why are captions required for accessibility, and who else benefits?
Practice
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.
Attach English captions (on by default) and French subtitles to a video talk.mp4.
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,labelanddefault.I know what the
disabled,hiddenandshowingmodes do.I can style captions with
::cue.