> ## Documentation Index
> Fetch the complete documentation index at: https://rendobar-docs-compose-headings.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Compose: render from a timeline

> Render a video from a declarative JSON timeline. Tracks, clips, transitions, text, and per-clip effects, in one API call.

<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{
__html: JSON.stringify({
  "@context": "https://schema.org",
  "@type": "TechArticle",
  "@id": "https://rendobar.com/docs/jobs/compose/#article",
  "headline": "Compose: render from a timeline",
  "description": "Render a video from a declarative JSON timeline. Tracks, clips, transitions, text, and per-clip effects, in one API call.",
  "datePublished": "2026-06-21",
  "dateModified": "2026-06-22",
  "author": { "@type": "Organization", "@id": "https://rendobar.com/#organization" },
  "publisher": { "@type": "Organization", "@id": "https://rendobar.com/#organization" },
  "isPartOf": { "@id": "https://rendobar.com/#website" }
})
}}
/>

`compose` renders a video from a JSON timeline. Start with one clip, then layer up: more clips, transitions, overlays, and effects. This page builds from the smallest render to the full model. Each step adds one idea.

## Your simplest render

The smallest timeline is one track with one clip. This trims three seconds of a video and renders it to MP4. The whole API call:

<CodeGroup>
  ```ts SDK theme={null}
  import { createClient, outputUrl } from "@rendobar/sdk";

  const client = createClient({ apiKey: "rb_YOUR_KEY" });

  const job = await client.jobs.create({
    type: "compose",
    params: {
      schemaVersion: 1,
      output: { format: "mp4", resolution: { width: 1280, height: 720 }, fps: 30 },
      timeline: {
        tracks: [
          { clips: [
            { asset: { type: "video", src: "https://cdn.rendobar.com/assets/examples/a.mp4", trim: { from: 0, to: 3 } }, start: 0, length: 3 },
          ] },
        ],
      },
    },
  });

  const result = await client.jobs.wait(job.id);
  console.log(outputUrl(result));
  ```

  ```python Python theme={null}
  import requests

  res = requests.post(
      "https://api.rendobar.com/jobs",
      headers={"Authorization": "Bearer rb_YOUR_KEY"},
      json={
          "type": "compose",
          "params": {
              "schemaVersion": 1,
              "output": {"format": "mp4", "resolution": {"width": 1280, "height": 720}, "fps": 30},
              "timeline": {
                  "tracks": [
                      {"clips": [
                          {"asset": {"type": "video", "src": "https://cdn.rendobar.com/assets/examples/a.mp4", "trim": {"from": 0, "to": 3}}, "start": 0, "length": 3}
                      ]}
                  ]
              },
          },
      },
  )
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.rendobar.com/jobs \
    -H "Authorization: Bearer rb_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "type": "compose",
      "params": {
        "schemaVersion": 1,
        "output": { "format": "mp4", "resolution": { "width": 1280, "height": 720 }, "fps": 30 },
        "timeline": {
          "tracks": [
            { "clips": [
              { "asset": { "type": "video", "src": "https://cdn.rendobar.com/assets/examples/a.mp4", "trim": { "from": 0, "to": 3 } }, "start": 0, "length": 3 }
            ] }
          ]
        }
      }
    }'
  ```
</CodeGroup>

<Info>
  **Assets:** video, image, text, audio, composition · **Output:** mp4 / webm / gif / mp3 (audio only) / jpg / png (still frame)
</Info>

That is the shape of every render. Everything below is one more clip, one more track, or one more field.

## Join two clips

Put a second clip on the same track and a transition between them. The clips overlap by the transition's `duration`, so the second clip starts a little before the first ends.

```json theme={null}
"clips": [
  { "asset": { "type": "video", "src": "a.mp4" }, "start": 0, "length": 3 },
  { "type": "transition", "transition": "crossfade", "duration": 1 },
  { "asset": { "type": "video", "src": "b.mp4" }, "start": 2, "length": 3 }
]
```

`slide` also accepts `direction: "left" | "right" | "up" | "down"`.

| Transition  | Effect                                        |
| ----------- | --------------------------------------------- |
| `crossfade` | A dissolves directly into B                   |
| `fade`      | A dips through black, then B fades up         |
| `slide`     | B slides in over A from an edge (directional) |
| `zoom`      | B grows in from the center                    |

<Columns cols={2}>
  <Frame caption="crossfade">
    <video src="https://cdn.rendobar.com/assets/showcase/transitions/crossfade.mp4" autoPlay loop muted playsInline />
  </Frame>

  <Frame caption="fade">
    <video src="https://cdn.rendobar.com/assets/showcase/transitions/fade.mp4" autoPlay loop muted playsInline />
  </Frame>

  <Frame caption="slide (left)">
    <video src="https://cdn.rendobar.com/assets/showcase/transitions/slide-left.mp4" autoPlay loop muted playsInline />
  </Frame>

  <Frame caption="zoom">
    <video src="https://cdn.rendobar.com/assets/showcase/transitions/zoom.mp4" autoPlay loop muted playsInline />
  </Frame>
