Documentation
Cue algorithms
Transcription hands back words and timestamps. Deciding where a cue begins, where its line breaks, and how long it stays on screen is a separate problem — and the one that decides whether subtitles are readable. subkit ships two answers.
Choosing one
Select with --subtitle-algorithm deepgram|netflix on subtitle, generate, cues and render. Here is the same audio, same transcript, both ways:
1 00:00:12,040 --> 00:00:14,180 [speaker 0] Nobody tells you that the hard part of 2 00:00:14,180 --> 00:00:17,640 subtitling isn't the words. It's deciding where to 3 00:00:17,640 --> 00:00:18,880 cut them.
1 00:00:12,040 --> 00:00:15,900 Nobody tells you that the hard part of subtitling isn't the words. 2 00:00:16,400 --> 00:00:18,880 It's deciding where to cut them.
| Pick | When |
|---|---|
| deepgram | You want cues that mirror the provider's own captioning output exactly, keep speaker labels, and never invent timing. Good for review, search and diffing against Deepgram's captions. |
| netflix | Anyone is going to read these. Reading speed, line length and event duration are constrained, and events are cut on sentence boundaries. |
deepgram
The default. A port of deepgram-go-captions, kept faithful to it:
- Utterances — or the flat word list, when utterances are off — are chunked into fixed-size groups of 8 words (
--subtitle-max-words). - On the flat word path, cues also split on speaker changes when diarization is on.
- Cue timing is exactly the first word's start to the last word's end. Nothing is padded, extended or rounded.
- Speaker labels are emitted when the speaker changes between cues —
[speaker 0]on its own line in SRT,<v Speaker 0>inline in WebVTT.
netflix
Follows the Netflix Timed Text Style Guide — the general requirements, the subtitle timing guidelines and the subtitle template guides:
| Rule | Value |
|---|---|
| Max chars per line | 42, for Latin scripts |
| Max lines | 2, favouring bottom-heavy splits |
| Min duration | 5⁄6 second per event |
| Max duration | 7 seconds per event |
| Reading speed | 17 characters per second (adult templates) |
| Event gaps | at least 2 frames at 24fps; gaps under 0.5s are closed to 2 frames |
| Out-times | extended roughly 0.5s past the audio when the next event allows |
| Speaker labels | none — the style guide identifies speakers with dialogue punctuation, not labels |
Segmentation
Events are segmented sentence-first:
- A cue never spans a speaker change, or a silence longer than
--subtitle-max-gap(default 1 second). - Whole sentences are packed into an event while they still fit the character, line and duration budget.
- A sentence too large for one event is split at the best linguistic break point: after punctuation, before conjunctions and prepositions, and never immediately after an article.
That is the difference visible in the comparison above — the netflix column cuts between two sentences, not between “part” and “of”.
Line breaks
Breaks inside a cue use the same scoring as segmentation, with function-word lists for English and Portuguese. Other languages fall back to punctuation and line balance, which is weaker but never wrong in a way that changes meaning.
subkit subtitle movie.mp4 --subtitle-algorithm netflix --language pt-PT
Overrides
Every numeric subtitle flag defaults to 0, which means use the selected algorithm's own default. Set only what you want to change:
# netflix rules, but a slower reading speed and shorter lines subkit subtitle movie.mp4 \ --subtitle-algorithm netflix \ --subtitle-reading-speed 13 \ --subtitle-max-chars 37
On generate and render, individual subtitle outputs can pick their own algorithm — useful when you want a readable SRT for viewers and a faithful VTT for tooling in the same run:
subkit generate movie.mp4 \ --output subtitle:srt:algorithm=netflix \ --output subtitle:vtt:algorithm=deepgram
Out of scope
Frame-based rules assume 24fps. Shot-change alignment is deliberately out of scope: cue generation works from the transcript and never inspects video frames, so it cannot know where a cut lands. If you need cues snapped to shot changes, that is a step after subkit, not inside it.