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

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

VTT
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

TEXT
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

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 caseBest formatWhy
Uploading captions to YouTubeSRTUniversally accepted, simplest to author
HTML5 <video> with <track>VTTIt's the format the spec requires
Editing in Premiere, Final Cut, DaVinciSRTBroadest editor support
Styled or positioned captions on the webVTTOnly VTT supports STYLE, positioning, and voice tags
Feeding a transcript to an LLMTXT or JSONNo timing syntax to confuse the model; JSON if you need offsets back
Search indexing / full-text searchTXTYou're indexing words, not cue timing
Programmatic processing (stitching, diffing, analysis)JSONStructured 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:

Shell
ffmpeg -i captions.srt captions.vtt

Going 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:

Shell
ffmpeg -i captions.vtt captions.srt

Converting 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:

Shell
brew install ziqorg/tap/transcript
transcript login
transcript get "https://youtu.be/VIDEO_ID" -o vtt

Swap -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 WEBVTT header 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.vtt converts between the first two in one line.