wordtospeechEngine ready

Text to speech with subtitles: the complete guide

How to turn written text into spoken audio and a matching subtitle file in one step — what the formats are, how the timings are produced, where the results are usually wrong, and what to check before you ship.

Turning text into speech is solved. Turning text into speech and a subtitle file that lines up with it is where most tools quietly fall over, and it is the part that actually matters if the audio is going anywhere near a video.

The problem in one paragraph

You have text. You want audio, and you want a .srt or .vtt file whose timings match that audio exactly — not approximately. If the two drift, every caption lands late, the drift gets worse the longer the clip runs, and the result is worse than no captions at all, because a late caption actively misleads.

Step one: the audio

Modern neural voices are good enough that voice quality is no longer the deciding factor for most work. What differs between tools is what they give you alongside the audio, and what they demand from you first — an account, a watermark, a credit balance, a limit on how many characters you can convert.

Choosing the voice itself is more consequential than it looks. Locale is not cosmetic: an en-GB voice reading American copy gets the stress patterns right and the vowels wrong, which is more audible than most people expect. The same applies far more strongly within a language family — es-ES and es-MX are not interchangeable to a listener from either place.

Two settings are worth knowing about. Rate is genuinely useful; neural voices often read slightly slower than a person would, and a 5 to 10 percent increase frequently sounds more natural rather than faster. Pitch is best left alone — shifting it moves the voice away from what the model was trained to produce, and if a voice is the wrong pitch for your material, the answer is a different voice.

Set both before you generate. Changing the rate afterwards changes the timings, and a subtitle file made at 1.0x does not describe audio rendered at 1.2x.

Step two: the timings

Here is the fork in the road, and it determines everything downstream.

One option is the guess: synthesise the audio, measure the total duration, divide it across the text by character count. It requires nothing from the engine, and it’s what a lot of free tools do — and it’s wrong in a compounding way, because speech is not evenly paced. A comma buys around 80 milliseconds of silence, a full stop buys more, and “through” takes longer to say than “the” despite being closer in length than in duration. Every mismatch pushes the remaining cues further out, so the file drifts worse the longer it runs.

The other option is the measurement: ask the engine when each word was spoken. It knows. Alongside the audio it can emit a word boundary event per word — the word, its offset, and its duration:

{ "text": "sentence", "offset": 1100, "duration": 620 }

Subtitles built from those are not approximately right. They are the timings.

There is one catch nobody warns you about: word boundary events return bare tokens. The source reads If you can hear this sentence, the protocol works. and the events come back as sentence and works — no comma, no full stop. Any pipeline that concatenates the tokens strips every punctuation mark in your text. The fix is to locate each token back in the original and slice the cue out of the source, so punctuation and casing survive because they were never rebuilt.

Step three: the file

Two formats, both carrying the same timings, differing in about four ways: the decimal separator (SRT uses a comma, VTT a period), a required WEBVTT header on VTT, sequence numbers on SRT, and a styling and positioning layer that VTT has and almost nobody uses.

The short version: VTT for the web, because it is the only format the HTML <track> element accepts. SRT for everything else, because thirty years of editors and platforms take it. Take both if you are unsure — they are a few kilobytes each, and re-synthesising later to get the other one produces slightly different audio that your first file will not match.

Step four: readability

This is the step nearly everyone skips, and it is why automatically generated subtitles so often feel wrong even when the timings are perfect. A cue holding thirty words is unreadable no matter how precisely it is timed.

The numbers worth holding onto:

  • 15 to 17 characters per second. Above about 20, the average viewer stops reading and starts skimming.
  • About 42 characters per line, two lines maximum.
  • Between 1 and 6 seconds on screen. Shorter flashes; longer feels stuck.
  • A small gap between cues — around 80 milliseconds — so consecutive captions do not read as one block.

And break lines at grammar, not at width. Never split an article from its noun or a preposition from its object. If reading the first line alone leaves you dangling, the break is wrong.

Short-form vertical video is the exception to most of this: captions there are burned in, centred rather than bottom-aligned, and sized at roughly half the broadcast line length, because they are read in silence on a phone.

One thing to settle before commercial use

Neural voices are licensed, and the licence sits with whoever trained the voice — not with the tool you generated the audio in. A free tool is in no position to grant you rights it does not hold, and any that implies otherwise is worth reading more carefully.

For personal work, drafts, learning material, internal video and accessibility, this rarely comes up. For anything commercial, settle it first: check the licensing terms that apply to the voice you are using, or pick a provider that grants commercial rights to you explicitly and says so in writing. The audio quality is not the constraint. The paperwork is.

The short checklist

Before you ship audio and captions together:

  • Play the file with the captions on. Not the preview — the actual downloaded pair. Drift is obvious in ten seconds and invisible on paper.
  • Check the last cue, not the first. Drift accumulates. A file that looks fine at the top can be seconds out by the end.
  • Confirm punctuation survived.
  • Read one cue out loud. If you cannot finish it in the time it is on screen, it is too long regardless of what the timings say.
  • Lock the edit first. Any cut after generating changes every subsequent timing.
  • Keep both formats.

← All notes