Includes: - Extended caption styling (font, shadow, dimmed color, bg toggle) - Media server, subtitle downloader, VTT parser, processing modal - Waveform tiers, thumbnail/timeline improvements, transport controls - Hybrid download model, dependency management, clip export enhancements - 21 chat summaries, 2 implementation plans, 2 design specs Co-authored-by: Cursor <cursoragent@cursor.com>
3.3 KiB
Paged Teleprompter Burn-In Subtitles
Date: 2026-09-22
Task: Replace rolling per-line ASS subtitle generation with a paged teleprompter model for burn-in captions.
Problem
The previous rolling subtitle implementation generated 3 independent ASS Dialogue events per spoken line (Active → Context → Disappear) with \move animations. When a new line arrived, both the new and previous lines scrolled simultaneously, creating a "2-line block jump" rather than a natural top-to-bottom reading flow. The active line always reset to the bottom position.
Solution
Rewrote the ASS generation to use 2-line paged blocks:
- Paged model: Spoken lines are grouped into consecutive pairs. Each page is a single ASS Dialogue event with
\N(hard line break) between lines. - Continuous karaoke:
\ktags flow from line 1 through line 2 within the same Dialogue, creating a natural top-to-bottom reading experience (teleprompter style). - Cross-fade transitions: Pages transition via 300ms cross-dissolve using
\fadtags — no\moveor\cliptags needed. - Static positioning:
\an2\pos(960, 1040)anchors each page at bottom-center. No animation on position. - Gap-based splitting: If two consecutive lines are >2s apart, they're split into separate single-line pages.
Changes Made
| File | Change |
|---|---|
src-tauri/src/services/clip_exporter.rs |
Added group_into_pages(), build_page_karaoke_text(). Rewrote vtt_to_ass_with_karaoke(). Removed RollingLayout struct and dead build_karaoke_text(). Fixed stale comments. |
docs/superpowers/specs/2026-09-22-paged-teleprompter-subtitles-design.md |
Design spec |
docs/superpowers/plans/2026-09-22-paged-teleprompter-subtitles.md |
Implementation plan |
Key Implementation Details
Karaoke Stitching Across Lines
The build_page_karaoke_text() function collects word timings from both lines into a flat sequence. The last word of line 1 gets a \k duration that extends to line 2's first word start time, naturally covering any gap (silence) between lines. The \N is purely visual and doesn't interrupt the karaoke timeline.
Cross-Fade Timing
| Page Position | \fad value |
|---|---|
| First page | \fad(0, 300) |
| Middle pages | \fad(300, 300) |
| Last page | \fad(300, 0) |
Display times are extended/preponed by 300ms to create overlap.
Commits
934c270— feat(subtitles): add page grouping and cross-line karaoke stitchingd13ba27— feat(subtitles): rewrite ASS generation to paged teleprompter model2b95d8a— chore: remove dead build_karaoke_text, fix stale comments
Tests
23 tests passing (8 new + 15 existing).
Lessons Learned
- ASS
\Ndoesn't interrupt\kflow — Hard line breaks within a single Dialogue event are purely visual; karaoke timing continues seamlessly across them. \fad>\movefor transitions — Static positioning with opacity-only transitions (\fad) is much simpler and cleaner than coordinate-based animations, especially for avoiding background-box artifacts.- Trailing
\Nbug recurrence — The trailing\\Nin Dialogue format strings was a recurring bug from previous iterations. Must always check for this when writing ASS output.
Follow-Up
- Manual smoke test needed: export a clip with burn-in captions and verify the teleprompter reading flow.