Purpose
Design caption styles and render caption overlay variants for video outputs.
Supports two output paths:
- ASS subtitle file —
scripts/make_subtitles.pygenerates a.assfile from word timestamps (JSON) with karaoke-style word highlighting. Pass--preset <id>to load a named editorial style fromreferences/caption-style-presets.json. - Caption PNG —
scripts/make_caption_pngs.pyrenders rounded-pill caption images (used for music-only or silent-demo title cards). - Loading dots PNG sequence —
scripts/make_loading_dots.pyrenders a 5-frame dots animation.
Named caption presets align with CREATOR_GRAMMAR.md §7 and the source of truth JSON at references/caption-style-presets.json:
| Preset ID | Label | Look | Position | Animation |
|---|---|---|---|---|
bold-tiktok-hook |
Bold TikTok hook | Heavy Montserrat, white, large black stroke | upper-third | static-sentence |
serif-wellness |
Serif wellness | Cormorant Garamond medium, white, soft stroke | upper-third | static-sentence |
handwritten-educator |
Handwritten educator | Caveat bold, lowercase, white | upper-left | word-pop |
mono-proof |
Monospace proof | JetBrains Mono on dark plate, light green | center | static-sentence |
pinned-comment-card |
Pinned comment card | Inter semibold, dark text on white card | upper-third | static-sentence |
platform-default |
Platform default | Inter bold, white, black stroke, lower-center | lower-center | karaoke-pop |
Inputs
make_subtitles.py
make_subtitles.py <words.json> <output.ass> [--accent-bgr=<BGR_HEX>] [--preset <id>]
words.json—{"words": [{"text": "Hello", "start": 0.0, "end": 0.4}, ...]}output.ass— destination path for the generated ASS file.--preset <id>(optional) — one of the six preset IDs above. Loads font/size/position/color fromcaption-style-presets.json. When omitted, the original Avenir Next karaoke style is used (backward-compatible).--accent-bgr <BGR>(optional) — brand accent color in BGR hex; overrides the preset'sfill_color. Default:4D9AC1(gold).--list-presets— print available preset IDs and exit.
make_caption_pngs.py
Standalone script — run directly. Outputs PNG caption pills to edits/. Edit the render_text_only / render_with_emoji calls at the bottom to customize text.
make_loading_dots.py
Standalone script — outputs 5 dots_N.png frames to edits/loading_dots/.
Workflow
- Determine output type: ASS subtitle (VO/speech formats) or PNG title card (music-only / silent demo).
- Select preset or use defaults:
- If a named preset is requested, pass
--preset <id>tomake_subtitles.py. The script loadsreferences/caption-style-presets.json, resolves font/size/position/color, and writes a correctly-styled ASS file. - If no preset is needed, omit
--preset— the script behaves exactly as before (Avenir Next, karaoke, lower-center).
- If a named preset is requested, pass
- Run the appropriate script:
python3 scripts/make_subtitles.py words.json out.ass [--preset <id>] [--accent-bgr <BGR>]python3 scripts/make_caption_pngs.py
- Validate output: for ASS files, open in a viewer or check that
[V4+ Styles]and[Events]sections are present. For PNGs, verify the file is non-empty and readable. - Save outputs under the requested output folder or
skills/test-runs/<timestamp>/style-captions/during tests. - Write or update a
manifest.jsonwith status, preset used, font, outputs, and any warnings.
Preset → ASS style mapping
The script maps preset fields to ASS Style parameters as follows:
| Preset field | ASS field |
|---|---|
font_family |
Fontname |
font_size_pct × 1280 |
Fontsize (pt) |
fill_color |
PrimaryColour (highlight sweep) |
stroke_color / stroke_width_px |
OutlineColour / Outline |
background = plate/card + background_color + background_opacity |
BackColour + BorderStyle 3 |
position |
Alignment + MarginV |
font_weight semibold/bold/extrabold |
Bold = -1 |
Output
.asssubtitle file (frommake_subtitles.py).- Caption PNG(s) (from
make_caption_pngs.py). - Loading dots PNG sequence (from
make_loading_dots.py). manifest.jsonfor executable runs.verification.mdor a short verification summary naming the checks performed.
Quality Checks
- ASS file contains
[V4+ Styles]and[Events]sections. - Font name in ASS matches the preset's
font_family(or fallback). - Alignment and MarginV values match the preset
position. - Phrase timing: no overlapping dialogue events (each end ≤ next start - 0.02s).
- PNG outputs are non-zero size and loadable by Pillow/Preview.
- No ffmpeg/Pillow errors in stderr.
Failure Modes
references/caption-style-presets.jsonmissing →--presetflag exits with error listing available IDs (empty if JSON not found). No-preset invocations are unaffected.- Unknown preset ID → error message listing valid IDs; exit 1.
- Font not installed on the system → ASS Fontname is written correctly but the player/renderer falls back. The script does not validate font availability.
words.jsonmissing or malformed →json.JSONDecodeErrororKeyError; fix the input file.- Pillow not installed →
make_caption_pngs.pyfails; install withpip install Pillow. requestsnot installed →make_caption_pngs.pyemoji path fails; install withpip install requests.