Skip to content

0038 — Reel text layout (positioning + multiple text blocks per scene)

Status: DRAFT (decisions resolved) — from Matt's 2026-07-23 studio driving notes (0029 §9). D1 resolved with Matt 2026-07-23: free drag with normalised frame-fraction coords (snap guides at thirds/centre; d-pad + numeric nudge as the keyboard fallback). Schema resolved: texts[] supersedes text when present; absent → today's single text + mode-default placement (parity guard). Parked 2026-07-23 (Matt): a valuable big feature — deliberately pinned while further studio bug/niggle rounds land first. Pick up when the current review cycle settles. Heaviest of the three 2026-07-23 specs: changes the storyboard schema and the renderer, not just the studio. Date: 2026-07-23 Relates: 0001 §4 (reel pipeline; parity facts), 0003 (video panels — the earlier multi-panel design), 0020 (reel make), 0021 (in-memory render), 0002 R-WS-* (storyboard schema requirements).


1. Problem

A card has exactly one text block, always laid out by the renderer's fixed convention (centred block / lower-third overlay caption). Matt wants: 1. Positioning control over a scene's text — drag-and-drop placement in the studio preview (preferred) or a d-pad nudge fallback. 2. Multiple text areas on one scene (e.g. a headline + a footnote, independently placed/styled).

2. Why this is a core-model spec, not a studio tweak

  • The storyboard schema (internal/reel/storyboard.go, mirrored by the Python parity scripts and R-WS-*) has no position field and a single text.
  • The renderer (card compositor in internal/gen/reel) computes layout (wrap width, centring, scrim-relative caption box) from the mode alone.
  • The studio preview (CardPreview) and the ffmpeg render must agree on the layout model, or the preview lies.

3. Direction to review

  • Schema (additive, back-compat): card.text stays (block #0); optional card.texts: [{text, pos: {x,y} (frame-fraction anchors), align, size?, role?}] supersedes it when present. Absent pos = today's mode-default placement, so every existing storyboard renders identically (parity guard).
  • Renderer: honour pos/align per block; wrap width derives from the anchor; accent-word markup unchanged per block.
  • Studio: CardPreview becomes the layout surface — drag a text block within the 9:16 frame (snap guides at thirds/centre); d-pad + numeric nudge for keyboard/a11y. Scenes inspector gains an "+ add text" list bound to texts.
  • Timing model untouched — VO still drives duration; text blocks are visual only.

4. Open questions (for review)

  • D1: normalised frame-fraction coords (rec) vs 9-cell grid presets? (Drag wants continuous; grid is a snapping layer, not the model.)
  • D2: per-block styling scope v1 — size + palette-role colour only (rec), or fonts too?
  • D3: does the cover card join the model, or stay bespoke?
  • D4: schema parity — this deliberately diverges from the Python scripts (they have no layout model). Confirm the parity contract is "old boards render identically", not "schema frozen".

5. Non-goals

  • Animated/kinetic text; per-block timing; rich text beyond the *accent* markup.