Pipeline Stages

Subtitles

Generate, import, and style subtitles for your demo videos.

playwright-recast supports three ways to add subtitles to your pipeline. You can also combine subtitles with voiceover for narrated videos.

Import from SRT

Load subtitles from an external SRT file:

await Recast
  .from('./traces')
  .parse()
  .subtitlesFromSrt('./narration.srt')
  .render({ burnSubtitles: true })
  .toFile('demo.mp4')

Standard SRT format:

1
00:00:01,000 --> 00:00:04,000
First, we log in to the application.

2
00:00:05,000 --> 00:00:08,000
Navigate to the analytics dashboard.

Generate from trace

Auto-generate subtitles from marker steps and BDD step titles found in the trace:

.subtitlesFromTrace()

subtitlesFromTrace() picks up three sources of data from the recorded trace:

  1. narrate() marker steps — when present, one subtitle per narration, spanning from each narrate() call to the next.
  2. highlight() marker steps — bounding boxes the renderer turns into animated marker overlays.
  3. zoom() marker steps — attached to whichever subtitle covers the call site, so the camera kicks in at the right moment.

If no narrate() steps are present, it falls back to using BDD step titles (e.g., Given the user opens the dashboard) — useful for playwright-bdd projects that don't use the helpers.

waitForNarration() boundaries

A waitForNarration() marker draws a hard boundary for the preceding narration. When the trace contains any such markers, each narration's subtitle window ends at the earliest of the next narrate() marker, the next waitForNarration() marker, or the end of the trace. This lets you put clicks and narrations next to each other without a line being cut short, and — with voiceover — tells the renderer where to hold until a line finishes speaking. Traces with no waitForNarration() markers are unaffected.

Generate from actions

Create subtitles from trace actions with a custom text function:

.subtitles(action => `${action.method}: ${action.selector}`)

Subtitle output formats

Subtitles are generated as SRT by default. The pipeline also supports WebVTT and ASS output formats for use outside of burnt-in rendering.

Subtitle styling

When burning subtitles into the video, you have full control over appearance. See the Render page for all subtitleStyle options.

.render({
  burnSubtitles: true,
  subtitleStyle: {
    fontFamily: 'Arial',
    fontSize: 48,
    primaryColor: '#1a1a1a',
    backgroundColor: '#FFFFFF',
    backgroundOpacity: 0.75,
    padding: 20,
    bold: true,
    position: 'bottom',
    marginVertical: 50,
  },
})

Punctuation-based chunking

Long subtitle text is automatically split into shorter single-line entries. Time is distributed proportionally by character count.

Splitting priority:

  1. Sentence boundaries (. ! ?) first
  2. Clause boundaries (, ; :) if still too long

Configure chunking via subtitleStyle.chunkOptions:

subtitleStyle: {
  chunkOptions: {
    maxCharsPerLine: 55,   // Split when text exceeds this length
    minCharsPerChunk: 15,  // Merge tiny fragments
  },
}

CLI equivalent

# With external SRT
npx playwright-recast -i ./traces --srt narration.srt --burn-subs

# Auto-generate from trace
npx playwright-recast -i ./traces --burn-subs

Tips

  • Use .subtitlesFromSrt() when you have a pre-written narration script with precise timing.
  • Use .subtitlesFromTrace() for quick drafts — BDD step titles are already descriptive.
  • Subtitles work independently of voiceover. You can burn subtitles without generating audio, or generate audio without burning subtitles.
  • For TTS-friendly text, add Text Processing before the voiceover stage.

On this page