> ## Documentation Index
> Fetch the complete documentation index at: https://rendobar-docs-compose-ref-tables.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 controls />
  </Frame>

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

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

  <Frame caption="zoom">
    <video src="https://cdn.rendobar.com/assets/showcase/transitions/zoom.mp4" autoPlay loop muted playsInline controls />
  </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

Effects are fields on a clip. Stack as many as you like on one clip. Each example below shows the source on the left and the rendered result on the right. Exact fields and ranges are in the [Reference](#clip-fields).

```json theme={null}
{
  "asset": { "type": "video", "src": "a.mp4" },
  "start": 0,
  "length": 5,
  "color": { "saturation": 1.2, "temperature": 6800 },
  "speed": 0.8,
  "transform": { "scale": 1.1, "animateTo": { "scale": 1.3 } }
}
```

### Color and filters

`color` grades a clip with `contrast`, `saturation`, `temperature`, `brightness`, `gamma`, and `hue`. `filter` applies a one-shot look like `greyscale` or `boost`.

<Columns cols={2}>
  <Frame caption="Cinematic color grade for mood (color)">
    <video src="https://cdn.rendobar.com/assets/showcase/fx/color-grade.mp4" autoPlay loop muted playsInline controls />
  </Frame>

  <Frame caption="Lift flat, hazy footage (color)">
    <video src="https://cdn.rendobar.com/assets/showcase/fx/social-pop.mp4" autoPlay loop muted playsInline controls />
  </Frame>

  <Frame caption="Dramatic black and white (filter: greyscale)">
    <video src="https://cdn.rendobar.com/assets/showcase/fx/black-white.mp4" autoPlay loop muted playsInline controls />
  </Frame>
</Columns>

### Motion and speed

`transform` positions, scales, and rotates a clip. Add `animateTo` for a Ken Burns move, or scale a clip down and place it on an overlay track for picture-in-picture. `speed` is a playback multiplier: below `1` for slow motion, above `1` for a timelapse.

<Columns cols={2}>
  <Frame caption="Ken Burns: bring a still photo to life (transform.animateTo)">
    <video src="https://cdn.rendobar.com/assets/showcase/fx/kenburns.mp4" autoPlay loop muted playsInline controls />
  </Frame>

  <Frame caption="Picture-in-picture webcam (transform.scale)">
    <video src="https://cdn.rendobar.com/assets/showcase/fx/pip-webcam.mp4" autoPlay loop muted playsInline controls />
  </Frame>

  <Frame caption="Slow motion (speed: 0.4)">
    <video src="https://cdn.rendobar.com/assets/showcase/fx/slowmo.mp4" autoPlay loop muted playsInline controls />
  </Frame>

  <Frame caption="Timelapse (speed: 3)">
    <video src="https://cdn.rendobar.com/assets/showcase/fx/timelapse.mp4" autoPlay loop muted playsInline controls />
  </Frame>
</Columns>

### Compositing

`chromaKey` keys out a solid background color so you can drop a presenter onto any scene. `blendMode` controls how an overlay blends with the track below. `opacity` (a number or keyframes) and `blur` handle fades and soft backdrops.

<Columns cols={2}>
  <Frame caption="Chroma key onto any background (chromaKey)">
    <video src="https://cdn.rendobar.com/assets/showcase/fx/chroma.mp4" autoPlay loop muted playsInline controls />
  </Frame>

  <Frame caption="Cinematic light-leak glow (blendMode: screen)">
    <video src="https://cdn.rendobar.com/assets/showcase/fx/light-leak.mp4" autoPlay loop muted playsInline controls />
  </Frame>

  <Frame caption="Clean fade in and out (opacity keyframes)">
    <video src="https://cdn.rendobar.com/assets/showcase/fx/fade-in-out.mp4" autoPlay loop muted playsInline controls />
  </Frame>

  <Frame caption="Blurred backdrop keeps text readable (blur)">
    <video src="https://cdn.rendobar.com/assets/showcase/fx/blur-text.mp4" autoPlay loop muted playsInline controls />
  </Frame>
</Columns>

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

Place between two clips on a track: `{ "type": "transition", "transition": <kind>, "duration": <seconds>, "direction"?: <dir> }`.

| Transition  | Effect                                                        |
| ----------- | ------------------------------------------------------------- |
| `crossfade` | A dissolves directly into B                                   |
| `fade`      | A dips through black, then B fades up                         |
| `slide`     | B slides in from an edge (`direction`: left, right, up, down) |
| `zoom`      | B grows in from the center                                    |
| `none`      | Hard cut, no blend                                            |

### Text style

Fields on a text asset's `style`:

| Field        | Notes                                                                        |
| ------------ | ---------------------------------------------------------------------------- |
| `font`       | Font family name                                                             |
| `size`       | Point size                                                                   |
| `weight`     | 100..1000 (default 400)                                                      |
| `color`      | Hex fill                                                                     |
| `align`      | `left`, `center`, `right`                                                    |
| `position`   | `{ x, y }` as percent strings                                                |
| `stroke`     | `{ color, width }` outline                                                   |
| `background` | Hex fill behind the text                                                     |
| `shadow`     | `{ color, opacity, offsetX, offsetY }`                                       |
| `animate`    | `{ type, duration }` (type: fade, slideUp, slideDown, slideLeft, slideRight) |
