ScreenPilot

Storyboard reference

The storyboard.json format: segments, holds, zooms, captions, highlights, narration, cards, overlays, music, extends, theme and output, with a full example.

A storyboard is a JSON file that describes the edit. The AI app writes it for you, but you can read it, change it and render it yourself. screenpilot schema (or the storyboard_schema tool) prints the authoritative JSON Schema. This page explains each field.

The three rules#

  • Times are source seconds. They count from the start of the raw recording, never from the rendered video. Cuts and speed changes do not move them. Only screenpilot check and the check_output tool use output seconds.
  • Positions are normalized 0..1. x runs from the left edge and y from the top edge of the recording. Read them from a frame with frames --grid.
  • Paths are relative to the storyboard file. This applies to source, events, extends, music.src and overlays[].src.

Every field except source is optional. Run screenpilot validate storyboard.json after each edit. It reports schema errors and warnings: overlapping segments, items past the end of the source, views that name an unknown screen, and callouts without x and y. It also gives style advice: see Style warnings.

Top-level fields#

FieldDefaultWhat it is
version1Format version. Always 1.
extendsnoneA base storyboard to take the look from. See Share a look with extends.
sourcerequiredPath to the raw recording.
eventsnoneevents.jsonl from record. It enables click ripples.
keepSourceAudiofalseKeep the audio of the recording. Segments can then set volume or mute.
output1920x1080 at 30 fpsSize and frame rate. See Output and voice.
themedefaultsThe look. See Theme.
voicedefaultsThe narration voice. See Output and voice.
segments[]Parts of the source to keep.
holds[]Freeze frames.
zooms[]Camera zooms.
screens, views[]Multi-monitor recordings. See Screens and views.
captions[]On-screen text.
highlights[]Boxes around parts of the screen.
narration[]Voiceover lines.
intro, outrononeTitle cards before and after the video.
cards[]Chapter cards in the middle of the video. See Chapter cards.
overlays[]Images, video and logos on top of the frame. See Overlays.
musicnoneBackground music, optionally ducked under the voice.

Segments#

segments lists the parts of the source to keep, in order. Anything between segments is cut. An empty list keeps the whole video. Segments must not overlap.

FieldDefaultMeaning
inrequiredStart, in source seconds.
outrequiredEnd. Must be greater than in.
speed1Playback speed, 0.25 to 16. Use 4 to 8 to fast-forward loading and waiting.
volume1Level of this segment's source audio, 0 to 4. Needs keepSourceAudio.
mutefalseSilence this segment's source audio. Same as volume 0.

A narration line placed inside a cut region is dropped, with a warning. volume and mute only matter when the source audio is kept: without keepSourceAudio: true and a recording that has sound, the render warns that they have no effect.

Holds#

holds freeze the video on one frame, to give a caption or a line room.

FieldMeaning
atSource time to freeze on.
durFreeze length in seconds.

You rarely need to write holds. When a narration line is longer than its slot, the render adds a hold automatically so lines never overlap. The result lists them as autoHolds.

Zooms#

FieldDefaultMeaning
atrequiredSource time at which the zoom is fully in.
durrequiredHow long to stay zoomed, in seconds.
x, yrequiredFocus point, 0..1.
scale1.8Zoom factor, 1 to 4.

Captions#

FieldDefaultMeaning
atrequiredSource time the caption appears.
dur3Seconds on screen.
textrequiredThe text. Use \n for a second line, for example a bilingual caption.
positionbottombottom, top or center.
stylepillpill, title or callout.
x, ynoneAnchor point for callout, 0..1.
motiontheme defaultHow this caption moves in and out. See Motion.

Caption styles#

  • pill: a subtitle-like box. Use it for most captions.
  • title: big text for a chapter-like moment.
  • callout: a bubble that points at x, y. Always give both. Without them the bubble points at the center and validate warns you.

Highlights#

A highlight draws a box around part of the screen.

FieldDefaultMeaning
atrequiredSource time it appears.
dur2Seconds on screen.
x, yrequiredTop-left corner, 0..1.
w, hrequiredWidth and height, 0..1.
labelnoneOptional text on the box.

Narration#

Each line is spoken by the storyboard's voice and mixed into the video. By default that is an Edge voice; see Voices for local models and your own programs. Narration never overlaps: when a line overruns its slot, the video freezes to make room.

