File types · Explainer

The VTT file (.vtt)

A VTT file is a WebVTT text track: timed captions, subtitles, chapter titles or data that a web page plays along with a video. This guide explains what's inside one, how browsers load it, the quirks worth knowing, and has free sample VTT files to download for testing.

At a glance

Full name
WebVTT: Web Video Text Tracks Format
Extensions
.vtt
MIME type
text/vtt
Kind
Plain text: timed cues, optionally with comments, CSS styling and position settings
Encoding
UTF-8, which the specification requires
Standard
W3C WebVTT, a Candidate Recommendation (latest draft May 2026)
First released
2010, in WHATWG drafts for HTML video, based on SRT (it was first called WebSRT)
Timestamp
HH:MM:SS.mmm or MM:SS.mmm, with a full stop before the milliseconds
Opens with
Web browsers through the <track> element, video players, any text editor
Magic number
57 45 42 56 54 54 (WEBVTT) at the start, optionally after a UTF-8 BOM

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.

captions.vtt
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. 1WEBVTT

    The 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. 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. 3STYLE

    A style block

    CSS for the cues, using the ::cue pseudo-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. 4NOTE 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. 5intro

    A 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 id in the browser's TextTrack API.

  6. 600:00:01.000 --> 00:00:03.500

    The 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. 7line:10%

    Cue settings

    Placement after the times: line (vertical position), position and size (horizontal), align, vertical for vertical text, and region. Here the cue moves near the top of the picture.

  8. 800:04.000

    A short timestamp

    Hours are optional when they're zero, so 00:04.000 is four seconds. Minutes and seconds always have two digits and milliseconds three.

  9. 9<v Ada Example>

    A voice span

    Names the speaker. Players can style each voice differently, and assistive technology can use the name.

  10. 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 &lt; or &amp;.

In the browser

How a web page loads a VTT file

page.html
<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>
  • kind says what the track is for (see the next section). Leave it out and it means subtitles.
  • srclang is the language, as a tag like en or nl. It's required for subtitles.
  • label is the name in the player's captions menu.
  • default turns 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.
  • src must be on the same origin as the page, unless the <video> has a crossorigin attribute and the server that hosts the .vtt sends an Access-Control-Allow-Origin header (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 holdsShown?
subtitlesDialogue, transcribed or translated, for viewers who can hear the sound but don't understand the languageYes, when selected
captionsDialogue plus sound effects, music and who is speaking, for viewers who can't hear the audioYes, when selected
descriptionsText descriptions of what's on screen, meant to be read aloud by a screen reader for viewers who can't see the videoNot drawn
chaptersChapter titles, one cue per chapter, for navigation. Chapter cues must not overlapUsed by the player's chapter menu where it has one
metadataAnything a script needs at a given time: JSON, ad markers, slide changes. Also used for invalid kind valuesNever

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

SettingValuesWhat it does
lineA line number (negative counts from the bottom) or a percentage, optionally with ,start, ,center or ,endThe vertical position. The default puts cues at the bottom.
positionA percentage, optionally with ,line-left, ,center or ,line-rightThe horizontal position of the cue box.
sizeA percentageThe width of the cue box.
alignstart, center, end, left, rightText alignment inside the box.
verticalrl or lrVertical text, for Japanese and similar scripts.
regionA region idPuts 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`)
SyntaxPlain textPlain textXML
HeaderWEBVTT requiredNoneXML root element
Timestamp00:00:01.00000:00:01,000Several forms, such as 00:00:01.000 or 1s
EncodingUTF-8 onlyNot definedXML rules, usually UTF-8
CommentsNOTE blocksNoneXML comments
Styling and positionCSS and cue settingsUnofficial tags onlyDetailed, built in
Plays in browsersYesNoNo
Typical useWeb video, HLS streamingDownloads, players, TVsBroadcast 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?

SymptomCauseFix
No captions, and a console error about an unsafe or blocked URLThe .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 appearsNo default attribute and the viewer hasn't switched captions on, or the kind is metadataAdd default, or set track.mode = "showing" from a script.
The whole file is ignoredThe first line isn't WEBVTT, often because an SRT file was renamedAdd the header and fix the timestamps.
Some cues are missingA comma in their timestamps (copied from SRT), a cue that starts before the previous one, or a blank line inside the textCheck the timing lines; a validator or parser error log shows the line.
Styling doesn't applyThe STYLE block comes after a cue, or the browser doesn't support that selectorMove STYLE blocks above the first cue; style from the page with video::cue as a fallback.
Captions cover text in the videoDefault placement at the bottomMove 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 WEBVTT line, the blank lines between blocks and UTF-8 encoding.
  • Watch with it: VLC and mpv open .vtt files 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 .vtt on 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.srt for 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.

sample-captions.vtt · 1.5 KBDownloadOpen
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.

  1. Note 1: the header

    WEBVTT, then a description after a space. Players ignore the description; they only check the first word.

  2. 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.

  3. 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.

  4. Note 4: captions, not subtitles

    Sounds are described in brackets, [Steady tone…] and [Beep], so the track is meant for kind="captions".

  5. Note 5: a short timestamp

    00:04.000 --> 00:05.900 leaves out the hours.

  6. Note 6: a tag

    <i>every</i> in the fourth cue: the basic tags work as in SRT.

  7. 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 with line, position, size and align; <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,…}) for kind="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.html in 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 .vtt files on another. Without crossorigin and an Access-Control-Allow-Origin header, 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.vtt to 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

Last reviewed 27 October 2026.