</Columns>

## How the timeline works

The model is two ideas:

* **Tracks stack.** The first track is the background. Each later track composites on top of the ones below it: overlays, titles, picture-in-picture.
* **Clips sit in time.** Each clip has a `start` and a `length` in seconds. Two clips on the same track are joined by a transition. A gap between them plays nothing.

So a multi-track timeline is layers (tracks) of sequences (clips). That is the whole structure.

## Add a layer

A second track renders over the first. This lays an animated title over the video.

```json theme={null}
"tracks": [
  { "clips": [
    { "asset": { "type": "video", "src": "a.mp4" }, "start": 0, "length": 5 }
  ] },
  { "clips": [
    { "asset": { "type": "text", "text": "Coastal Escape", "style": { "size": 84 } },
      "start": 0, "length": 5 }
  ] }
]
```

## Effects on a clip

Any clip takes effects as fields. Add one field at a time, on the clip it applies to.

### Color grade

`color` grades a clip. Set `contrast`, `saturation`, `temperature`, `brightness`, and `gamma` to set a cinematic mood, or push them to lift flat, hazy footage.

```json theme={null}
"color": { "contrast": 1.12, "saturation": 1.2, "temperature": 6800, "gamma": 0.95 }
```

<Frame caption="Cinematic color grade to set a mood">
  <video src="https://cdn.rendobar.com/assets/showcase/fx/color-grade.mp4" autoPlay loop muted playsInline />
</Frame>

### Filters

`filter` applies a one-shot look: `greyscale`, `negative`, `boost`, `muted`, `lighten`, `darken`, or `contrast`.

```json theme={null}
"filter": "greyscale"
```

### Transform and Ken Burns

`transform` positions and scales a clip. Add `animateTo` and it animates from the start values to the end values over the clip, the Ken Burns move that brings a still photo to life.

```json theme={null}
"transform": {
  "scale": 1.1,
  "position": { "x": "38%", "y": "48%" },
  "animateTo": { "scale": 1.32, "position": { "x": "62%", "y": "52%" } }
}
```

<Frame caption="Bring a still photo to life for slideshows and docs">
  <video src="https://cdn.rendobar.com/assets/showcase/fx/kenburns.mp4" autoPlay loop muted playsInline />
</Frame>

### Picture-in-picture

Scale a clip down and position it on an overlay track to put a webcam over your content, for reactions and screen-shares.

```json theme={null}
"transform": { "scale": 0.3, "position": { "x": "80%", "y": "75%" } }
```

### Speed

`speed` is a multiplier. Below `1` slows a clip down for a dramatic action beat, above `1` speeds a slow process into a timelapse.

```json theme={null}
"speed": 0.4
```

### Chroma key

`chromaKey` removes a solid background color so you can drop a presenter onto any scene. Put the keyed clip on an overlay track above the new background.

```json theme={null}
"chromaKey": { "color": "#66DD09", "similarity": 0.28 }
```

<Frame caption="Put a presenter on any background (chroma key)">
  <video src="https://cdn.rendobar.com/assets/showcase/fx/chroma.mp4" autoPlay loop muted playsInline />
</Frame>

### Blend modes

`blendMode` controls how an overlay composites with the track below: `screen`, `multiply`, `overlay`, `add`, and more. A light-leak clip over your footage with `screen` adds a cinematic glow.

```json theme={null}
"blendMode": "screen",
"opacity": 0.7
```

### Opacity and blur

`opacity` takes a number or keyframes. Keyframe it from `0` to `1` and back for a clean fade in and out. `blur` softens a clip, useful as a backdrop that keeps overlaid text readable.

```json theme={null}
"opacity": [
  { "time": 0, "value": 0 },
  { "time": 0.6, "value": 1 },
  { "time": 2.4, "value": 1 },
  { "time": 3, "value": 0 }
]
```

## Text and titles

A `text` asset renders a styled title. Set the `font`, `size`, `weight`, `color`, `position`, and an `animate` entrance (`fade`, `slideUp`, `slideDown`, `slideLeft`, `slideRight`). Put one text clip on its own track for a title card, another low and left for a lower-third name tag.

```json theme={null}
"asset": {
  "type": "text",
  "text": "Coastal Escape",
  "style": {
    "font": "Inter",
    "size": 84,
    "weight": 800,
    "color": "#FFFFFF",
    "position": { "x": "50%", "y": "38%" },
    "animate": { "type": "slideUp", "duration": 0.7 }
  }
}
```

<Frame caption="Add a title card and a lower-third name tag (text)">
  <video src="https://cdn.rendobar.com/assets/showcase/fx/title-lowerthird.mp4" autoPlay loop muted playsInline />
</Frame>

## Set the canvas

