# What is a film?

> The film block and page transitions: how a multi-page template renders as one video with scenes, cuts, music and cross-page layers. Schema and limits.

- **URL**: https://orshot.com/docs/definitions/film

---

A **film** is a template whose pages play one after another as a single video. Each page is a **scene**, each scene after the first can have a **transition** from the previous one, and **tracks** carry music or layers across scenes. The template stores two things for this:

- `film`: a template-level object. Its presence makes the template a film.
- `transitionIn`: an optional object on each page, describing how that page starts.

Everything else about the template stays the same: pages, elements, [animations](https://orshot.com/docs/orshot-studio/animations), [motion](https://orshot.com/docs/orshot-studio/motion), [audio and subtitles](https://orshot.com/docs/orshot-studio/audio-subtitles) and parameters work per scene exactly as they do on a normal page. Times inside a scene (`showAt`, keyframes, reveals) count from that scene's own start.

In Studio, films are made with [Film mode](https://orshot.com/docs/orshot-studio/film-mode). Through the API, send `film` and `transitionIn` with [Create Template](https://orshot.com/docs/api-reference/enterprise-template-create) or [Update Template](https://orshot.com/docs/api-reference/enterprise-template-update).

```json
{
  "name": "Launch film",
  "canvas_width": 1080,
  "canvas_height": 1920,
  "film": { "v": 1, "background": "#17171C", "output": { "profile": "film" } },
  "pages_data": [
    { "id": "hook", "videoDuration": 3.2, "canvas": { "width": 1080, "height": 1920, "backgroundColor": "#17171C" }, "elements": [] },
    { "id": "proof", "videoDuration": 4, "transitionIn": { "type": "whip", "direction": "left", "duration": 0.6 }, "elements": [] },
    { "id": "end", "videoDuration": 3, "transitionIn": { "type": "grow", "from": "stamp" }, "elements": [] }
  ]
}
```

## When a video is a film

| Video | Structure |
| --- | --- |
| A quick animation: one idea, up to 8 seconds (a logo sting, an animated post, a loop) | One page, no `film` |
| A carousel: each page is posted as its own slide | Separate pages, no `film`. Rendered page by page |
| Everything else: longer than 8 seconds, more than one scene, or one deliverable video | A film: one page per scene, 2 to 8 seconds each, plus `film` |

A single page longer than 8 seconds is flagged by Studio and by the API's design checks; past 15 seconds it is always flagged. One long page with layers appearing and disappearing in time windows is hard to edit and keeps no clean thumbnail per scene. Scenes can be reordered, swapped and retimed on their own.

## The `film` object

| Field | Type | Description |
| --- | --- | --- |
| `v` | Number | Required. Always `1` |
| `background` | String | A CSS colour shown under the scenes while a transition reveals part of the frame. 1 to 100 characters; the characters `< > { } ; " ' \` are refused. Default: page 1's canvas `backgroundColor`, else `#000000` |
| `output` | Object | Encoding: `{ "profile": "default" \| "film", "fps": 24 \| 25 \| 30 }`. `profile: "film"` uses a slower x264 preset at constant quality (CRF 18) with 192 kbps AAC: a higher quality tier that is usually a much smaller file. `fps` sets the film's frame rate. A render's `videoOptions.fps` wins when you send it. Films render at 30 fps at most. Default: the normal video encode at 30 fps |
| `tracks` | Array | Up to 8 [tracks](#tracks-and-clips) that run across scenes |
| `combine` | Boolean | Accepted for forward compatibility. A film always renders as one video today, so the value has no effect |
| `poster` | Object | `{ "page": "<page id>", "at": <seconds from that scene's start> }`. Validated (the page must exist; a moment inside the scene's incoming transition is a warning). Reserved: renders and thumbnails do not read it yet |

Unknown keys are kept and reported as warnings with a did-you-mean suggestion. Studio's Film mode switch writes `{ "v": 1, "output": { "profile": "film" } }`.

Orshot may add preview fields to the block (`thumbnail_url`, `thumbnail_meta`, `video_thumbnail_url`, `video_thumbnail_meta`). They are derived, never part of the design; send them back unchanged or leave them out.

## Page `transitionIn`

Set on a page in `pages_data`. It describes how that page starts from the previous one; page 1 has nothing to start from, so its `transitionIn` is ignored with a warning. No `transitionIn` (or `null`) is a hard cut.

```json
{ "type": "colorFlood", "duration": 0.8, "color": "#FF4D2E", "origin": { "x": 0.5, "y": 0.72 } }
```

| Field | Type | Description |
| --- | --- | --- |
| `type` | String | One of the [transition types](#transition-types). `"none"` is a hard cut (used with `overlay`) |
| `duration` | Number | Seconds, 0 to 3. Default per type (table below). A value under 0.05 plays as 0.05. A transition may take at most half of either neighbouring scene's length |
| `ease` | String | 1 to 120 characters. GSAP names (`power1` to `power4`, `expo`, `sine`, `circ`, `back`, each with `.in`, `.out` or `.inOut`; `none`, `linear`) or `cubic-bezier(a, b, c, d)`. Unknown names play as `power2.inOut` |
| `direction` | String | `left`, `right`, `up` or `down`. Used by `slide`, `push`, `wipe`, `flip`, `cube` and `whip`. Default `left` |
| `color` | String | A CSS colour (same rules as `film.background`). Used by `dip` and `colorFlood` |
| `origin` | Object | `{ "x": 0..1, "y": 0..1 }` as a fraction of the frame. Used by `iris`, `clockWipe`, `zoom` and `colorFlood`. Default the centre `{ 0.5, 0.5 }` |
| `from` | String | `grow` only, required: the id of the layer on the **previous** page that grows into this page |
| `target` | String | `zoomThrough` only, required: the id of the layer on the previous page the camera dives into |
| `pairs` | Array | `match` only, up to 24 `{ "from": "<id on the previous page>", "to": "<id on this page>" }`. Default: layers that have the same id on both pages |
| `others` | String | `match` only. What the unpaired layers do: `"clear"` (default) fades the old ones out first and the new ones in last; `"dissolve"` crossfades everything |
| `reveal` | String | `match` only. How this page appears around the morph: `"iris"` (default) opens a circle from behind the paired layer; `"fade"` crossfades |
| `overlay` | Object | An effect on a hard cut: `{ "type": "flash" \| "leak", "duration": <s, up to 3>, "color": <CSS colour> }`. Defaults: flash 0.25 s white, leak 0.4 s `#ffb36b`. An overlay never changes timing |

### Transition types

Overlapping transitions: the previous scene keeps playing while this one starts, and the film is shorter by the transition's length.

| Type | Default | Effect |
| --- | --- | --- |
| `fade` | 0.9 s, `power1.inOut` | The two pages blend into each other |
| `dip` | 1.0 s, `#000000` | Fades to `color`, then into this page |
| `slide` | 0.9 s, left | This page slides in over the previous one |
| `push` | 0.9 s, left | This page pushes the previous one out |
| `wipe` | 0.9 s, left | An edge sweeps across to show this page |
| `iris` | 1.0 s | A circle opens from `origin` |
| `clockWipe` | 1.0 s | A clock hand sweeps around `origin` |
| `flip` | 1.0 s, left | The page flips over like a card |
| `cube` | 1.0 s, left | The pages turn like the sides of a cube |
| `whip` | 0.6 s, left, `power3.inOut` | A fast, blurred pan into this page |
| `blurSwap` | 1.0 s | The previous page blurs out as this one blurs in |
| `zoom` | 1.0 s | Zooms into this page from `origin` |
| `colorFlood` | 1.1 s, `#111111` | `color` floods the frame from `origin`, then clears to this page |

Continuity transitions: a layer of the previous page carries the cut.

| Type | Default | Effect |
| --- | --- | --- |
| `grow` | 1.2 s, `power3.inOut` | The layer named by `from` grows to fill the frame and becomes this page. Needs a visible box; if the layer is gone it plays as `zoom` |
| `zoomThrough` | 1.0 s, `power3.in` | The camera moves through the layer named by `target` into this page. Falls back to `zoom` the same way |
| `match` | 1.1 s, `power3.inOut` | Each pair moves from its place on the previous page to its place on this page while the rest of the page is revealed (`reveal`) and the unpaired layers hand over (`others`) |

Cut overlays (`type: "none"` plus `overlay`): `flash` peaks on the cut (a short rise before, a longer fall after); `leak` sweeps a warm light across the frame around the cut.

### Transition rules

Errors block the save; warnings come back in the response.

- `type` must be a known type; `direction` one of the four directions. Unknown values fail with a did-you-mean suggestion.
- A cut has either a `type` or an `overlay`, not both (error).
- `duration` over half of the shorter neighbouring scene is an error, so a 0.8 s transition needs scenes of at least 1.6 s on both sides.
- `grow` without `from` and `zoomThrough` without `target` are errors; the named layer must exist on the previous page.
- `match`: every `pairs[].from` must exist on the previous page and every `pairs[].to` on this page. Without `pairs`, at least one layer id should appear on both pages (warning). A matched layer that also has its own entrance (`wake`, `reveal`, `typewriter` or `transitions.enter`) plays both at once (warning: drop the entrance).
- `pairs`, `others` and `reveal` on a non-`match` type are ignored (warning).
- Unknown keys are ignored (warning).

## Timeline

Nothing about timing is stored; it is derived from the pages and the film block:

- A scene lasts its page's `videoDuration`. Without one, a still page holds for 2 seconds. A page with animations lasts until they end, and a page with a video lasts as long as the video.
- A transition overlaps the end of one scene with the start of the next, so the film's total length is the scenes added up minus each transition's length. Scene 2 starts at `scene1.end - transition.duration`.
- Studio shows the overlaps as striped parts of the timeline.

## Tracks and clips

`film.tracks` holds things that run across scenes. A track is `{ "id", "kind": "audio" | "layers", "name"?, "clips": [...] }`, with up to 8 tracks and up to 400 clips per track. Later `layers` tracks draw above earlier ones. Track and clip ids must be unique.

A clip's window on the film:

| Field | Type | Description |
| --- | --- | --- |
| `id` | String | Required, unique |
| `name` | String | Optional label (Studio shows it on the timeline) |
| `start` | Number | Absolute film seconds. Ignored when `anchor` is set |
| `anchor` | Object | `{ "page": "<page id>", "edge": "start" \| "end", "offset": <s> }`. The clip starts at that moment and follows the scene when scenes move. Default edge `start`, offset 0 |
| `duration` | Number | Seconds from the clip's start |
| `until` | Object | `{ "page", "edge", "offset" }` or `{ "edge": "filmEnd", "offset" }`. Wins over `duration`. With neither, the clip runs to the end of the film |

Audio clips (`kind: "audio"`): `src` (required), `volume` 0 to 2, `fadeIn`, `fadeOut`, `trimStart` (seconds into the file), `loop`.

```json
{ "id": "music", "kind": "audio", "clips": [
  { "id": "bed", "src": "https://cdn.example.com/bed.mp3", "start": 0, "until": { "edge": "filmEnd" }, "volume": 0.6, "fadeOut": 1.5, "loop": true }
] }
```

Layers clips (`kind: "layers"`): `elements` (required, at least one, the same schema as page elements). Times inside the clip count from the clip's start. Use them for a logo, a progress bar or a background video that keeps playing under the cuts.

```json
{ "id": "brand", "kind": "layers", "clips": [
  { "id": "logo", "anchor": { "page": "proof" }, "until": { "edge": "filmEnd" }, "elements": [ { "id": "logo-mark", "type": "image", "content": "https://..." } ] }
] }
```

Rules: an anchor or `until` must name an existing page; a clip that ends before it starts is an error; page `audioTracks` (narration per scene) still play inside each scene.

## Limits

| Limit | Value |
| --- | --- |
| Scenes | 400 |
| Film length | 1,800 s, and your plan's maximum video length |
| Tracks | 8, with 400 clips each |
| Transition or overlay duration | 3 s, and at most half of either neighbouring scene |
| `match` pairs | 24 |
| Audio sources mixed into one film (page tracks, video layers with sound, audio clips) | 64; the rest are dropped with a warning |
| Canvas | Every scene has the same canvas size |

## How a film renders

- A whole-template render with `response.format: "mp4"` returns **one video**: `data` is a single object (like a single-page template) with `content` and `videoDuration` set to the film's length. See [Render as a video](https://orshot.com/docs/api-reference/render-from-studio-template#render-a-film).
- `includePages` renders the listed pages one by one, as for any multi-page template. Other video formats (`webm`, `mov`, `mkv`, `gif`) also render page by page. Image and PDF formats are unchanged.
- `videoOptions.muted` silences the film; `videoOptions.fps` sets the frame rate and wins over `film.output.fps`. A film never renders above 30 fps. `combinePages`, `pageTransition`, `duration`, `audioSource` and the subtitle options do not apply to a film.
- Audio: each scene's `audioTracks`, unmuted video layers and the film's audio clips are mixed into one soundtrack with a limiter. Captions are not burned into films yet, and audio waveform layers are not animated inside films; both are reported as render warnings.
- `size` renders the film at another size, and `extraSizes` adds up to 4 more sizes per call. Each size is a full film render and is billed as one. Per-page size maps are refused: a film has one canvas.
- Motion follows the resized layers, so 9:16 to 1:1 or 4:5 work well; 9:16 to 16:9 gives a centred column, so design 16:9 separately.

## Validation on write

[Create Template](https://orshot.com/docs/api-reference/enterprise-template-create) and [Update Template](https://orshot.com/docs/api-reference/enterprise-template-update) validate `film` and every `transitionIn` together with the pages they refer to. Shape and limit problems, missing layers and bad timings fail with `400` and `"error": "Validation failed"`; `details` lists each problem by path, e.g. `pages.2.transitionIn.from: No layer "stamp" on page 2 (the previous page).` Soft problems come back in the response `warnings`. On an update, problems that were already in the stored template never block an unrelated change; they are listed as `pre-existing (not blocking)` warnings. A `film` value that is not an object or `null` fails with the code `invalid_film`.

[Update Pages](https://orshot.com/docs/api-reference/enterprise-update-pages) re-checks the stored film against the pages after the operations and refuses, with the code `invalid_film`, only operations that would newly break it. Studio saves check the block's shape and limits the same way (`Invalid film`, code `invalid_film`).

## Related

- [Film mode in Studio](https://orshot.com/docs/orshot-studio/film-mode)
- [Motion](https://orshot.com/docs/orshot-studio/motion) for keyframes, reveals, counters and groups inside a scene
- [Template Anatomy](https://orshot.com/docs/definitions/template-anatomy)
- [Render from Studio Template](https://orshot.com/docs/api-reference/render-from-studio-template)