The basics
What is a VTT file?
A VTT file holds timed text for a video or audio file: captions, subtitles, chapter titles, or data for scripts. It's the format web browsers use. A video on a web page gets its captions by pointing a <track> element at a .vtt file, and the browser draws the text over the picture at the right moments. Video platforms and streaming formats such as HLS use it too.
WebVTT grew out of SRT, the most common subtitle format. When HTML5 video needed a caption format around 2010, the WHATWG wrote down an extended SRT, first called WebSRT and soon renamed WebVTT. It keeps SRT's simple layout and adds what SRT lacks: a header that identifies the file, a fixed encoding (UTF-8), comments, CSS styling, cue positioning and kinds of track beyond subtitles.
The specification is maintained by the W3C Timed Text Working Group. It is a Candidate Recommendation (the latest draft is from May 2026), not yet a final Recommendation, but every current browser has supported the core format since about 2015.
Anatomy
What's inside a VTT file
A WebVTT file is a header followed by blocks separated by blank lines. Most blocks are cues: a time range and the text to show. Optional blocks hold comments, CSS and regions. Here is a small file with one of each, part by part.
WEBVTT
STYLE
::cue(.loud) { color: yellow; }
NOTE Comments start with NOTE and are never shown.
intro
00:00:01.000 --> 00:00:03.500 line:10%
Hello, and welcome.
00:04.000 --> 00:06.250 align:start
<v Ada Example>Hi, I'm <c.loud>Ada</c>.
00:01:02.040 --> 00:01:05.000
[Door slams]
- 1
WEBVTTThe header
Required, and the only way to recognise the format: a file that doesn't start with
WEBVTT(after an optional byte order mark) is rejected outright. Text may follow after a space, such as a description of the file. - 2
(blank line)Block separator
Blank lines separate the header and every block. That's why a cue's text can't contain an empty line: it would end the cue.
- 3
STYLEA style block
CSS for the cues, using the
::cuepseudo-element. Style blocks must come before the first cue. They can change colour, background and fonts but not position, which is the job of cue settings. - 4
NOTE Comments start with NOTE…A comment
Ignored by players. Useful for credits, translator notes or, as in our samples, explaining the file. A comment can't contain the
-->arrow. - 5
introA cue identifier
An optional name for the cue on the line before its times. Scripts can find a cue by it, and it shows up as the cue's
idin the browser's TextTrack API. - 6
00:00:01.000 --> 00:00:03.500The timing line
Start and end, with a full stop before the milliseconds (SRT uses a comma). Each cue must start no earlier than the one before it.
- 7
line:10%Cue settings
Placement after the times:
line(vertical position),positionandsize(horizontal),align,verticalfor vertical text, andregion. Here the cue moves near the top of the picture. - 8
00:04.000A short timestamp
Hours are optional when they're zero, so
00:04.000is four seconds. Minutes and seconds always have two digits and milliseconds three. - 9
<v Ada Example>A voice span
Names the speaker. Players can style each voice differently, and assistive technology can use the name.
- 10
<c.loud>Ada</c>A class span
Like an HTML
<span class>, styled by the::cue(.loud)rule in the STYLE block.<i>,<b>,<u>,<ruby>and<lang>work too. A literal<or&must be written as<or&.
In the browser
How a web page loads a VTT file
<video controls src="talk.mp4">
<track kind="captions" src="talk.en.vtt"
srclang="en" label="English" default>
<track kind="subtitles" src="talk.nl.vtt"
srclang="nl" label="Nederlands">
<track kind="chapters" src="talk-chapters.vtt" srclang="en">
</video>kindsays what the track is for (see the next section). Leave it out and it meanssubtitles.srclangis the language, as a tag likeenornl. It's required for subtitles.labelis the name in the player's captions menu.defaultturns the track on when the page loads, unless the viewer's settings prefer another. Without it, captions stay off until the viewer picks them, and the browser doesn't even download the file.srcmust be on the same origin as the page, unless the<video>has acrossoriginattribute and the server that hosts the.vttsends anAccess-Control-Allow-Originheader (CORS). A track from another site without both simply fails to load, with a console error and no captions.
Scripts can read the same cues through the TextTrack API: video.textTracks[0].cues, and a cuechange event whenever the active cues change. A track whose mode is hidden is loaded and readable but not drawn, which is how chapter menus and metadata tracks work.
Serve it as text/vtt
Give .vtt files the Content-Type: text/vtt header. Most web servers know the type; if yours sends application/octet-stream, add it to the server's MIME settings.
Accessibility
Kinds of track: captions, subtitles, descriptions, chapters, metadata
| `kind` | What it holds | Shown? |
|---|---|---|
subtitles | Dialogue, transcribed or translated, for viewers who can hear the sound but don't understand the language | Yes, when selected |
captions | Dialogue plus sound effects, music and who is speaking, for viewers who can't hear the audio | Yes, when selected |
descriptions | Text descriptions of what's on screen, meant to be read aloud by a screen reader for viewers who can't see the video | Not drawn |
chapters | Chapter titles, one cue per chapter, for navigation. Chapter cues must not overlap | Used by the player's chapter menu where it has one |
metadata | Anything a script needs at a given time: JSON, ad markers, slide changes. Also used for invalid kind values | Never |
The difference between captions and subtitles matters for accessibility. Accessibility rules such as WCAG ask for captions on prerecorded video with sound, and captions have to carry the sounds, not just the words: [door slams], [music stops], speaker names when it isn't obvious who's talking. Our featured sample is marked as captions and describes the test tone and beep.
Layout
Cue settings, STYLE blocks and ::cue
| Setting | Values | What it does |
|---|---|---|
line | A line number (negative counts from the bottom) or a percentage, optionally with ,start, ,center or ,end | The vertical position. The default puts cues at the bottom. |
position | A percentage, optionally with ,line-left, ,center or ,line-right | The horizontal position of the cue box. |
size | A percentage | The width of the cue box. |
align | start, center, end, left, right | Text alignment inside the box. |
vertical | rl or lr | Vertical text, for Japanese and similar scripts. |
region | A region id | Puts the cue into a REGION, a box where cues scroll up like live captions. |
Styling is CSS. Inside the file, a STYLE block before the first cue holds rules for ::cue (all text), ::cue(.class), ::cue(v[voice="Name"]) and so on. The page's own stylesheet can do the same with video::cue. Viewers' caption settings in the operating system can override both, which is intended: someone who needs large yellow text should get it.
Support varies
Browsers implement the core well and the newer parts unevenly. In our tests in Chromium, percentages for line, position, size, align, STYLE blocks, classes and voices all worked, but the alignment suffix in line:74%,end was ignored and so were REGION blocks. Test the settings you rely on in each browser you support.
Related formats
VTT vs SRT and other caption formats
| WebVTT (`.vtt`) | SRT (`.srt`) | TTML (`.ttml`, `.dfxp`) | |
|---|---|---|---|
| Syntax | Plain text | Plain text | XML |
| Header | WEBVTT required | None | XML root element |
| Timestamp | 00:00:01.000 | 00:00:01,000 | Several forms, such as 00:00:01.000 or 1s |
| Encoding | UTF-8 only | Not defined | XML rules, usually UTF-8 |
| Comments | NOTE blocks | None | XML comments |
| Styling and position | CSS and cue settings | Unofficial tags only | Detailed, built in |
| Plays in browsers | Yes | No | No |
| Typical use | Web video, HLS streaming | Downloads, players, TVs | Broadcast and streaming services |
SRT to VTT: add a WEBVTT line and a blank line at the top, change the comma in every time to a full stop, and save as UTF-8. Or ffmpeg -i subs.srt subs.vtt. Cue numbers can stay: VTT reads them as identifiers. VTT to SRT: ffmpeg -i subs.vtt subs.srt keeps text and times; styling, positions and comments are dropped.
Troubleshooting
Why don't my captions show?
| Symptom | Cause | Fix |
|---|---|---|
| No captions, and a console error about an unsafe or blocked URL | The .vtt is on another origin, the page runs in a sandboxed frame, or the page was opened from file:// | Serve page and track from the same site, or add crossorigin to the video and CORS headers to the track's server. |
| The track loads but nothing appears | No default attribute and the viewer hasn't switched captions on, or the kind is metadata | Add default, or set track.mode = "showing" from a script. |
| The whole file is ignored | The first line isn't WEBVTT, often because an SRT file was renamed | Add the header and fix the timestamps. |
| Some cues are missing | A comma in their timestamps (copied from SRT), a cue that starts before the previous one, or a blank line inside the text | Check the timing lines; a validator or parser error log shows the line. |
| Styling doesn't apply | The STYLE block comes after a cue, or the browser doesn't support that selector | Move STYLE blocks above the first cue; style from the page with video::cue as a fallback. |
| Captions cover text in the video | Default placement at the bottom | Move the cues with line, as our featured sample does to avoid the label in the sample video. |
Tools beyond the browser are often older than the newer blocks. FFmpeg 6.1, for example, read no cues at all from our sample-styled.vtt, because of its STYLE block; the files without STYLE convert fine.
How to
How to open, edit and convert a VTT file
- Read or fix it: any text editor. Keep the
WEBVTTline, the blank lines between blocks and UTF-8 encoding. - Watch with it: VLC and mpv open
.vttfiles through their subtitle menu, and usually pick up one with the same name as the video. - Edit timing: Subtitle Edit and Aegisub open and save WebVTT, and most caption services and video editors export it.
- Put it on a web page: add a
<track>to your<video>, with the.vtton the same site as the page. - Upload to YouTube: YouTube accepts WebVTT with positioning, and styling limited to
<b>,<i>and<u>. - Convert:
ffmpeg -i subs.vtt subs.srtfor players that only take SRT, or the reverse for the web.
A VTT file is text and can't run code. The cue text is not HTML: browsers only recognise the WebVTT tags, so <script> in a cue is never executed.
Free downloads
Sample VTT files to download
Free WebVTT samples timed to our 10-second sample MP4, plus a page that plays the video with all of them. Each track loaded and produced the expected active cues in Chromium.
Loading…What's inside sample-captions.vtt
The file explains itself in numbered NOTE comments, which players ignore. Open it in a text editor next to these notes.
Note 1: the header
WEBVTT, then a description after a space. Players ignore the description; they only check the first word.Note 2: a cue
Each cue has an identifier (
intro,tone,bars…), a timing line with full stops before the milliseconds, and up to two lines of text.Note 3: placement
Every cue has
line:60%, which lifts it above the label band at the bottom of the sample video. Remove it and the captions cover the label.Note 4: captions, not subtitles
Sounds are described in brackets,
[Steady tone…]and[Beep], so the track is meant forkind="captions".Note 5: a short timestamp
00:04.000 --> 00:05.900leaves out the hours.Note 6: a tag
<i>every</i>in the fourth cue: the basic tags work as in SRT.Note 7: the end
The last cue ends at exactly
00:00:10.000, the length of the video.
All sample VTT files
English captions for the sample MP4
sample-captions.vtt · 1.5 KB (1,565 bytes)
Six identified cues with
line:60%placement, sound descriptions and seven numbered NOTE comments that explain the format. UTF-8, LF.- Testing `<track>` loading and caption rendering
- Teaching the parts of a WebVTT file
Cue settings and styling
sample-styled.vtt · 1.0 KB (1,074 bytes)
A STYLE block with
::cue, a class and two voice selectors; cues placed withline,position,sizeandalign;<v>voice spans,<c.yellow>,<b>,<i>,<u>, ruby inside a<lang ja>span, and karaoke-style timestamp tags.- Checking which styling and settings a player supports
- Testing parsers against the newer blocks
Chapters track
sample-chapters.vtt · 432 bytes
Three chapters covering the 10 seconds with no gaps or overlaps, for
kind="chapters".- Chapter menus and seek bars
- TextTrack API tests
Metadata track with JSON
sample-metadata.vtt · 1.4 KB (1,473 bytes)
Ten 0.1-second cues, one per beep in the sample video, each carrying a line of JSON (
{"event":"beep","second":3,"frame":90,…}) forkind="metadata".- Testing `cuechange` events and timed scripts
- Checking audio and video sync against the beeps
Player page with all four tracks
sample-player.html · 6.4 KB (6,554 bytes)
Plays
/samples/mp4/sample-720p-h264-aac.mp4(with the WebM version as a fallback) with the four tracks above, loaded by relative URL, and lists the tracks, the chapters, the active caption cue and the latest metadata cue. In a sandboxed preview frame the browser treats the tracks as cross-origin and blocks them, and the page says so: open it in its own tab.- Seeing captions, chapters and metadata work together
- A starting point for your own captioned video page
Ways to test with them
- Track loading: open
sample-player.htmlin its own tab. All four tracks should report as loaded (the styling demo only once you switch it on in the captions menu) and the first caption should appear at once. - CORS: host the page on one origin and the
.vttfiles on another. Withoutcrossoriginand anAccess-Control-Allow-Originheader, the tracks should fail; with both, they should load. - Placement and styling: switch to the styling demo track and compare browsers. Yellow class text, coloured voices and the left-aligned 40% box should appear where the cue settings say.
- Parsers and converters: feed
sample-styled.vttto your tool. A parser that doesn't know STYLE blocks may drop every cue, as FFmpeg 6.1 did.
Free to use for testing, teaching and demos, commercially or not, with no attribution needed. A link back to this page is always appreciated.
Put it online
Have a VTT file? Give it a link.
Drop a VTT file on 99helpers and it gets its own web address, where it opens as plain text. Publish it next to your page and video, and a <track> on that page can load it; tracks loaded from another site need CORS headers.
Questions
VTT files, frequently asked
What is a VTT file?
A WebVTT file: plain text with timed cues for a video, such as captions, subtitles or chapter titles. Web browsers show it over a video through the <track> element.
How do I open a VTT file?
Any text editor shows its contents. To watch it with a video, add it through VLC's subtitle menu, or load it on a web page with <track>.
What's the difference between VTT and SRT?
WebVTT is based on SRT. It needs a WEBVTT header, uses a full stop instead of a comma in times, must be UTF-8, and adds comments, styling and positioning. Browsers play VTT, not SRT.
How do I convert SRT to VTT?
Run ffmpeg -i subs.srt subs.vtt, or do it by hand: add WEBVTT and a blank line at the top, replace the commas in the times with full stops, and save as UTF-8.
Why won't my VTT captions load?
Usually because the file is on a different origin from the page (it needs crossorigin and CORS headers), the page was opened from file://, or the file doesn't start with WEBVTT.
What's the difference between captions and subtitles?
Subtitles carry the dialogue, often translated, for people who can hear. Captions also describe sounds and speakers, for people who can't. In <track> they're kind="subtitles" and kind="captions".
Can I style WebVTT captions?
Yes, with CSS: a STYLE block in the file or video::cue rules on the page, plus cue settings for position. Support for the newer features varies between browsers, and viewers' own caption settings can override you.
What MIME type should a VTT file have?
text/vtt. Set it on your server if it sends .vtt files as something else.
Related formats
Other file types explained
Keep going
Related
Sources
- W3C: WebVTT, The Web Video Text Tracks Format
- MDN: Web Video Text Tracks Format (WebVTT)
- MDN: the <track> element
- Library of Congress: WebVTT format description
- YouTube Help: Supported subtitle and closed caption files
Last reviewed 27 October 2026.