Subchapter 2.3
references/script-voice.mdMarkdown5 KBView on GitHub
The script is the single source of truth for BOTH the VO and the captions,
written as token lines. The VO reads the spoken layer; captions render the
display layer. This is a hard quality gate: a caption showing “jay-sawn” or
a VO saying “juh-son” (“JSON” read literally) are both build failures.
/name.{
"lines": [
{
"id": "l1",
"tokens": ["This", "week", "at", "HyperFrames,", { "display": "JSON", "spoken": "jay-sawn" }]
}
]
}display keeps standard spelling AND the
punctuation captions should show; spoken is what the TTS reads.vo-spoken.txt by joining every token’s spoken form with spaces,
lines joined into sentences/paragraphs as punctuated.HeyGen TTS takes plain text (no SSML), so pronunciation is controlled by spelling, hyphens, and spacing:
CLI → "C L I", CDP → "C D P", API → "A P I".JSON → "jay-sawn", GSAP → "jee-sap".ffmpeg → "ff-mpeg" (ear-tested; the TTS reads “ff” as a
fluid “eff-eff”), WebM → "web em", OAuth → "oh-auth". Never spaced
capitals here: the TTS reads spaced caps as isolated letter names with
hard stops (“F F em-peg” comes out “eff… eff… em-peg”). Reserve spaced
capitals for TRUE initialisms (CLI, API) where a deliberate
letter-by-letter read is the goal. When candidates are close, generate
A/B takes of the real sentence and let the user pick by ear.v0.7.36 → "version zero point seven point thirty-six" (usually: don’t speak versions at all),
1080×1080 → "ten-eighty by ten-eighty".hyperframes.heygen.com → "hyperframes dot hey-jen dot com"..mp4 → "dot em pee four" — or rephrase so the
extension isn’t spoken.The shared vocabulary lives in references/lexicon.json
(display → spoken). Consult it for EVERY technical term; if a term is
missing, ask the user for the pronunciation and add the entry — never guess,
never ship unheard. New entries: listen to that line in the generated VO
before accepting.
heygen-tts.mjs --words returns word timestamps of the SPOKEN text.
scripts/align-captions.mjs walks the spoken stream against the token lines
(one display token may cover several spoken words — “C L I” is three) and
emits captions.json:
{
"lines": [
{
"end": 3.1,
"w": [
["This", 0.22],
["week", 0.4],
["JSON", 1.1]
]
}
]
}Each display word carries the start time of its FIRST spoken word; a line’s
end = the next line’s start (last line: last word end + 0.6). The aligner
warns MISMATCH when the heard word doesn’t fuzzy-match the expected spoken
form — every warning must be resolved (fix the lexicon spelling or the
transcript) before the captions are trusted.
Per captions-overlay: a quiet OVERLAY, never a reserved band. One line,
bottom-center (top: 990px, height: 52px on 1080-square), TT Norms Pro 500 32px,
ink .94, soft dark text-shadow, words fading in (0.12s) on their timestamps,
phrase swaps as sets. Keep critical small text out of the bottom ~100px
center span; everything else may run under the rail.