---
title: "Scenes"
canonical_url: https://docs.zvid.io/docs/structure/scenes/
source: docs/structure/scenes.md
content_revision: 6338386a1dd87e4a
---

# 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:

```json
{
  "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](#global-overlay-layer)).

If you provide scene IDs, make each one unique within the project. Reusing an
ID such as `rest` in two scenes produces a validation error at the duplicate
scene's `id` field before the job is queued. Omit IDs when you do not need to
refer to scenes by name; Zvid generates IDs without colliding with the IDs you
provided.

## 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: 0` is
  "the start of this scene."
- Reordering, adding, or removing a scene never breaks the timing of the others.
- Per-scene `backgroundColor` and auto-fitting `duration` remove most manual
  bookkeeping.
- Scene-to-scene transitions reuse the same [xfade effects](https://docs.zvid.io/docs/structure/properties/xfade-effects/)
  as video transitions.

## Scene Interface

The interface describes scene rendering fields. For authoring, scenes can
also declare `variables`, `iterate`, `iterateAs`, and `condition`; see
[Dynamic content](https://docs.zvid.io/docs/templates/dynamic-content/).

```typescript
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`.                    |

> **Resolution and frame rate are project-wide**
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 `duration` is 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 `exitEnd` they do **not** constrain the scene.
  - If nothing constrains the scene, it falls back to a default of **10
    seconds**.

```json
{
  "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`.

The example above is a **scene fragment**, placed in `payload.scenes`.
Hosted project `durationMode: "auto"` needs explicit media bounds so the API
can calculate plan limits and credits without probing source files. Stored
video templates require positive resolved scene durations. For those paths,
set `videoEnd`/`exitEnd` or an explicit scene `duration` as appropriate, and
read [Timing](https://docs.zvid.io/docs/concepts/timing/) before submitting.

## Transitions Between Scenes

Scenes always play in **array order**. A transition blends the current scene
into the one that follows it:

```json
{
  "scenes": [
    {
      "id": "intro",
      "duration": 4,
      "transition": "fade",
      "transitionDuration": 0.8,
      "visuals": []
    },
    { "id": "main", "duration": 5, "visuals": [] }
  ]
}
```

Key rules:

- `transition` is the [xfade effect](https://docs.zvid.io/docs/structure/properties/xfade-effects/) into the
  next scene. `null` (the default) is a hard cut.
- A transition **overlaps** the two scenes: the next scene starts
  `transitionDuration` seconds before the current one ends, exactly like
  [video transitions](https://docs.zvid.io/docs/structure/transitions/).
- `transitionDuration` defaults to `0.5` and must be shorter than both adjacent
  scene durations.
- `transitionId` is 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 be `null` /
  `"none"`); pointing it anywhere else disables the transition and falls back to
  a hard cut.
- A `transition` on 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:

```json
{
  "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 scene sequence length is the sum of scene durations minus transition
overlaps. With fixed/default project timing, `duration` interacts with that total:

- If `duration` is omitted, the video is exactly the scenes' total length.
- If `duration` is **shorter** than the scenes' total, it is extended to fit the
  scenes (a warning is logged).
- If `duration` is **longer** than the scenes' total, the video is extended to
  `duration`; the global overlay layer keeps playing over the final frame.

With `durationMode: "auto"`, timed global visuals, audio, and captions can
also extend the project. `duration` is a minimum. See
[Automatic timing](https://docs.zvid.io/docs/concepts/timing/) for the complete calculation.

## Complete Example


Three scenes, two transitions, a global watermark and music bed — the payload below, rendered by Zvid

[Watch rendered example](https://cdn.zvid.io/library/docs/scenes-demo.mp4)


A three-scene promo: a title built with an [HTML element](https://docs.zvid.io/docs/structure/text-elements/), a
background video clip, and an outro — with cross-scene transitions and a global
watermark.

```json
{
  "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: 0` inside a scene over large
  global offsets.
- Use `duration: -1` for video/audio-driven scenes and an explicit `duration`
  for text-only scenes.
- Reach for scenes when sections have distinct backgrounds or you want clean
  cross-section transitions; reach for a single `visuals` timeline when elements
  overlap continuously.
- Put persistent branding (logo, watermark, music) on the **project** level so
  it spans every scene.
- Keep `transitionDuration` well under the shorter of the two scenes it joins.

## Related Pages

- [JSON Structure Overview](https://docs.zvid.io/docs/structure/)
- [Text / HTML Elements](https://docs.zvid.io/docs/structure/text-elements/)
- [Video Transitions](https://docs.zvid.io/docs/structure/transitions/)
- [Animation Effects](https://docs.zvid.io/docs/structure/animations/)
- [XFadeEffect](https://docs.zvid.io/docs/structure/properties/xfade-effects/)