FieldDefaultMeaning
atrequiredSource time the line starts.
textrequiredWhat the voice says.
voicevoice.nameVoice for this line only, for example ar-EG-ShakirNeural. It replaces voice.name and keeps voice.provider, so it must be a voice of the same provider. Use it to mix languages.
captionvoice.subtitlesAlso show this line as a subtitle (true) or not (false).

List the voices with screenpilot voices --lang ar-EG. Examples: ar-EG-SalmaNeural, ar-EG-ShakirNeural, en-US-AndrewNeural, en-US-AriaNeural.

Intro and outro#

intro and outro are title cards before and after the video. Both take the same fields.

FieldDefaultMeaning
titlerequiredMain text.
subtitlenoneSecond line.
dur3Seconds on screen.

Chapter cards#

cards are title cards in the middle of the video, for a new chapter. Each one freezes the picture at at and shows a full card with a fade in and out, then the video carries on from the same frame.

FieldDefaultMeaning
atrequiredSource time the video freezes and the card shows.
dur2.5Seconds the card stays. The video is frozen meanwhile.
titlerequiredMain text.
subtitlenoneSecond line.

A card whose at is in a cut region is dropped, and validate warns. Cards use the same look as the intro and outro (theme.motion.cards and the .card CSS).

Overlays#

overlays put an image or a video on top of the output frame: a logo, a watermark, a picture-in-picture clip. Unlike zooms and highlights, the position is a fraction of the output frame, not of the recording, so a logo stays in the same corner however the camera moves.

FieldDefaultMeaning
srcrequiredAn image or a video, relative to the storyboard.
x, yrequiredLeft and top edge, 0..1 of the output frame.
w, hrequiredWidth and height, 0..1 of the output frame. Must be above 0.
atnoneSource time it appears.
durnoneSeconds it stays. With at and no dur, it stays until the end. With neither at nor dur, it shows for the whole video.
fitcontaincontain shows the whole image inside the box. cover fills the box and trims the overflow.
opacity10 to 1.
radiusnoneCorner radius in output pixels.
shadownoneDraw a shadow under it.
motionfadeEntrance and exit. See Motion. words, lines and wipe fall back to fade.
mutetrueFor a video: false plays its sound too.
loopfalseFor a video: repeat it until the overlay ends.
"overlays": [
  { "src": "assets/logo.png", "x": 0.88, "y": 0.04, "w": 0.08, "h": 0.08, "opacity": 0.8 },
  { "src": "assets/webcam.mp4", "at": 5, "dur": 12, "x": 0.72, "y": 0.66, "w": 0.24, "h": 0.28, "fit": "cover", "radius": 16, "shadow": true }
]

Music#

FieldDefaultMeaning
srcrequiredPath to an audio file, relative to the storyboard.
volume0.120 to 1. Keep it low so it does not cover the voice.
ducknoneLowers the music while someone speaks. See below.

Ducking makes the music quieter under each narration line and brings it back after. Give duck any of these fields, or an empty object {} for the defaults:

FieldDefaultMeaning
amount0.6How much quieter the music gets, 0 to 1. 0.6 means 60% lower.
attack0.2Seconds the music takes to go down, finishing as the line starts. 0 to 5.
release0.5Seconds the music takes to come back after the line. 0 to 5.

Share a look with extends#

extends points at a base storyboard, relative to this file. Use it when several videos should look and sound the same: each one extends the same base and adds its own recording. It is how a feature clip takes the look of the demo it joins in a sequence.

  • Five fields are inherited: theme, voice, output, music and keepSourceAudio. Everything else (source, segments, zooms, captions, narration, intro, outro and the rest) is never inherited.
  • The child wins. Objects merge key by key, so a child that sets only theme.accent keeps the base's other theme fields. Arrays replace instead of merging, for example theme.fonts.
  • Chains work. The base can extend another storyboard. A cycle is an error that lists the chain.
  • Paths stay valid. music.src from the base is rewritten to point at the same file from the child's folder.
{
  "extends": "../demo.json",
  "source": "rec.mp4",
  "narration": [{ "at": 1, "text": "Invoices can now repeat on a schedule." }]
}

Screens and views#

For a recording of several monitors, screens says where each monitor sits in the recording and views says which part the camera shows from each time on. init --suggest fills both for you.

