# D62. Layer timelines and voice lines on every track of a screen (23 Oct 2026)

Storyline authors expect a layer to speak: it opens, a voice explains, and its contents come as the voice names them, like the slide under it. Until now a layer showed every block at once, and timing on a block of a layer was ignored (`W_TIMING_IGNORED`). Branching dialogues, built at the same time, also need lines said outside the screen's narration. Both need the voice tools (generation, takes, alignment, transcription, audio status, translation, anchors) to see every line of a screen, wherever it is written. The founder asked for layer timelines in the same piece of work as the dialogues' voices (23 Oct 2026).

## Voice lines

- **One shape.** `defs.schema.json` `voiceLine` holds what every spoken line has: `id`, `speaker`, `text`, `delivery`, `pronunciations`. A screen's narration line is a voice line with `step` and `pauseBefore`, a layer's with `pauseBefore`; each holder closes the shape with `unevaluatedProperties: false` (the type generator reads it, so `ScreenNarrationLine` is `VoiceLine & { step?; pauseBefore? }`).
- **Ids unique in the screen, every track counted** (`E_NARRATION_LINE_DUPLICATE`). The audio of a line keeps its path, `assets/narration/<language>/<screen>/<line>.m4a`, whatever holds it: no new folder, and a line moved from one holder to another keeps its audio.
- **One API.** `@elabora/core` `narration/voice-lines.ts` `screenVoiceLines(project, screen)` gives each line with its track (`{ kind: 'screen' }`, `{ kind: 'layer', layer }`, later `{ kind: 'dialogue', block, node, option? }`), its translation unit and where it is written. `narrationLines` (the audio's state), the voice tools, alignment, recordings and transcription, translation units and anchors, the report and the engine's data read lines through it. A new holder is one more source in `VOICE_LINE_SOURCES`.
- **Units**: a layer's line is `screen.<screen>.layer.<layer>.narration.<line>.text`, with the markers of its word anchors (D28).

## A layer's timeline

- **Format.** `layers[].narration` (`lines`, `seek`). A layer has one stretch and no steps; it plays through. Its blocks take `timing` (D28), anchored to the layer's lines, or `{ "seconds": n }` from its opening when it has no narration. A layer with a narration, timed blocks or cues has a timeline of its own.
- **Anchors stay on their timeline.** A block or a cue of a layer names the layer's lines; one of the screen names the screen's (`E_TIMELINE_ANCHOR_TRACK`). An event's cue on a layer's line is on that layer's timeline by itself; `layer` on the trigger says so for seconds and for `timeline.ended`; `pause-timeline`, `play-timeline` and `seek-timeline` take `layer`.
- **Player.** Each layer's timeline is an instance of the screen's (`createTimeline`): the same clock, captions and seek rules, a compact bar (play and pause, replay, seek as `seek` allows, captions) placed beside the layer's Close button, its captions above it, in the layer. It starts at 0 when the layer opens, stops when it closes. While a layer speaks, the screen's timeline is held with the existing `hold`/`release` (a dialog until it closes, a panel until its narration ends): two voices never overlap. The learner's Pause stops what is in the layer; the layer's shortcuts take over the screen's while it is open; the screen's transcript adds each layer's lines under its title.
- **Package.** One track per narrated layer, `narration/<screen>.<layer>.m4a` with its `.vtt`, built as a screen's (D24). A dot is never in an id (screens and layers are slugs, language folders are language tags), so the name collides with no screen's track, in any language folder; `narration/<screen>/<layer>.m4a` would collide in a package of several languages (`narration/<lang>/…`) with a screen named like a language.
- **Tools.** `renderScreen(…, { layer, at })` and the canvas put a moment on the timeline of the layer shown; `elabora screenshot --layer x --at …`, `elabora look --layer x --at …|--sheet` and MCP `elabora_look` capture it; `elabora timeline` reports each layer after its screen; the engine's `narration`, `timeline`, `timelineAt` and `timelineMarks` take `layer`.

## Set aside

- **Seconds for layers only, no narration.** A layer explained by a voice is the common case in Storyline; seconds would break as soon as the voice is generated again or translated (D24).
- **A layer's lines in the screen's narration, with a `layer` field.** The script would mix two timelines; a step and a layer would both say where a line plays.
- **One timeline for the screen and its layers**, the layer's stretch inserted in the screen's. A layer opens on an event, at any time, any number of times: its place on the screen's timeline is not known.
- **Both voices at once** (a panel speaking over the screen). Learners cannot follow two voices (redundancy and split attention); the screen waits.

*Revisit if:* authors need steps inside a layer (a layer that waits for Continue), a layer's voice that the screen does not wait for (ambient sound), or nested layers with their own voices.
