Skip to main content
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 renders the first three seconds of a video to MP4. The whole API call:
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, 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.
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.
slide also accepts direction: "left" | "right" | "up" | "down".

crossfade

fade

slide (left)

zoom

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.
A watermark. It has no duration of its own, so it needs nothing at all.
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.

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.

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.

Cinematic color grade for mood (color)

Lift flat, hazy footage (color)

Dramatic black and white (filter: greyscale)

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.

Picture-in-picture webcam (transform.scale)

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.

Chroma key onto any background (chromaKey)

Cinematic light-leak glow (blendMode: screen)

Clean fade in and out (opacity keyframes)

Blurred backdrop keeps text readable (blur)

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. font takes any Google Fonts family by name, or a family from your own file. See Fonts.

Add a title card and a lower-third name tag (text)

Shapes

A shape asset draws a rect, ellipse, circle, triangle, diamond, or any SVG path you paste in. No image to make, no file to host, and it stays sharp at any output resolution.
That is a bar across the lower third, dark enough to read a title on and thin enough to see the footage through. Put it on a track below the text.

a bar, plus a text track

animateTo on the width

radius with a shadow

a circle, two colours

a stroke with no fill

pasted path data

Only type and size are required. Everything else on this page is optional, and the defaults are chosen so the common cases need almost none of it. Two things to know up front. position is the centre of the shape unless you set anchor, so a full-width bar sits at x: "50%" whatever its height. And there is no length, because a shape has no duration of its own: like text and images it runs to the end of its container unless you give it one. width is a percent of the frame’s width, height a percent of its height.

Leave the height out to get a circle

Omit size.height and the shape is made square in pixels. That is the only way to get a true circle or square on a frame that is not square, and it is the one thing about shapes worth memorising. A 10% by 10% box on a 16:9 frame is 192 by 108 pixels. Rounding it fully gives a pill, not a circle. Omit the height and the engine derives 17.8% for you.
shape: "circle" is ellipse with that rule applied, so it rejects an explicit height rather than quietly ignoring one. The same omission works on every kind: a rect with no height is a square, a diamond with no height is a regular one.

Anchor

anchor: "topLeft" points position at the corner instead of the centre. It is what makes letterbox bars arithmetic-free.

Fills, flat and gradient

color takes #RRGGBB, or the 8-digit #RRGGBBAA for a constant translucency the way #0A0A0ACC does above. Do not reach for the clip’s opacity instead. opacity dims the fill as well as fading it, so a half-opacity white box renders grey rather than white you can see through. Keep opacity for animated fades. Give color two colours and it is a gradient. gradient names the direction: vertical (the default), horizontal, diagonal or radial.
The gradient maps to the shape, not the frame, so a small badge shows the whole ramp instead of a sliver of one. Stops take the 8-digit form too, which is how you get a scrim that fades to nothing: ["#0A0A0A00", "#0A0A0AFF"].

Outlines, with a real hole

stroke draws a border just inside the shape’s edge, its width in pixels of the output frame. Give the shape a color too and you get a filled shape with a border; radius rounds both together. Leave color out and only the outline draws. What it surrounds is genuinely transparent, not filled with black, so the footage shows through the middle. That works on every kind except a path.

Rounded corners

radius runs from 0 (square) to 1 (fully round) on a rect. At 1 a wide box becomes a pill and a square one becomes a circle.

Drop shadows

shadow follows the shape, including its outline, its rotation and its animation. Every field has a default, so "shadow": {} is a sensible shadow.

Rotation turns about the shape, not the frame

rotate is degrees clockwise about the shape’s own centre, so a shape spins where it sits rather than orbiting the middle of the frame. A diagonal rule is a thin rect and one field:
There is no line type for the same reason there is no square type: a line is a rect with a small height, and rotate is what makes it diagonal.

Softness

softness runs 0 to 1 and feathers the edge. At 0.2 to 0.4 an ellipse reads as a spotlight or a vignette rather than a hard-edged disc. A full-frame ellipse with softness: 0.5 and a radial ["#00000000", "#000000CC"] fill is a vignette with no image asset at all.

Paths

shape: "path" takes SVG path data straight out of a design tool. viewBox gives the coordinate space it was drawn in, defaulting to "0 0 100 100", and the path is scaled into size so the same data works at any size on any frame.
M L H V C S Q T Z are supported, absolute and relative. Elliptical arcs (A) are refused rather than approximated, so re-export with arcs converted to curves. A path takes a fill, a gradient, a shadow, rotation and softness, but not a stroke: outlining arbitrary path data needs true path offsetting.

Animation

animateTo moves position, size, rotate, color and radius over the clip. easing is linear by default, which is what a progress bar or a timer wants, or smooth for an entrance.
A dimension you leave out of animateTo keeps its starting value, so the bar above grows sideways without changing height. A shape that is square in pixels stays square as it grows, so an animated circle does not turn into an ellipse halfway through. A solid colour can animate into a gradient, and a square can round itself off with animateTo.radius. An animated rotate has no limit: rotation is baked into the shape’s own outline, so animating from 0 to 720 is two full turns.

Combinations the API refuses

Two engine primitives draw shapes and they have different abilities, so a few combinations are rejected at submit time rather than rendered wrong. Each error names the fix.

What else a shape takes

