Shot Sequence (voiceover-paced motion graphics)
Author a narrated motion-graphics video whose on-screen text and shapes are revealed in sync with the voiceover — each visual appears the moment the narration speaks its matching phrase. Three tools form the pipeline; an LLM (or you) authors a brief, and the server bakes word timings into exact frames.
Flow
generate_speech(script)→ a narration MP3 (job; readaudioUrlfrom the job output).forced_alignment(audio_url, transcript)→ per-word timings (job; readoutput_data.alignment). The transcript MUST be the exact narration script. Costs 3 credits.resolve_shot_sequence(brief, audio_url, alignment)→ a render-ready plan (synchronous; returns the plan inline + any warnings). No credits.render_shot_sequence(plan)→ an MP4 (job; progress + result render in the tool card, and it lands in your library). Costs 5 credits (standard render fee).
Authoring a brief
A brief has a narration (script + cues), and scenes → shots → reveals. A
cue is a contiguous phrase of the script; a reveal anchors an element to
a cue:
{
"fps": 30, "width": 1920, "height": 1080, "backgroundColor": "#0b0b0f",
"narration": {
"script": "Ship faster. Nodaro turns your idea into video.",
"cues": [
{ "id": "c_ship", "text": "Ship faster" },
{ "id": "c_idea", "text": "your idea into video" }
]
},
"scenes": [
{
"id": "scene_hook",
"shots": [{
"id": "shot_1",
"reveals": [
{
"id": "rv_title",
"element": { "id": "t1", "type": "text", "text": "Ship faster", "fontFamily": "Inter", "fontSize": 120, "fontWeight": 900, "color": "#ffffff", "x": 200, "y": 450 },
"revealAt": { "kind": "cue", "cueId": "c_ship", "edge": "start" },
"enter": { "motion": "slide-up", "durationFrames": 12, "easing": "easeOut" }
}
]
}]
}
]
}
Anchors
{ "kind": "cue", "cueId": "...", "edge": "start" | "end", "offsetMs"?: number }— reveal at the start (or end) of the cue’s spoken phrase, optionally nudged byoffsetMs.{ "kind": "frame", "frame": 0 }— a fixed frame, for non-narrated elements (an intro logo, an outro). For a frame-0 poster element, use"enter": { "motion": "none", "durationFrames": 0 }so it’s opaque on the thumbnail.
Element lifetime
An element unmounts at its scene’s content end — if you want it to persist visibly through the rest of the scene, set hold (frames to keep it on-screen after the entrance finishes) and/or exit (an exit motion that plays after hold).
Rules that keep it deterministic
- Cues must be whitespace-exact substrings of the script. Write spoken forms — “twenty twenty-six”, not “2026”; “five dollars”, not “$5”.
- Scene cue spans must not interleave. Each scene owns a contiguous time window; if two scenes’ reveals overlap in time, resolve returns
422 scene_overlap. Keep one scene’s reveals before the next scene’s. - Fonts must be one of the 20 supported families: Inter, Roboto, Open Sans, Montserrat, Poppins, Raleway, Nunito, Lato, Playfair Display, Merriweather, Lora, EB Garamond, Bebas Neue, Oswald, Anton, Dancing Script, Pacifico, Caveat, Roboto Mono, Fira Code.
- Entrance motions:
fade,scale-up,wipe-in,slide-up,slide-down,slide-left,slide-right,none. - Exit motions:
fade,slide-up,slide-down,slide-left,slide-right,none.
Blueprint catalog
A catalog of parameterised shot-shapes (“blueprints”) covers the most common beat
roles (15 at the time of writing — list_shot_shapes always returns the live set).
Use list_shot_shapes to browse the catalog and get_shot_shape to inspect a
blueprint’s exact param contract before writing a blueprint reveal in a brief.
Blueprints are declarative shot-shapes — most are text/shape only, and two
(device-surface-showcase, cursor-ui-demo) take your own uploaded screenshot
images as params. Either way they carry no pricing or credit information: the
standard render-video credit (5 credits) applies to the overall
render_shot_sequence call, not to individual blueprints.
| Id | Roles | Default duration (frames) | What it does |
|---|---|---|---|
comparison-split |
feature_showcase | 180 | Two labeled panels slide in from opposite sides and hold with a center divider; optional badges pop near the end. |
constellation-hub |
hook, social_proof | 180 | Labeled nodes spring into a ring around a center hub, then the shot resolves on the core — camera push-in or orbiting badges. |
cta-morph-press |
cta | 150 | A CTA button appears centered; a cursor decelerates in and presses it. |
cursor-ui-demo |
feature_showcase, product_intro | 210 | A brand cursor drives a screenshot UI through clicks while the viewport chases each target; screens swap per beat. |
dataviz-countup |
pain_point | 240 | A big number counts up to a value with a label; numbers are the hero. |
device-surface-showcase |
product_intro, feature_showcase | 240 | A device mockup holds as hero while its screens cycle through a real flow (camera-static static-tour); side headlines swap in sync. |
grid-card-assemble |
feature_showcase, benefit_highlight, social_proof | 180 | N text cards cascade-assemble into a grid with a staggered entrance. |
kinetic-type-beats |
hook | 150 | 1–4 statement lines swap in by hard-cut/scale-pop; final line spring-pops on an accent. |
logo-assemble-lockup |
product_intro, branding | 180 | Brand word’s letters cascade/assemble into a centered lockup (+ optional tagline). |
overwhelm-surround |
pain_point | 210 | Tool cards assemble, density chips scatter in, the center morphs to reveal the viewer, then demand bubbles close in from all sides. |
spatial-pan-stations |
hook, pain_point | 240 | Labeled stations on one oversized canvas, traversed by ease-in-out camera pans that pop a callout at each stop. |
ticker-takeover |
hook, branding | 180 | A typed lead-in with an accent word cycling options, then the hero crashes in and shoves the text aside. |
titlecard-reveal |
benefit_highlight, social_proof | 120 | One clean title (+ optional subtitle) revealed with one restrained move, then held. |
typewriter-reveal |
hook, branding | 180 | Text types in character-by-character with a blinking caret; optional sublabel fades up after typing finishes. |
waterfall-reveal |
hook, feature_showcase | 150 | Words of a line cut in one-by-one with a small horizontal slide, cascading left-to-right; optional sublabel fades up after the last word lands. |
list_shot_shapes()
Returns the full blueprint catalog as a JSON array. Each entry:
| Field | Type | Description |
|---|---|---|
id |
string | Blueprint identifier (kebab-case) |
roles |
string[] | Beat roles this blueprint suits (from the doctrine vocabulary) |
description |
string | One-line description |
defaultDurationFrames |
number | Default scene duration at 30 fps |
No inputs, no scope gate, no credits.
get_shot_shape(id)
Returns detailed information for one blueprint.
Inputs:
| Field | Type | Notes |
|---|---|---|
id |
string | Blueprint id, e.g. "titlecard-reveal". Call list_shot_shapes to browse all ids. |
Returns (JSON):
| Field | Type | Description |
|---|---|---|
id |
string | Blueprint id |
roles |
string[] | Beat roles |
description |
string | Description |
defaultDurationFrames |
number | Default duration |
paramSchema |
object | JSON Schema (OpenAPI 3) for the blueprint’s params object |
example |
object | Filled worked-params example ready to paste into a blueprint reveal |
Unknown id → error result (isError: true) with the list of known ids.
No scope gate, no credits.
Tool reference
forced_alignment(audio_url?, audio_asset_id?, transcript)
Returns a job_id; in hosts with interactive tool cards (claude.ai), the
alignment JSON renders inline in the tool card as it completes. Clients
without card support poll get_job; when complete, the alignment is in
output_data.alignment — an array of [{ word, start, end }] objects with
timings in seconds. Costs 3 credits.
Inputs:
| Field | Type | Notes |
|---|---|---|
audio_url |
string (URL) | HTTPS URL to the narration audio. Either audio_url or audio_asset_id is required. |
audio_asset_id |
string (UUID) | Asset ID of an existing audio file. Either audio_url or audio_asset_id is required. |
transcript |
string | The exact narration script; must match the audio word-for-word. |
Returns: { job_id: string }
resolve_shot_sequence(brief, audio_url, alignment)
Pure function (synchronous). Resolves the brief into a render-ready plan by:
- Matching each cue to its word timings (from alignment)
- Converting cue-based reveal anchors to exact frame numbers
- Validating scene temporal non-overlap
- Returning any warnings (e.g. unmatched cue fallback)
No credits.
Inputs:
| Field | Type | Notes |
|---|---|---|
brief |
object | The ShotSequenceBrief — narration, scenes, shots, reveals |
audio_url |
string (URL) | HTTPS URL to the narration audio (for reference; not re-fetched) |
alignment |
array | The [{ word, start, end }] array from forced_alignment output_data |
Returns:
{
"plan": { ... },
"warnings": [
"Cue 'c_id' not found in transcript; reveals fall back to proportional timing"
]
}
If scenes interleave in time, returns HTTP 422 scene_overlap.
render_shot_sequence(plan)
Renders the resolved plan into an MP4 video. Returns a job_id; in hosts
with interactive tool cards (claude.ai), progress and the finished video
render inline in the tool card. The MP4 is also saved to your Nodaro
library. Costs 5 credits (the standard render fee for Remotion videos).
Inputs:
| Field | Type | Notes |
|---|---|---|
plan |
object | The resolved plan from resolve_shot_sequence |
Returns: { job_id: string }
Example workflow
1. Author a narration script and brief
"Ship faster. Nodaro turns your idea into video."
(declare cues, scenes, shots, reveals, fonts, motions)
2. Call generate_speech(script) → get audioUrl
Wait for job to complete
3. Call forced_alignment(audioUrl, script) → get alignment
Wait for job; extract output_data.alignment
4. Call resolve_shot_sequence(brief, audioUrl, alignment) → get plan
Returns immediately; check warnings
5. Call render_shot_sequence(plan) → render
Wait for job; download MP4 from library