ItemFields
screens[]name (monitor name, for example eDP-1), x, y, w, h (0..1 of the source; w must be above 0)
views[]at, then exactly one of screen (a screens[].name, or "all") or rect (x, y, w, h, any region); optional label and transition (pan by default, or cut)

Output and voice#

ObjectFields and defaults
outputwidth 1920, height 1080, fps 30 (1 to 120)
voiceprovider edge (or local or command), name en-US-AndrewNeural, rate +0% (for example +10% or -5%), subtitles true (show narration lines as subtitles), gap 0.3 (minimum silence between lines; the video freezes to make room), volume 1 (0 to 4)
voice.providerWhat voice.name means
edge (default)An Edge voice, for example ar-EG-SalmaNeural. Needs the internet.
localA model on this computer: voicetut, voicetut:<Voice> such as voicetut:Sarah, or namaa. rate becomes a speed factor for voicetut; namaa ignores it.
commandThe key of a voice you defined in voices.json.

A narration line's own voice works inside the provider. For example, with "provider": "local", one line can use namaa and another voicetut:Sarah. Setup, limits and examples are on Voices.

Pick the output size from where the video will be shown: 1920x1080 for YouTube, a README or slides; 1080x1920 for Reels, TikTok and Shorts; 1080x1080 for feeds.

Theme#

The theme sets the look of the whole video. There is no house style: choose it for the audience, and take colors and type from the product you show.

FieldDefaultMeaning
backgroundindigo-to-blue gradientAny CSS background: a color, a gradient or an image.
padding0.05Space around the recording, as a fraction of the output width (0 to 0.2).
radius16Corner radius of the recording, 0 to 60.
shadowtrueShadow under the recording.
accent#facc15Accent color.
fontInter, Cairo, system-uiCSS font family.
captionColor#ffffffCaption text color.
captionBackgrounddark, translucentCaption box color.
clickRipplestrueDraw a ripple on every recorded click. Needs events.
fonts[]Extra Google Fonts to load, as css2 family specs such as "Tajawal:wght@400;700" or "Space Grotesk:wght@500;700". Then use them in font or css.
cssemptyYour own CSS, applied after the built-in styles.
motion{}Default text motion: captions (captions and subtitles) and cards (intro and outro).

Custom CSS#

The elements you can style with theme.css:

  • #root: the whole frame and its background.
  • #frame: the box that holds the recording.
  • .card, .card-inner, .card h1, .card p: the intro and outro cards.
  • .text: captions. Variants are .text.pill, .text.subtitle and .text.title. The visible box is .text.<style> > span, and .text .ln is one line of a multi-line caption.
  • .pos-bottom, .pos-top, .pos-center: caption positions.
  • .hl and .hl span: highlights.
  • .callout span: callout bubbles.
  • .ripple: click ripples.

Motion#

Motion controls how text enters and leaves. Set the default in theme.motion.captions and theme.motion.cards, and override it with motion on one caption. Every field is optional.

FieldMeaning
inEntrance preset.
outExit preset. words and lines exit like fade.
durationSeconds per entrance, 0 to 3. Default 0.3.
easeAny GSAP ease, for example "power3.out", "back.out(1.7)", "expo.out" or "elastic.out(1,0.5)".
staggerSeconds between words or lines for words and lines, 0 to 1. Default 0.08.

Presets: none, fade, rise, drop, slide-left, slide-right, pop, zoom, blur, wipe, words, lines. wipe uncovers text in the reading direction. words reveals one word at a time and lines one line at a time; both work in any script, Arabic included.

Calm tutorials suit fade and rise. Energetic social clips suit pop or words with a bouncy ease. Titles suit wipe or words.

A complete example#

This storyboard keeps one recording, speeds up a loading stretch, zooms on two clicks, adds three captions and a highlight, and speaks three lines (the last one in Arabic). The paths are relative to the storyboard file. It passes screenpilot validate; the warning you see without the files is only that the source video is missing.