blendMode mixes it with the layer below, blur softens the whole thing, colour grading tints it, and opacity keyframes fade it in and out. A track holding only shapes composites as a base layer, so a solid colour background is a timeline that references no files at all. There is no arbitrary path stroke and no per-corner radius. Anything past that is still an image asset.

Fonts

Name any Google Fonts family in style.font and it is fetched at render time. Nothing to upload, no configuration.
Seven families are bundled into the render image and resolve instantly, with no fetch: Inter, Roboto, Montserrat, Poppins, Oswald, Anton, Bebas Neue.

Your own font files

Put the file URLs in the job’s fonts input, each under a family name you choose, then reference that name in style.font.
There is no limit on how many you declare. TTF, OTF, TTC, WOFF and WOFF2 all work, and an uploaded asset’s content URL works in src like any other input URL. Naming your own font is the point. The alternative is to reference the family name buried inside the file, which you have to discover with a font inspector and which is frequently not what the file is called. Omit family and that embedded name is read for you, so { "src": "..." } on its own is also valid. A family you declare shadows a bundled one of the same name, exactly the way an @font-face rule beats a system font in CSS. Declare a font called Inter and yours is the one that renders. For a single file, inputs.font is the shorthand. It is addressed as custom:

Fallback stacks

style.font takes a CSS-style list, and it falls through in order:
This is a failure policy, not just a preference. If your CDN is unreachable or a Google family cannot be fetched, the render continues with the next entry instead of failing the job or silently substituting whatever the system happens to have. Every substitution is reported on the job:
A value naming nothing resolvable is rejected at submit, which catches the common mistake of pasting a font URL into style.font instead of into fonts.

Weights and languages

weight runs 100 to 1000. Variable fonts honour it exactly; a static family snaps to its nearest available cut, rounding up on a tie. Latin, Cyrillic, Greek, Vietnamese, Turkish, Arabic, Hebrew, Thai, Devanagari, Bengali, Tamil, Amharic, Khmer, Chinese, Japanese and Korean all render, with right-to-left joining and conjunct shaping handled by the engine. Colour emoji does not render in text assets.

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.

Landscape footage reframed to a vertical 9:16 Reel (crop)

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. A scene’s transition blends INTO it from the previous scene, so put it on the second of two scenes. overlays is a flat list of clips that sit over the whole composition, like a persistent logo or watermark.
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

You do not need editing experience to get professional results. These are the conventions working editors follow, translated into compose fields. AI agents get the same rules automatically through the MCP compose reference.

Pacing

Clip length is the pacing lever. Social clips run 0.8 to 3 seconds each with a visual change at least every 3 seconds, vlogs 2 to 6, tutorials 5 to 15 (cut at step boundaries), cinematic 4 to 8 with varied lengths. Shorten clips as the video builds, then hold the final shot about twice as long. The first clip is the hook: no fade-in, no logo, the payoff visible or promised within 3 seconds. A 20 to 35 second total is the highest-completion band on social. Cutting to music: seconds per beat is 60 divided by BPM. Cut every 2 or 4 beats, every beat only at the climax. For 120 BPM music that means 1 or 2 second clips.

Transitions

Professionals cut. Keep crossfades under 10% of your joins and pick ONE decorative style (slide or zoom) per video, at section boundaries only.

Text placement and size

On vertical video keep every text element inside x 8 to 92%, y 13.5 to 83% (platform UI covers the rest). Captions sit at y 60 to 75%. Sizes at 1080x1920: titles 135 to 190px, lower thirds 70 to 80px, captions 90 to 105px. Always add a stroke (black, width 4 to 8), a shadow, or a background box. Titles are 6 words or fewer, 2 lines maximum. Pair one display font (Oswald, Anton, Bebas Neue) with one body font (Inter, Roboto).

Color looks

Apply one look to every clip in the video: With faces in frame stay inside saturation 0.7 to 1.3, hue within 8 degrees, temperature 4500 to 12000K. To match two clips shot differently: fix brightness first, then temperature, then saturation.

Audio levels

volume is linear gain. With speech at 1.0: music under speech 0.10 to 0.12, music alone 0.35 to 0.5, sound effects 0.25 to 0.32. Fade music in over 1 to 2 seconds and out over 3 to 5, ending on the last frame. To duck music under speech, split the music into three clips: before speech at 0.4 with a 0.4s fadeOut, under speech at 0.11, after speech at 0.4 with a 1.5s fadeIn.

Photos need motion

A static photo in a video reads as a mistake. Give every image clip a gentle Ken Burns move: "transform": { "scale": 1, "position": { "x": "50%", "y": "50%" }, "animateTo": { "scale": 1.12 } }.

Reference

Output

The output object controls the render target. Provide either an explicit resolution or a named size preset.

Assets

A clip’s asset is one of:

Clip fields

Every clip has an asset. start and length are optional, and length also accepts "end" (see Timing is optional). Plus any of:

Transitions

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

Scenes

The scene-first shape, when you use scenes instead of tracks:

Text style

Fields on a text asset’s style:

See also

  • Job output: the output shape every job returns
  • Animated captions: word-level captions over a rendered video
  • Compress: shrink the render to a size or quality budget
  • FFmpeg: drop to a raw command when the timeline cannot express it
  • Webhooks: receive job.completed instead of polling
  • Plan limits: render duration and file-size caps per plan
Last modified on September 18, 2026