diff --git a/docs.json b/docs.json index dbe7218..21406c1 100644 --- a/docs.json +++ b/docs.json @@ -58,7 +58,44 @@ "pages": [ "jobs/ffmpeg", "jobs/ffprobe", - "jobs/compose", + { + "group": "Compose", + "icon": "film", + "expanded": true, + "root": "jobs/compose", + "pages": [ + "jobs/compose/timeline", + "jobs/compose/framing", + "jobs/compose/transitions", + "jobs/compose/motion", + "jobs/compose/text", + "jobs/compose/fonts", + "jobs/compose/shapes", + "jobs/compose/effects", + "jobs/compose/audio", + "jobs/compose/output", + "jobs/compose/craft", + { + "group": "Reference", + "icon": "table-list", + "pages": [ + "jobs/compose/reference/timeline", + "jobs/compose/reference/video", + "jobs/compose/reference/image", + "jobs/compose/reference/audio", + "jobs/compose/reference/text", + "jobs/compose/reference/shape", + "jobs/compose/reference/timer", + "jobs/compose/reference/subtitles", + "jobs/compose/reference/animation", + "jobs/compose/reference/composition", + "jobs/compose/reference/transition", + "jobs/compose/reference/output", + "jobs/compose/reference/effects" + ] + } + ] + }, "jobs/compress", { "group": "Captions", diff --git a/jobs/compose.mdx b/jobs/compose.mdx index 7a73390..1dced86 100644 --- a/jobs/compose.mdx +++ b/jobs/compose.mdx @@ -1,7 +1,7 @@ --- title: "Compose: render from a timeline" sidebarTitle: "Compose" -description: "Render a video from a declarative JSON timeline. Tracks, clips, transitions, text, and per-clip effects." +description: "Render a video from a declarative JSON timeline: tracks, clips, transitions, text, shapes, keyframes and effects." icon: "film" keywords: ["video compose api", "json timeline to video", "video transitions api", "video editing api", "compose"] canonical: "https://rendobar.com/docs/jobs/compose" @@ -15,7 +15,7 @@ canonical: "https://rendobar.com/docs/jobs/compose" "@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.", + "description": "Render a video from a declarative JSON timeline: tracks, clips, transitions, text, shapes, keyframes and effects.", "datePublished": "2026-06-21", "author": { "@type": "Organization", "@id": "https://rendobar.com/#organization" }, "publisher": { "@type": "Organization", "@id": "https://rendobar.com/#organization" } @@ -27,6 +27,7 @@ canonical: "https://rendobar.com/docs/jobs/compose" ## Your simplest render + The smallest timeline is one track with one clip. This renders the first three seconds of a video to MP4. The whole API call: @@ -140,7 +141,7 @@ 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) +**Assets:** video, image, audio, text, timer, subtitles, shape, animation (Lottie), 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, 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. @@ -149,6 +150,7 @@ That is the shape of every render. Everything below is one more clip, one more t ## Join two clips + Put a second clip on the same track and a transition between them. ```json @@ -171,682 +173,66 @@ Writing the timing out by hand gives the same render. The second clip starts `du ] ``` -`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 | - - - - - -## 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.** 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 - -Every clip sits in a **container**: the scene it belongs to, or the whole video. Two sentences cover every case. - -- **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 wherever you want something else. Every timeline written with full timing keeps rendering exactly as it did. - -### Worked examples - -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": [ - { "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" } - ] } -] -``` - -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. - -### The rules that fall out of it - -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. - -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 - -A second track renders over the first. This lays an animated title over the video. - -```json -"tracks": [ - { "clips": [ - { "asset": { "type": "video", "src": "https://cdn.rendobar.com/assets/examples/sea.mp4" }, "start": 0, "length": 3 } - ] }, - { "clips": [ - { "asset": { "type": "text", "text": "Coastal Escape", "style": { "font": "Inter", "size": 84, "color": "#FFFFFF" } }, - "start": 0, "length": 3 } - ] } -] -``` - -## 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 -{ - "asset": { "type": "video", "src": "https://cdn.rendobar.com/assets/examples/sea.mp4" }, - "start": 0, - "length": 3, - "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`. - - - - - -### 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. - - -