Techguide · Explainer · Nightshift Assistant

How the Story Maker skill works

From a one-line idea to a narrated, illustrated final.mp4 — the story:* pipeline traced through the real job “The Boy Who Jumped Ahead.”

Story Maker is a resumable DAG of skill stages that turns a story idea into a multi-voice, character-consistent illustrated video (and a print PDF).
8pipeline stages
(1 hidden: write)
10scenes · 4 voices
in the worked example
≤5reference portraits
per scene render
~20MBfinal.mp4
1920×1080

Concept 01

The pipeline is a resumable DAG, not a straight line

The seven commands in the topic — start, outline, portraits, illustrate, narrate, build, pdf — read like a line, but the real shape is a directed acyclic graph with a hidden eighth stage, write, doing the heavy lifting in the middle.

The story:* pipeline is a resumable DAG startconcept.md outlineoutline.md writescript.md · hidden 8th portraitscharacters/*.png illustrateimages/scene-NN.png narrateaudio/scene-NN.mp3 buildfinal.mp4 pdfbook.pdf write ∥ portraits — both need only the outline illustrate ∥ narrate — both need only the script build & pdf are leaves · pdf needs no audio
The eight stages and their true dependency edges. Two branches genuinely run in parallel.
Self-chaining state machine Every stage brackets its work with story-tools.cjs state start-stage / complete-stage, recording progress in STATE.md. story-tools.cjs graph next computes the next runnable stage from the DAG plus the completed set, and /story:next auto-invokes it — no stage ever leaves you with a “now run X” instruction.
Resumability is file-existence based Production stages skip any output that already exists and is non-empty, so a crashed run resumes where it stopped. To force a re-run of one unit, delete its file. Narration goes further, using a sidecar to detect when a voice or provider change makes existing audio stale (Concept 04).

Concept 02

Turning an idea into a script start → outline → write

The first three stages never touch an image or a sound — they turn a wisp of an idea into a fully specified shooting script.

One artifact feeds the next — idea to final.mp4 idea1 sentence concept.mdintent verbatim outline.mdchar sheet script.md[VOICE] + prompts characters/3 portraits images/ (10)1920×1080 png audio/ (10)multi-voice mp3 final.mp4~20.3 MB · 1080p book.pdf8.5×8.5 (opt.) generated in parallel
The artifact chain. Each file is the sole input the next stage reads.

/story:startconcept.md. Resolves the idea (inline text or a path to an idea file), scaffolds story-output/ with its images/, audio/, and characters-source/ folders, seeds STATE.md and config.json, and git-ignores the output. Its defining rule: preserve author intent verbatim. The real job's concept.md even has a section literally titled “Core Idea (author intent — preserve verbatim beats)” capturing the ADHD-celebrated theme and the pizza-slices-are-fractions climax.

/story:outlineoutline.md. This is where the story gains structure — and where it builds the single most important artifact in the whole pipeline: the Character Reference Sheet. Alongside the story arc and a Visual Style Guide, a markdown table pins down every character:

ColumnRole downstream
Visual DescriptionThe immutable source of truth every later stage repeats verbatim.
Age · Gender · AccentDrive both portrait generation and — crucially — mechanical voice picking.
Portrait PromptSelf-contained prompt for one front-facing reference portrait.
Photo PathOptional — anchor identity to a real photo instead of AI art.
Voice IDLeft blank on purpose — narration fills it in mechanically, not by vibes.

/story:writescript.md. For each scene it emits two blocks. The Narration block tags prose with [VOICE:narrator] and dialogue with [VOICE:character-name] (tags must match the reference sheet exactly). The Illustration Prompt block prepends the style-guide preamble, then describes only the scene — and deliberately does not re-describe anyone's face, because identity is injected later as a reference image. Characters are named with one identifier, e.g. “Milo in his mustard-yellow t-shirt.”

Concept 03

Keeping characters on-model portraits → illustrate

Here's the hard problem every AI picture book hits: text-only image models can't hold a character's identity across many stateless calls. Tiny wording shifts make the protagonist's face drift from page to page. Story Maker solves it in two stages.

Identity lock: portraits condition every scene render portraits (once/story) milo.pngmustard tee byte.pngteal-glow bot ms-rose.pngsage cardigan 1024×1024 identity anchors script.md · scene prompt (no faces) Nano BananaGemini Flash Image · ≤5 refs portraits as inlineData + "preserve identity" scene-01.pngon-model scene-05.pngon-model scene-10.pngon-model same faces across all 10 scenes → no drift
Each scene render is conditioned on the same canonical portraits — identity travels as pixels, not words.

/story:portraitscharacters/<slug>.png. Generates exactly one canonical 1024×1024 portrait per non-narrator character. The source is chosen in priority order: (1) a per-story photo in characters-source/, (2) a photo in the reusable central library ~/nsaf/data/story/characters/, (3) an explicit Photo Path, or (4) AI-generated via Nano Banana. Mixed casts work naturally — a real-photo kid can stand beside an AI-generated dragon. Runs once per story no matter how often you re-illustrate; ~$0.04 per AI portrait.

/story:illustrateimages/scene-NN.png. For each scene it figures out which characters are present, maps each to its portrait PNG, and calls Nano Banana with the scene's illustration prompt plus those portraits as reference images — up to 5 per scene (the model's limit; ties broken by who has dialogue, then prominence). It calls the REST API directly (the old CLI-plus-extension chain was the pipeline's most notorious failure) and classifies errors so a quota error stops instead of retrying for an hour; only a non-quota failure falls through to the Leonardo text-only path. Result: 10 scenes, ~$0.40 total, every character on-model.

Concept 04

Casting and speaking every line narrate

/story:narrate turns the [VOICE:…]-tagged script into per-scene audio, and its cleverness is in casting.

narrate: deterministic casting → loudness-matched audio cast.jsonage·gender·accent pick-voice.cjsmechanical + dedup voice-assignmentswritten back to outline real job (openai) narrator → alloymilo → echobyte → fablems-rose → shimmer per-segment TTSsplit by [VOICE:*] tags concat-scene-audioloudnorm −16 LUFS + fades scene-NN.mp3+ sidecar.jsonprovider-aware skip
Voices are assigned mechanically from age/gender/accent, deduped across the cast, then baked with matched loudness.

Deterministic, deduped cast assignment. It builds cast.json — one entry per character plus the narrator — and calls pick-voice.cjs cast, which assigns voices mechanically and spreads them across the cast so two similar characters don't collide. (An old bug gave two young boys the same OpenAI echo voice; cast-level dedup fixed it.) The resolved IDs are written back into the outline so re-runs are stable.

Loudness-matched assembly. Each scene's narration is split into ordered (voice, text) segments, each synthesized to its own mp3, then concatenated by concat-scene-audio.sh — which applies per-segment loudnorm to −16 LUFS + 25 ms fade-in + 50 ms fade-out and matched-rate silence between speakers. That one helper is why voices no longer clunk between different perceived loudness levels.

Provider-aware resumability Each scene-NN.mp3 gets a sidecar JSON recording provider, sample rate, and a hash of the voice assignments. A scene is skipped only if its sidecar matches the current provider and casting — otherwise the stale file is deleted and regenerated. This stops a leftover 24 kHz OpenAI clip from surviving an ElevenLabs re-render and silently corrupting the concat.

Concept 05

Assembling the film — and the book build → final.mp4, pdf

build: detect audio params, then concat title → scenes → credits ffprobe scene-01sample rate + channelscards MUST match or -c copy drops audio silently title card4 s · 0x1a1a2e scene clips ×10-loop png + mp3 -shortest credits3 s card concat.txt-f concat -c copy final.mp4title+10+credits~20.3 MB Each scene clip's length is driven by its own narration (-shortest) — pacing follows the voice. pdf is a separate leaf: same images + script, no audio, laid out as an 8.5×8.5 print book.
The audio-parameter probe is the subtle first step — mismatched cards silently drop the audio stream under -c copy.

/story:buildfinal.mp4. After a pre-flight that verifies every scene image has a matching scene audio, build does something subtle first: it detects the audio parameters (sample rate, channel layout) from scene-01.mp3 with ffprobe, because the title and credits cards must match those exactly — otherwise the -c copy concat silently drops the audio stream. Then it renders a title card, turns each scene-NN.png + scene-NN.mp3 into a clip whose length is driven by its narration (-shortest), renders a credits card, and concatenates title → scenes → credits into final.mp4.

/story:pdf<slug>-book.pdf. A leaf stage that needs only the script and the scene images (no audio). It lays out an 8.5×8.5 inch print book — cover, one page per scene (image on top, narration in a cream text zone rendered as real vector type, so no AI-misspelling risk), and a credits page — via headless Chrome. Cost is $0 because it reuses artifacts you already have.

Worked example

“The Boy Who Jumped Ahead,” end to end

Real job · story-output

Milo & Byte — a 10-scene watercolor book about ADHD

A boy whose racing brain leaps past the “steps,” and the patient AI tutor bot who follows his jumps instead of fighting them — until pizza slices click into fractions.

Scenes · characters
10 scenes · Milo, Byte, Ms. Rose, Narrator
Portraits
3 AI-generated (milo · byte · ms-rose)
Image provider
nano-banana · 1920×1080 · ~$0.40
TTS provider
openai tts-1-hd · 24 kHz
Voices
alloy · echo · fable · shimmer (no collision)
Output
final.mp4 ~20.3 MB · 1080p

The whole trip, in one line:

idea → concept.mdoutline.md (arc + Character Reference Sheet + scenes)
    → script.md ([VOICE] tags + illustration prompts)
    → characters/ (identity anchors)
    → images/audio/
    → final.mp4 (+ optional book.pdf)

That's Story Maker: eight small stages, one shared state file, a character sheet that keeps faces and voices consistent, and a pair of leaf stages that pour the same artifacts into a movie or a book.