Scenes
Scenes split a project into a sequence of self-contained segments that play one
after another. Each scene has its own local timeline (where 0 is the start
of that scene), its own elements, and an optional transition into the next
scene. Zvid renders every scene independently and then stitches them together,
so scenes are the easiest way to build multi-part videos — intros, chapters,
product shots, outros — without hand-computing global timestamps.
Add scenes with the project-level scenes array:
{
"name": "my-video",
"resolution": "hd",
"scenes": [
{ "id": "intro", "duration": 4, "visuals": [] },
{ "id": "main", "duration": -1, "visuals": [] },
{ "id": "outro", "duration": 3, "visuals": [] }
]
}
When scenes is present and non-empty, it drives the timeline. The project-level
visuals and audios still render, but as a global overlay that spans the
whole video (see Global overlay layer).
Why scenes?
Without scenes, every element shares one global timeline, so a clip that starts
at second 12 needs enterBegin: 12, and inserting a new section means shifting
every timestamp after it. With scenes:
- Each scene's elements use times relative to that scene, so
enterBegin: 0is "the start of this scene." - Reordering, adding, or removing a scene never breaks the timing of the others.
- Per-scene
backgroundColorand auto-fittingdurationremove most manual bookkeeping. - Scene-to-scene transitions reuse the same xfade effects as video transitions.
Scene Interface
interface Scene {
id?: string;
visuals?: Item[];
audios?: AudioItem[];
duration?: number;
transition?: XFadeEffect | null;
transitionId?: string | null;
transitionDuration?: number;
backgroundColor?: string;
}
| Property | Type | Default | Notes |
|---|---|---|---|
id | string | scene-<index> | Unique scene id. Auto-generated from the array index when omitted. Duplicate ids are rejected. |
visuals | Item[] | [] | Visual elements on the scene-local timeline (0 = scene start). |
audios | AudioItem[] | [] | Audio tracks on the scene-local timeline. |
duration | number | -1 (auto) | Scene length in seconds. -1 (or omitted) auto-fits the scene to its content. |
transition | XFadeEffect | null | null | Effect blending into the next scene. null is a hard cut. |
transitionId | string | null | next scene | Id of the next scene. Optional; scenes always play in array order (see below). |
transitionDuration | number | 0.5 (when transition set) | Overlap length in seconds. Must be shorter than both adjacent scene durations. |
backgroundColor | string | project backgroundColor | Per-scene background. Falls back to the project background, then #ffffff. |
Scenes never set their own resolution, width, height, or frameRate.
Every scene is rendered on the parent project's canvas so all intermediates
match. Set those on the project.
Scene Duration
Each scene resolves to a concrete length before rendering:
- A positive
durationis used as-is. duration: -1(or omitted) auto-fits the scene: Zvid uses the latest end time among the scene's elements.- Videos and audios without an explicit end contribute their intrinsic (probed) length.
- Images, text/HTML, SVG, and GIF elements stretch or loop to any length, so
without an explicit
exitEndthey do not constrain the scene. - If nothing constrains the scene, it falls back to a default of 10 seconds.
{
"id": "hero-clip",
"duration": -1,
"visuals": [
{
"type": "VIDEO",
"src": "https://videos.pexels.com/video-files/1409899/1409899-sd_640_360_25fps.mp4",
"resize": "cover",
"position": "center-center",
"volume": 0
}
]
}
Here the scene auto-fits to the clip's length. To pin an auto-fit scene to a
text element, give that element an explicit exitEnd.
Transitions Between Scenes
Scenes always play in array order. A transition blends the current scene into the one that follows it:
{
"scenes": [
{
"id": "intro",
"duration": 4,
"transition": "fade",
"transitionDuration": 0.8,
"visuals": []
},
{ "id": "main", "duration": 5, "visuals": [] }
]
}
Key rules:
transitionis the xfade effect into the next scene.null(the default) is a hard cut.- A transition overlaps the two scenes: the next scene starts
transitionDurationseconds before the current one ends, exactly like video transitions. transitionDurationdefaults to0.5and must be shorter than both adjacent scene durations.transitionIdis optional. Because scenes play in array order, you may omit it. If you do set it, it must match the next scene's id (or benull/"none"); pointing it anywhere else disables the transition and falls back to a hard cut.- A
transitionon the last scene is ignored (there is nothing to blend into).
Global overlay layer
Project-level visuals and audios still apply when scenes are used. They are
composited on top of the stitched scenes and span the entire video —
ideal for a persistent watermark, logo, or background music bed:
{
"name": "branded-video",
"resolution": "hd",
"scenes": [
{ "id": "a", "duration": 3, "visuals": [] },
{ "id": "b", "duration": 3, "visuals": [] }
],
"visuals": [
{
"type": "TEXT",
"html": "<div class=\"wm\">© zvid</div>",
"position": "bottom-right",
"opacity": 0.6,
"customCode": {
"css": ".wm { color: #ffffff; font-family: Montserrat; font-size: 32px; }"
}
}
],
"audios": [
{
"src": "https://cdn.pixabay.com/audio/2026/02/24/audio_3f375fdf97.mp3",
"volume": 0.2
}
]
}
Scene elements always render below every global visual, so overlays stay on
top regardless of track values inside scenes.
Total Duration
The video length is the sum of all scene durations minus their transition
overlaps. The project duration field interacts with that total:
- If
durationis omitted, the video is exactly the scenes' total length. - If
durationis shorter than the scenes' total, it is extended to fit the scenes (a warning is logged). - If
durationis longer than the scenes' total, the video is extended toduration; the global overlay layer keeps playing over the final frame.
Complete Example
A three-scene promo: a title built with an HTML element, a background video clip, and an outro — with cross-scene transitions and a global watermark.
{
"name": "scenes-demo",
"resolution": "hd",
"frameRate": 30,
"backgroundColor": "#000000",
"outputFormat": "mp4",
"scenes": [
{
"id": "intro",
"duration": 4,
"backgroundColor": "#1a1a2e",
"transition": "fade",
"transitionId": "clip",
"transitionDuration": 0.8,
"visuals": [
{
"type": "TEXT",
"html": "<div class=\"title\">Welcome</div>",
"position": "center-center",
"enterBegin": 0.5,
"exitEnd": 4,
"customCode": {
"css": ".title { color: #ffffff; font-family: Montserrat; font-size: 96px; font-weight: 800; } .title { animation: rise 0.8s ease-out both; } @keyframes rise { from { opacity: 0; transform: translateY(40px); } to { opacity: 1; transform: translateY(0); } }"
}
}
]
},
{
"id": "clip",
"duration": -1,
"transition": "slideleft",
"transitionId": "outro",
"transitionDuration": 0.6,
"visuals": [
{
"type": "VIDEO",
"src": "https://www.pexels.com/download/video/4927963/",
"resize": "cover",
"position": "center-center",
"anchor": "center-center",
"volume": 0,
"videoBegin": 0,
"videoEnd": 5
}
]
},
{
"id": "outro",
"duration": 3,
"backgroundColor": "#16213e",
"visuals": [
{
"type": "TEXT",
"html": "<div class=\"thanks\">Thanks for watching</div>",
"position": "center-center",
"customCode": {
"css": ".thanks { color: #f3efa2; font-family: Montserrat; font-size: 72px; font-weight: 800; }"
}
}
]
}
],
"visuals": [
{
"type": "TEXT",
"text": "© zvid",
"position": "bottom-right",
"track": 5,
"opacity": 0.6,
"style": {
"fontFamily": "Montserrat",
"fontSize": "32px",
"color": "#ffffff"
}
}
],
"audios": [
{
"src": "https://cdn.pixabay.com/audio/2026/02/24/audio_3f375fdf97.mp3",
"volume": 0.2
}
]
}
Best Practices
- Keep timings scene-local: prefer
enterBegin: 0inside a scene over large global offsets. - Use
duration: -1for video/audio-driven scenes and an explicitdurationfor text-only scenes. - Reach for scenes when sections have distinct backgrounds or you want clean
cross-section transitions; reach for a single
visualstimeline when elements overlap continuously. - Put persistent branding (logo, watermark, music) on the project level so it spans every scene.
- Keep
transitionDurationwell under the shorter of the two scenes it joins.