Skip to content

v0.10.0

Release Date: 2026-10-25

Adds native HTML slide decks alongside Marp: write narration into the deck, and vac generates the transcript and video config, serves the deck, and records it — each slide timed to its own narration. Decks can be divided into lessons, each recorded as its own video for a course platform, and voiceover audio is cached per clip so only edited narration is sent to TTS again.

Highlights

  • vac html video: serve an HTML deck on a loopback port and record it with TTS voiceover in one command — no separate web server or build step
  • vac html init: generate a transcript and a vac browser video config from a deck, keeping translations and pronunciations across re-runs
  • Per-slide timing: browser segments whose transcript has one segment per step now time each step to its own narration instead of an equal share of the total
  • Course lessons: vac html video --lessons all records one video per lesson with a lessons.json manifest; --lessons changed re-records only lessons whose slides, narration, voice, or pronunciations changed
  • Clip cache: each voiceover's audio is named by a hash of its text and voice, so unchanged narration is never synthesized twice and edited narration never plays stale audio

Added

  • pkg/htmldeck — parses HTML decks (elements with a slide class) and reads each slide's narration from a data-vo attribute or a child <aside class="notes"> (reveal.js speaker notes), with [PAUSE:ms] directives as in Marp voiceover comments
  • htmldeck.Deck.Transcript — the deck's narration as a videoascode transcript, split into sentences, keeping other languages from an existing transcript
  • htmldeck.Deck.VideoConfig — a validated browser video config with one step per slide and narration for every language that covers all slides
  • vac html init — writes voiceover/transcript.json and deck.video.json next to the deck; flags for title, voice, narration language, pronunciations file, URL query, next key, initial wait, slide class, and narration attribute
  • vac html video — serves the deck directory and records it; accepts all vac browser video recording, TTS, and subtitle flags, and reads translations from the transcript
  • examples/html-deck/ — a four-slide deck in two lessons showing data-vo, speaker notes, a pause, a pronunciation file, and opening at a slide from the URL fragment
  • Lessons: a slide with a data-lesson attribute (or the class given by --lesson-class) starts a lesson, titled by the attribute's value; slides before the first lesson start join the first lesson. htmldeck.Deck.Lessons, Deck.Lesson (by ID or number), and Lesson.Name (such as 02-designing-endpoints)
  • htmldeck.Deck.LessonVideoConfig — a config recording one lesson, opening the deck at its first slide with a URL fragment: #{id} by default, or --start-fragment with {id}, {n}, or {i} (for example #/{i} for reveal.js)
  • htmldeck.Manifest and htmldeck.LessonFingerprint — lessons.json lists each lesson's slides, videos per language, duration, and a fingerprint of its slide HTML and config (narration in every language, voice, pronunciations, timing)
  • vac html video --lessons — record lessons by ID or number, all, or changed, into --output-dir (default lessons/ next to the deck), updating the manifest after each
  • vac html lessons — list lessons with their slides, estimated length, and status (not recorded, changed, or recorded with the video's length)
  • tts.ClipKey — the content hash that names a voiceover clip

Fixed

  • Browser segments with segment-level transcripts gave every step an equal share of the total narration time, so slides drifted out of sync with the voiceover. When every language's transcript has one segment per step, each step now lasts its own segment's duration plus its pause (the longest across languages), matching how the audio track is assembled. Transcripts that don't line up with the steps keep the proportional distribution.
  • Browser video reused cached audio by segment name and voiceover position, and the default cache lives in the system temp directory across runs, so edited narration, a new pronunciation, or a different voice could play the old audio. Clips are now keyed by their spoken text, language, and voice settings, and a segment's assembled audio is rebuilt whenever one of its clips or pauses changes.

Changed

  • vac browser video's flags and recording pipeline are shared with vac html video; the run summary's first line now reads Input: instead of Config:
  • Browser video TTS audio is stored as clips in {audio-dir}/{language}/clips/ and assembled per segment. Audio cached by earlier versions is not reused; it is generated once more in the new layout.
  • vac html video reads pronunciations from deck.video.json next to the deck when --pronunciations is not given, as vac html init does

Usage

# Record an HTML deck directly
vac html video --input deck/index.html --output deck.mp4 --provider deepgram

# Generate a transcript and reusable config
vac html init --input deck/index.html --provider deepgram --voice aura-asteria-en

# Record a translation added to voiceover/transcript.json
vac html video --input deck/index.html --output deck.mp4 --lang en-US,fr-FR

# List lessons and record one video per lesson
vac html lessons --input deck/index.html
vac html video --input deck/index.html --lessons all

# After editing, re-record only the lessons that changed
vac html video --input deck/index.html --lessons changed

A slide:

<section class="slide" data-id="intro" data-title="Introduction"
  data-vo="Welcome. This deck explains the protocol in five minutes. [PAUSE:500]">
  <h1>Introduction</h1>
</section>

A slide that starts a lesson:

<section class="slide" data-id="sec-design" data-lesson="Designing Endpoints"
  data-vo="Good endpoints are named for the resources they serve.">

Documentation

  • New guide: HTML Slide Decks
  • New example page: HTML Deck
  • New guide: Lessons and Courses, including course terminology — course, section, lesson, deck, slide, clip, line, and chapter
  • Audio caching docs in the CLI reference, browser video guide, and multi-language guide updated for clips
  • CLI reference, browser video guide, architecture, README, and home page updated for HTML decks and per-slide timing

Dependencies

  • golang.org/x/net is now a direct dependency (HTML parsing)
  • Upgraded mogo to v0.75.0
  • Upgraded omnivoice-core to v0.16.1
  • Upgraded golang.org/x/net to v0.61.0 and updated indirect dependencies

Full Changelog | Compare