{
  "version": 1,
  "source": "sessions/2026-10-27_14-02/video.mkv",
  "events": "sessions/2026-10-27_14-02/events.jsonl",
  "output": { "width": 1920, "height": 1080, "fps": 30 },
  "voice": { "name": "en-US-AndrewNeural", "rate": "+0%", "subtitles": false, "gap": 0.3 },
  "theme": {
    "background": "linear-gradient(135deg, #0f172a 0%, #1d4ed8 100%)",
    "padding": 0.05,
    "radius": 16,
    "accent": "#facc15",
    "font": "'Space Grotesk', 'Cairo', sans-serif",
    "fonts": ["Space Grotesk:wght@500;700"],
    "css": ".text.pill > span { letter-spacing: 0.01em; }",
    "motion": {
      "captions": { "in": "rise", "out": "fade", "duration": 0.35, "ease": "power3.out" },
      "cards": { "in": "words", "stagger": 0.08 }
    }
  },
  "intro": { "title": "Acme Invoices", "subtitle": "Send an invoice in 20 seconds", "dur": 3 },
  "outro": { "title": "Try it today", "subtitle": "acme.example", "dur": 3 },
  "segments": [
    { "in": 0, "out": 6, "speed": 1 },
    { "in": 6, "out": 19, "speed": 6 },
    { "in": 19, "out": 41, "speed": 1 }
  ],
  "holds": [{ "at": 30.5, "dur": 1.5 }],
  "zooms": [
    { "at": 2.5, "dur": 3, "x": 0.18, "y": 0.22, "scale": 2 },
    { "at": 24, "dur": 4, "x": 0.82, "y": 0.9, "scale": 1.8 }
  ],
  "captions": [
    { "at": 1, "dur": 3, "text": "Start from the dashboard", "position": "bottom", "style": "pill" },
    { "at": 22, "dur": 3, "text": "One click to send", "style": "callout", "x": 0.82, "y": 0.9 },
    { "at": 36, "dur": 3, "text": "Done", "position": "center", "style": "title", "motion": { "in": "pop", "ease": "back.out(1.7)" } }
  ],
  "highlights": [{ "at": 31, "dur": 2.5, "x": 0.3, "y": 0.4, "w": 0.3, "h": 0.12, "label": "Total" }],
  "narration": [
    { "at": 1, "text": "We start on the dashboard." },
    { "at": 22, "text": "One click and the invoice is sent." },
    { "at": 36, "text": "هذا كل شيء.", "voice": "ar-EG-SalmaNeural", "caption": true }
  ],
  "music": { "src": "assets/bgm.mp3", "volume": 0.12 }
}
The last narration line turns its own subtitle on with caption: true, while voice.subtitles is off for the others. The click near 24 s gets a zoom and a callout; the highlight sits on a different moment, so no single click carries every effect.

Style warnings#

validate (and the storyboard_validate tool) also warns when the storyboard breaks the style guidance below. These are warnings only: they never block a render. Each message names the item, for example captions[2].

WarningLimit
Narration too longMore than 12 words in a line.
Narration too fastMore than about 3 words per second before the next line. The render would freeze the video to fit.
Caption too longMore than 6 words.
Captions overlapTwo captions at the same position at the same time. Callouts are left out of this check.
Zooms overlapTwo zooms at the same time.
Zoom scaleOutside 1.4 to 2.5.
Stacked effectsA zoom, a highlight and a callout on the same point within one second.
Segment speedAbove 8x. Cut it instead.
Video lengthOutput over 90 seconds or under 15 seconds.
Cards in cut regionsA chapter card whose at is cut away.

Style guidance#

  • Narration: one sentence, under about 12 words, one idea. Say what the viewer sees or why it matters, not which button to press. Keep lines short: a long line freezes the video.
  • Zooms: zoom on what is about to be clicked. Start the zoom about 0.5 seconds before the click, hold it for 2 to 4 seconds, and use a scale of 1.6 to 2.2. Take x and y from a --grid frame. Return to the full view between zooms, and do not zoom when the whole screen matters.
  • Captions: 2 to 6 words, one idea each. Use callout only to point at one small element, title for chapter-like beats and pill for the rest. Do not repeat in a caption what the narration already says.
  • Idle time: cut it, or speed it up 4 to 8 times. Loading, waiting and mistakes do not belong at normal speed.
  • Pace: start a narration line together with the action it describes, and leave about 0.3 seconds between lines. Aim for 30 to 90 seconds in total.
  • Do not stack effects: a zoom, a highlight and a callout on one click is too much. Choose the one that points best.
  • Bilingual captions: use one caption with two lines ("English\nعربي"). Avoid a lone Arabic waw between Latin words; use an Arabic comma instead.
Work in passes. Write segments and narration first, render a draft with -q draft, look at it with check, then add zooms and captions. Change one kind of thing at a time.