SRT vs VTT vs TXT: which subtitle format should you use?
If you've exported a transcript or subtitle file, you've probably had to pick a format and weren't totally sure why it mattered. The short answer: they're not interchangeable, and picking the wrong one means re-exporting later. Here's what each format actually contains, the syntax differences that trip people up, and which one fits your use case.
What each format actually is
SRT (SubRip Text) is the oldest and most widely supported subtitle format. It's a plain text file made of numbered cues, each with a start and end time and a line of text. Almost every video editor, media player, and upload form that accepts subtitles accepts SRT.
WebVTT (Web Video Text Tracks) is the subtitle format built for the web.
It's the format the HTML5 <track> element expects, and it extends SRT's
idea with a required header, optional styling, and cue positioning.
TXT is just the words — no timestamps, no cue numbers, no structure. Good for reading, searching, or feeding to a summarizer; useless for syncing text to video.
JSON isn't a subtitle format in the broadcast sense, but it's the shape most programmatic work actually wants: an array of segments, each with a start time, end time, and text, that you can loop over in code without writing a parser.
The real syntax differences
This is where people get it wrong, because SRT and VTT look almost identical at a glance. They're not.
SRT
1
00:00:00,000 --> 00:00:02,500
Hello there.
2
00:00:02,500 --> 00:00:05,000
Welcome to the show.Three things define an SRT cue: a sequential number, a timestamp line using
-->, and the text. Note the comma as the decimal separator in the
timestamps (00,000, not 00.000) — that comma is not a typo, it's part of
the spec, and it's the single most common thing that breaks when people
hand-edit SRT files.
WebVTT
WEBVTT
00:00:00.000 --> 00:00:02.500
Hello there.
00:00:02.500 --> 00:00:05.000
Welcome to the show.Two differences that matter: the file must start with a WEBVTT header line
(a bare VTT file without it isn't valid), and the timestamp separator is a
period, not a comma. Cue numbers are optional in VTT — you can add an
identifier before a cue if you want to reference it, but the player doesn't
require one.
VTT also supports things SRT has no concept of: inline styling and basic
markup like <b>, <i>, and <c> (voice/class tags for styling specific
speakers), positioning cues on screen with line, position, and align,
and a STYLE block for CSS-like rules that target cues with ::cue. None of
that exists in SRT — it's plain text with no styling hooks at all.
TXT
Hello there. Welcome to the show.No timestamps, no cue boundaries, just the transcript as prose or line by line. Whatever structure the source had (segments, speakers) is gone unless you keep it as formatting.
JSON
[
{ "start": 0.0, "end": 2.5, "text": "Hello there." },
{ "start": 2.5, "end": 5.0, "text": "Welcome to the show." }
]This is the shape you actually want when you're writing code against a
transcript — no regex to pull timestamps out of --> lines, just array
access.
When to use which
| Use case | Best format | Why |
|---|---|---|
| Uploading captions to YouTube | SRT | Universally accepted, simplest to author |
HTML5 <video> with <track> | VTT | It's the format the spec requires |
| Editing in Premiere, Final Cut, DaVinci | SRT | Broadest editor support |
| Styled or positioned captions on the web | VTT | Only VTT supports STYLE, positioning, and voice tags |
| Feeding a transcript to an LLM | TXT or JSON | No timing syntax to confuse the model; JSON if you need offsets back |
| Search indexing / full-text search | TXT | You're indexing words, not cue timing |
| Programmatic processing (stitching, diffing, analysis) | JSON | Structured access without parsing timestamp syntax |
If you're not sure and the destination is "a video player," start with SRT —
it's the safest default because almost nothing rejects it. Reach for VTT
specifically when you're building for the web and need HTML5 <track>
compatibility or styling.
Converting between them
Because SRT and VTT share the same structure — sequential cues with start
and end times — converting between them is mostly a find-and-replace: add the
WEBVTT header, and swap commas for periods in the timestamps. ffmpeg
does this for you in one command:
ffmpeg -i captions.srt captions.vttGoing the other way (VTT to SRT) works the same way, though anything VTT-only — styling, positioning, voice tags — gets dropped, since SRT has nowhere to put it:
ffmpeg -i captions.vtt captions.srtConverting to TXT is lossy on purpose — you're stripping timing to keep just the words — so it's usually easiest to do in whatever tool produced the original file rather than round-tripping through ffmpeg.
Getting all four without picking one upfront
If you're pulling a transcript from a video rather than authoring subtitles by hand, you don't have to commit to one format ahead of time. transcript.land can export the same transcript as TXT, SRT, VTT, Markdown, or JSON — for YouTube, TikTok, X, Instagram, Bilibili, Facebook, and RedNote, or a direct audio URL — and falls back to AI transcription automatically when a video has no captions to read. From the CLI:
brew install ziqorg/tap/transcript
transcript login
transcript get "https://youtu.be/VIDEO_ID" -o vttSwap -o for srt, txt, md, or json depending on where the output is
going. There's a free tier if you just need to check the output shape before
deciding.
Summary
- SRT uses comma decimal separators and numbered cues; VTT uses a
WEBVTTheader and period separators, and adds styling and positioning on top. - TXT drops timing entirely; JSON keeps it in a structure that's easy to code against.
- Use SRT for editors and upload forms, VTT for the web, TXT or JSON for
anything programmatic — and
ffmpeg -i file.srt file.vttconverts between the first two in one line.