`output` controls the resolution, frame rate, and format. A `9:16` canvas with a `crop` on the clip turns landscape footage into a vertical Reel that fills the frame, no black bars.

```json theme={null}
"output": { "format": "mp4", "resolution": { "width": 1080, "height": 1920 }, "fps": 30 }
```

<Frame caption="Landscape footage reframed to a vertical 9:16 Reel (crop)">
  <video src="https://cdn.rendobar.com/assets/showcase/fx/reframe.mp4" autoPlay loop muted playsInline style={{ maxWidth: "300px" }} />
</Frame>

## Scene-first authoring

Tracks give you precise multi-track control. For a sequential edit, you can instead describe a list of scenes. Each scene is a self-contained segment with its own clips and an optional transition into the next. `overlays` is a flat list of clips that sit over the whole composition, like a persistent logo or watermark.

```json theme={null}
{
  "scenes": [
    { "duration": 4, "transition": { "transition": "crossfade", "duration": 0.6 },
      "clips": [{ "asset": { "type": "video", "src": "a.mp4" } }] },
    { "duration": 4, "clips": [{ "asset": { "type": "video", "src": "b.mp4" } }] }
  ],
  "overlays": [
    { "asset": { "type": "image", "src": "logo.png" }, "start": 0, "length": 8,
      "transform": { "scale": 0.2, "position": { "x": "88%", "y": "12%" } } }
  ]
}
```

A scene's clips can omit `start` and `length` to span the whole scene. Every clip field works the same as in tracks mode. `scenes` and `tracks` are mutually exclusive: a timeline uses one or the other.

## Reference

### Output

The `output` object controls the render target.

| Field        | Values                                                                                    |
| ------------ | ----------------------------------------------------------------------------------------- |
| `format`     | `mp4` (default), `webm`, `gif` (no audio), `mp3` (audio only), `jpg`, `png` (still frame) |
| `resolution` | `{ width, height }` (integer pixels)                                                      |
| `fps`        | number                                                                                    |
| `videoCodec` | `h264`, `vp9` (optional; per-format default otherwise)                                    |
| `audioCodec` | `aac`, `opus`, `mp3` (optional)                                                           |
| `frameTime`  | second of the timeline to capture for `jpg` / `png` (default `0`)                         |

### Assets

A clip's `asset` is one of:

| `type`        | Fields                                                                     |
| ------------- | -------------------------------------------------------------------------- |
| `video`       | `src`, `trim` `{ from, to }`, `volume` (default `1`)                       |
| `image`       | `src`                                                                      |
| `audio`       | `src`, `trim`, `volume`, `fadeIn`, `fadeOut` (seconds)                     |
| `text`        | `text`, `style`                                                            |
| `composition` | `timeline`, a nested `{ tracks }` rendered and composited as a single clip |

### Clip fields

Every clip has `asset`, `start`, `length`, plus any of:

| Field              | What it does                                                                                                                          |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `opacity`          | `0`..`1`, or keyframes `[{ time, value, easing: step \| linear \| smooth }]`                                                          |
| `transform`        | `position {x,y}`, `scale`, `rotate`, `anchor` (`center` \| `topLeft`), `fit` (`contain` \| `fill`), `animateTo` (Ken Burns end state) |
| `crop`             | `top` / `right` / `bottom` / `left`, each a percent string like `"10%"`                                                               |
| `speed`            | playback rate (`0.5` = slow motion, `2` = fast, and pitch shifts with speed)                                                          |
| `color`            | `brightness` (-1..1), `contrast` (0..4), `saturation` (0..3), `gamma` (0.1..10), `hue` (-180..180), `temperature` (1000..40000 K)     |
| `filter`           | `greyscale`, `negative`, `boost`, `muted`, `lighten`, `darken`, `contrast` (mutually exclusive with `color`)                          |
| `blur` / `sharpen` | gaussian blur strength / unsharp amount                                                                                               |
| `chromaKey`        | `color` (hex), `similarity` (0..1)                                                                                                    |
| `blendMode`        | `normal`, `multiply`, `screen`, `overlay`, `darken`, `lighten`, `add`, `difference`                                                   |
| `pan`              | stereo balance, `-1` (left) to `1` (right)                                                                                            |
| `flip`             | `{ horizontal, vertical }`                                                                                                            |

### Transitions

A track item `{ "type": "transition", "transition": <kind>, "duration": <seconds>, "direction"?: <dir> }`:

`crossfade`, `fade`, `slide` (`direction`: `left` / `right` / `up` / `down`), `zoom`, `none` (hard cut).

### Text style

Fields on a text asset's `style`:

`font`, `size`, `weight` (100..1000), `color`, `align` (`left` / `center` / `right`), `position {x,y}`, `stroke {color,width}`, `background`, `shadow {color,opacity,offsetX,offsetY}`, `animate {type: fade \| slideUp \| slideDown \| slideLeft \| slideRight, duration}`.
