From c8051ad8ce6ea3ed695b45c11934bf8de8722432 Mon Sep 17 00:00:00 2001 From: Abdelrahman Essawy Date: Tue, 25 Aug 2026 14:48:27 +0300 Subject: [PATCH 1/4] docs(compose): clip start and length are optional Auto-timing lands: a clip with no start and no length plays its whole source, laid after the clip before it and overlapped by any transition between them. - "Join two clips" leads with the untimed form and keeps the hand-timed one below, since both render the same thing. - New "Timing is optional" section under the timeline model, with the default for each field and the two places timing is still required. - Reference tables corrected: clip fields, scene duration, and overlays, which no longer need a length the engine was discarding. --- jobs/compose.mdx | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/jobs/compose.mdx b/jobs/compose.mdx index 7a0ffe4..f057759 100644 --- a/jobs/compose.mdx +++ b/jobs/compose.mdx @@ -145,13 +145,25 @@ curl -X POST https://api.rendobar.com/jobs \ **Assets:** video, image, text, audio, composition ยท **Output:** mp4 / webm / gif / mp3 (audio only) / jpg / png (still frame) -Without `trim`, a clip plays its source from the start for `length` seconds. To use a specific segment instead, add `trim: { from, to }` (in source seconds) to the asset. `length` is always the timeline duration. `trim` only chooses which part of the source fills it. +Without `trim`, a clip plays its source from the start for `length` seconds, or for the whole source when `length` is left off. To use a specific segment instead, add `trim: { from, to }` (in source seconds) to the asset. `length` is always the timeline duration. `trim` only chooses which part of the source fills it. 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. +Put a second clip on the same track and a transition between them. + +```json +"clips": [ + { "asset": { "type": "video", "src": "https://cdn.rendobar.com/assets/examples/river.mp4" } }, + { "type": "transition", "transition": "crossfade", "duration": 1 }, + { "asset": { "type": "video", "src": "https://cdn.rendobar.com/assets/examples/forest.mp4" } } +] +``` + +No timing anywhere. Each clip plays its whole source, the second follows the first, and the transition overlaps them by its own `duration`. You do not need to know how long either file is. + +Writing the timing out by hand gives the same render. The second clip starts `duration` seconds before the first ends, which is what the engine works out for you above. ```json "clips": [ @@ -190,10 +202,26 @@ Put a second clip on the same track and a transition between them. The clips ove 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. +- **Clips sit in time.** A clip can carry a `start` and a `length` in seconds, and it can leave both out. 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. +### Timing is optional + +Both fields default to something useful, so most timelines need neither. + +| Field | Omitted means | +| --- | --- | +| `length` | Play the whole source. With `trim`, the trimmed span. With `speed`, the source duration divided by the rate, so `speed: 0.5` on a 4 second clip occupies 8 seconds. On a `composition` clip, the nested timeline's own end | +| `start` | Begin where the previous clip on this track ends, pulled back by any transition between them. The first clip on a track starts at 0 | + +Two rules follow from that: + +- `length` is required on `text` and `image` clips. Neither has a source with a duration, so there is nothing to measure, and the submit is rejected with that message rather than rendering a guess. +- Inside a `composition`, both stay required. A nested timeline's length is set by the clip hosting it, so there is nothing to append to. + +Set either field where you want something other than the default: a gap before a clip, a deliberate overlap, or less than the whole source. Every timeline written with full timing keeps rendering exactly as it did. + ## Add a layer A second track renders over the first. This lays an animated title over the video. @@ -416,13 +444,13 @@ Tracks give you precise multi-track control. For a sequential edit, you can inst "clips": [{ "asset": { "type": "video", "src": "https://cdn.rendobar.com/assets/examples/forest.mp4" } }] } ], "overlays": [ - { "asset": { "type": "image", "src": "https://cdn.rendobar.com/assets/brand/logo-mark.png" }, "start": 0, "length": 7.4, + { "asset": { "type": "image", "src": "https://cdn.rendobar.com/assets/brand/logo-mark.png" }, "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. +A scene's clips can omit `start` and `length` to span the whole scene, and a scene can omit its own `duration` to take the length of the video inside it. Every clip field works the same as in tracks mode. `scenes` and `tracks` are mutually exclusive: a timeline uses one or the other. ## Edit like a pro @@ -505,7 +533,7 @@ A clip's `asset` is one of: ### Clip fields -Every clip has `asset`, `start`, `length`, plus any of: +Every clip has an `asset`, an optional `start` and `length` (see [Timing is optional](#timing-is-optional)), plus any of: | Field | What it does | | --- | --- | @@ -540,10 +568,10 @@ The scene-first shape, when you use `scenes` instead of `tracks`: | Field | Notes | | --- | --- | -| `scenes[].duration` | Seconds. Defaults to the longest clip in the scene | +| `scenes[].duration` | Seconds. Defaults to the longest clip in the scene, or to the length of the video inside it when its clips carry no timing either | | `scenes[].transition` | The transition INTO this scene from the previous one: `{ transition, duration, direction? }`, same kinds as the table above | | `scenes[].clips` | Clips as in tracks mode. `start` and `length` are optional and default to spanning the scene | -| `overlays` | Clips composited over the whole video (a logo, a watermark, a persistent title). `start` and `length` are required here | +| `overlays` | Clips composited over the whole video (a logo, a watermark, a persistent title). They always span the whole video, so `length` is ignored here and `start` defaults to 0 | ### Text style From f950b5891b7f17849879175747538cce0a64a334 Mon Sep 17 00:00:00 2001 From: Abdelrahman Essawy Date: Tue, 25 Aug 2026 14:58:20 +0300 Subject: [PATCH 2/4] docs(compose): document length "end" for clips that span the video --- jobs/compose.mdx | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/jobs/compose.mdx b/jobs/compose.mdx index f057759..1e3aa2f 100644 --- a/jobs/compose.mdx +++ b/jobs/compose.mdx @@ -213,14 +213,37 @@ Both fields default to something useful, so most timelines need neither. | Field | Omitted means | | --- | --- | | `length` | Play the whole source. With `trim`, the trimmed span. With `speed`, the source duration divided by the rate, so `speed: 0.5` on a 4 second clip occupies 8 seconds. On a `composition` clip, the nested timeline's own end | -| `start` | Begin where the previous clip on this track ends, pulled back by any transition between them. The first clip on a track starts at 0 | +| `start` | Begin after everything already placed on this track, pulled back by any transition immediately before it. The first clip on a track starts at 0 | -Two rules follow from that: +Set either field where you want something other than the default: a gap before a clip, a deliberate overlap, or less than the whole source. Every timeline written with full timing keeps rendering exactly as it did. -- `length` is required on `text` and `image` clips. Neither has a source with a duration, so there is nothing to measure, and the submit is rejected with that message rather than rendering a guess. -- Inside a `composition`, both stay required. A nested timeline's length is set by the clip hosting it, so there is nothing to append to. +### Running to the end of the video -Set either field where you want something other than the default: a gap before a clip, a deliberate overlap, or less than the whole source. Every timeline written with full timing keeps rendering exactly as it did. +Some clips should last as long as the finished video: a music bed, a watermark, a title that stays up. Their length is not their source's length (a 3 minute music track under a 20 second video) and working it out means totalling clips on a different track. Write `"end"` instead. + +```json +"tracks": [ + { "clips": [ + { "asset": { "type": "video", "src": "https://cdn.rendobar.com/assets/examples/river.mp4" } }, + { "asset": { "type": "video", "src": "https://cdn.rendobar.com/assets/examples/forest.mp4" } } + ] }, + { "clips": [ + { "asset": { "type": "audio", "src": "https://cdn.rendobar.com/assets/showcase/music.mp3", "volume": 0.35, "fadeOut": 2 }, + "length": "end" } + ] } +] +``` + +The music starts at 0 and stops on the last frame, whatever the clips above it add up to. Add a third clip later and it still does. + +An `"end"` clip measures against every clip that is not one, so two of them on different tracks both land on the real end rather than chasing each other. It runs to the last frame, so nothing may follow it on the same track, and a timeline made only of `"end"` clips has nothing to measure and is rejected. + +Inside a scene, the scene is the end, so `"end"` and an omitted `length` mean the same thing there. + +### Where a length is still required + +- `text` and `image` clips have no source with a duration, so they need a number or `"end"`. The submit is rejected with that message rather than rendering a guess. +- Inside a `composition`, `start` and `length` both stay required. A nested timeline's length is set by the clip hosting it, so there is nothing to append to. ## Add a layer @@ -533,7 +556,7 @@ A clip's `asset` is one of: ### Clip fields -Every clip has an `asset`, an optional `start` and `length` (see [Timing is optional](#timing-is-optional)), plus any of: +Every clip has an `asset`. `start` and `length` are optional, and `length` also accepts `"end"` (see [Timing is optional](#timing-is-optional)). Plus any of: | Field | What it does | | --- | --- | @@ -570,7 +593,7 @@ The scene-first shape, when you use `scenes` instead of `tracks`: | --- | --- | | `scenes[].duration` | Seconds. Defaults to the longest clip in the scene, or to the length of the video inside it when its clips carry no timing either | | `scenes[].transition` | The transition INTO this scene from the previous one: `{ transition, duration, direction? }`, same kinds as the table above | -| `scenes[].clips` | Clips as in tracks mode. `start` and `length` are optional and default to spanning the scene | +| `scenes[].clips` | Clips as in tracks mode. `start` and `length` are optional and default to spanning the scene, which is also what `length: "end"` means here | | `overlays` | Clips composited over the whole video (a logo, a watermark, a persistent title). They always span the whole video, so `length` is ignored here and `start` defaults to 0 | ### Text style From ba6cc75a46d4bf1f836277a75b90f555d10cb574 Mon Sep 17 00:00:00 2001 From: Abdelrahman Essawy Date: Tue, 25 Aug 2026 15:16:04 +0300 Subject: [PATCH 3/4] docs(compose): one rule for clip timing Rewrites the timing section around the container idea: a clip runs as long as its content does, never past its container, and "end" runs it to the container's end. Replaces four rules that disagreed (whole source on a track, whole scene in a scene, length required on text and images, overlay length discarded). Adds the music-bed and watermark worked examples and corrects the reference tables. --- jobs/compose.mdx | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/jobs/compose.mdx b/jobs/compose.mdx index 1e3aa2f..e3a7df6 100644 --- a/jobs/compose.mdx +++ b/jobs/compose.mdx @@ -208,18 +208,22 @@ So a multi-track timeline is layers (tracks) of sequences (clips). That is the w ### Timing is optional -Both fields default to something useful, so most timelines need neither. +Every clip sits in a **container**: the scene it belongs to, or the whole video. Two sentences cover every case. -| Field | Omitted means | -| --- | --- | -| `length` | Play the whole source. With `trim`, the trimmed span. With `speed`, the source duration divided by the rate, so `speed: 0.5` on a 4 second clip occupies 8 seconds. On a `composition` clip, the nested timeline's own end | -| `start` | Begin after everything already placed on this track, pulled back by any transition immediately before it. The first clip on a track starts at 0 | +- **Omit `length`** and the clip runs as long as its **content** does, never past its container. +- **Write `length: "end"`** and it runs to the end of its **container**, whatever its content says. + +Content means the source's duration, or the trim span, or the source duration divided by `speed` (so `speed: 0.5` on a 4 second clip occupies 8 seconds), or a composition's own end. + +Text and images have no duration of their own, so for them the two sentences say the same thing. A title with no `length` stays up for the whole video, which is what a watermark wants. + +`start` follows the same shape: omit it and the clip begins after everything already placed on its track, pulled back by any transition immediately before it. Give it a number and that is seconds from the start of its container. -Set either field where you want something other than the default: a gap before a clip, a deliberate overlap, or less than the whole source. Every timeline written with full timing keeps rendering exactly as it did. +Set either field wherever you want something else. Every timeline written with full timing keeps rendering exactly as it did. -### Running to the end of the video +### Worked examples -Some clips should last as long as the finished video: a music bed, a watermark, a title that stays up. Their length is not their source's length (a 3 minute music track under a 20 second video) and working it out means totalling clips on a different track. Write `"end"` instead. +A music bed. The source is three minutes long, the video is not, and `"end"` is how you say so without totalling the clips above it. ```json "tracks": [ @@ -234,16 +238,24 @@ Some clips should last as long as the finished video: a music bed, a watermark, ] ``` -The music starts at 0 and stops on the last frame, whatever the clips above it add up to. Add a third clip later and it still does. +A watermark. It has no duration of its own, so it needs nothing at all. + +```json +{ "clips": [ + { "asset": { "type": "image", "src": "https://cdn.rendobar.com/assets/brand/logo-mark.png" }, + "transform": { "scale": 0.15, "position": { "x": "88%", "y": "12%" } }, "opacity": 0.8 } +] } +``` + +Both stop on the last frame, whatever the clips elsewhere add up to. Add another clip later and they still do. -An `"end"` clip measures against every clip that is not one, so two of them on different tracks both land on the real end rather than chasing each other. It runs to the last frame, so nothing may follow it on the same track, and a timeline made only of `"end"` clips has nothing to measure and is rejected. +### The rules that fall out of it -Inside a scene, the scene is the end, so `"end"` and an omitted `length` mean the same thing there. +A clip running to the end of the **video** is measured against every clip that is not, so two of them on different tracks both land on the real end rather than chasing each other. It runs to the last frame, so nothing may follow it on the same track, and a timeline where nothing has a length of its own has no end to measure and is rejected. -### Where a length is still required +A clip never runs past its container, so a 6 second video in a 3 second scene plays 3 seconds, and a 2 second video in a 5 second scene plays 2. That applies to a length you wrote as well as one the engine worked out. -- `text` and `image` clips have no source with a duration, so they need a number or `"end"`. The submit is rejected with that message rather than rendering a guess. -- Inside a `composition`, `start` and `length` both stay required. A nested timeline's length is set by the clip hosting it, so there is nothing to append to. +Inside a `composition`, `start` and `length` both stay required. A nested timeline's length is set by the clip hosting it, so there is nothing to append to and no container to measure against. ## Add a layer @@ -591,10 +603,10 @@ The scene-first shape, when you use `scenes` instead of `tracks`: | Field | Notes | | --- | --- | -| `scenes[].duration` | Seconds. Defaults to the longest clip in the scene, or to the length of the video inside it when its clips carry no timing either | +| `scenes[].duration` | Seconds. Defaults to the longest thing in the scene, measured from its clips' own lengths or the sources inside them | | `scenes[].transition` | The transition INTO this scene from the previous one: `{ transition, duration, direction? }`, same kinds as the table above | -| `scenes[].clips` | Clips as in tracks mode. `start` and `length` are optional and default to spanning the scene, which is also what `length: "end"` means here | -| `overlays` | Clips composited over the whole video (a logo, a watermark, a persistent title). They always span the whole video, so `length` is ignored here and `start` defaults to 0 | +| `scenes[].clips` | Clips as in tracks mode. The scene is their container: an omitted `length` runs the clip as long as its source but never past the scene, and `length: "end"` spans the scene | +| `overlays` | Clips composited over the whole video (a logo, a watermark, a persistent title). The whole video is their container, so an omitted `length` spans it. `start` defaults to 0 | ### Text style From 6db55fa4480f4d3b3e5fd067c3843f443f89d71d Mon Sep 17 00:00:00 2001 From: Abdelrahman Essawy Date: Tue, 25 Aug 2026 15:33:38 +0300 Subject: [PATCH 4/4] docs(compose): note that "end" does not cap A clip is capped by its container, but "end" is the opposite request and asks for the container in full. On a source shorter than that it is impossible, and the job is rejected for outrunning its source. --- jobs/compose.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jobs/compose.mdx b/jobs/compose.mdx index e3a7df6..eb35326 100644 --- a/jobs/compose.mdx +++ b/jobs/compose.mdx @@ -255,6 +255,8 @@ A clip running to the end of the **video** is measured against every clip that i A clip never runs past its container, so a 6 second video in a 3 second scene plays 3 seconds, and a 2 second video in a 5 second scene plays 2. That applies to a length you wrote as well as one the engine worked out. +`"end"` is the opposite request and does not cap: it asks for the container's full extent whatever the source holds. On a source shorter than the container that is impossible, and the job is rejected for outrunning its source. Leave the length off instead, or trim less. + Inside a `composition`, `start` and `length` both stay required. A nested timeline's length is set by the clip hosting it, so there is nothing to append to and no container to measure against. ## Add a layer