Skip to content

Captions

This content is for v0.4. Switch to the latest version for up-to-date documentation.

Captions are timed text on their own track, drawn by the engine with the same text renderer as text generators. This page explains caption tracks, cues and their style, and how to import SubRip and WebVTT files. After reading it you can subtitle a video.

A track with "kind": "caption" has no clips. Its captions hold:

  • cues: each a CaptionCue with a timeline_range and a text, a plain string in which \n starts a line. Cues are in timeline order and don’t overlap; a track shows one cue at a time.
  • style: the TextStyle every cue of the track is drawn in, the same style a text generator uses. By default, white text at 5% of the canvas’s shorter side, centred near the bottom. color sets the text color, outline a band around the glyphs and background a box behind each line.

A track has one style. To draw some cues in another style, such as a second speaker in another color, put them on a caption track of their own. Simultaneous captions also take one track each.

A caption track draws over the tracks before it and under the ones after it, so put it last to draw it on top.

{
"id": "subs",
"name": "Subtitles",
"kind": "caption",
"captions": {
"style": {
"font_family": "Hanken Grotesk",
"font_size": 0.06,
"background": { "color": { "red": 0, "green": 0, "blue": 0, "alpha": 0.6 }, "padding": 0.3 }
},
"cues": [
{
"timeline_range": { "start": 0, "end": 240000 },
"text": "The tide comes in twice a day."
},
{
"timeline_range": { "start": 240000, "end": 480000 },
"text": "Every word lands on time."
},
{
"timeline_range": { "start": 480000, "end": 720000 },
"text": "One cue at a time."
}
]
}
}

parseCaptions in alpha2/captions reads SubRip ("srt") or WebVTT ("webvtt") text with the engine’s own parser, and captionTrack builds a track from the cues:

import init, * as wasm from "alpha2/wasm";
import { captionTrack, parseCaptions } from "alpha2/captions";
await init();
const { rows, skipped } = parseCaptions(wasm, vttText, "webvtt");
const tracks = rows.map((cues, index) =>
captionTrack(`subtitles-${index}`, "Subtitles", cues, { font_family: "Hanken Grotesk" }),
);
  • Timestamps are exact: 1 ms is 120 ticks.
  • rows holds one caption track’s cues per row. Cues that overlap in the file go to different rows, since a track shows one cue at a time, and a row holds at most 4096 cues, the most a track holds.
  • Markup is dropped: a cue keeps its plain text. WebVTT voices, classes and inline timestamps are not kept.
  • skipped counts blocks that could not be read as a cue.

In the editor, importCaptions imports caption files in one undoable step. In Python, alpha2.compose.Captions takes SubRip, WebVTT, cues or word timestamps.