video#
A video clip with optional trim, loop, playback rate, time remapping, and volume control. Inherits common fields.
interface VideoElement extends BaseElement { type: "tok-str">'video'; source: string; fit?: "tok-str">'cover' | "tok-str">'contain' | "tok-str">'fill' | "tok-str">'none'; crop_x?: number; crop_y?: number; crop_width?: number; crop_height?: number; volume?: number | Keyframe[]; audio_fade_in?: number; audio_fade_out?: number; playback_rate?: number | Keyframe[]; trim_start?: number; trim_duration?: number; time_remap?: Keyframe[]; loop?: boolean; }
Required#
| Field | Type | Default | Description |
|---|---|---|---|
source | string (URL) | required | HTTP(S) URL of the video file. MP4 (H.264 / H.265), WebM (VP8 / VP9), and MOV are supported. Local file paths fail in the renderer today; see renderer docs. |
Audio#
| Field | Type | Default | Description |
|---|---|---|---|
volume | number | Keyframe[] | 100 | Volume in percent, from 0 (mute) to 100 (full). Animatable — use keyframes for fade-ins / fade-outs. |
audio_fade_in | number (seconds) | 0 | Audio fade-in length in seconds, applied at the start of the played window. |
audio_fade_out | number (seconds) | 0 | Audio fade-out length in seconds, applied at the end of the played window. |
To strip audio entirely, set volume: 0. For a simple fade, prefer audio_fade_in / audio_fade_out over hand-authoring volume keyframes.
Playback#
| Field | Type | Default | Description |
|---|---|---|---|
playback_rate | number | Keyframe[] | 1 | Speed multiplier. 0.5 = half speed; 2 = double. Animatable. |
loop | boolean | false | Whether to loop the clip if the element's duration exceeds the trimmed clip length. |
Trimming#
| Field | Type | Default | Description |
|---|---|---|---|
trim_start | number (seconds) | 0 | Where in the source file playback begins (media in-point). |
trim_duration | number (seconds) | source-derived | How much of the source file to play, starting from trim_start. Defaults to the rest of the file. |
The element's own duration (from common fields) is the duration on the timeline. The clip's playback fills that window, looping if loop is true and the trimmed clip is shorter, or stopping at the trimmed clip's end if it isn't.
Time remapping#
| Field | Type | Default | Description |
|---|---|---|---|
time_remap | Keyframe[] | — | Keyframes whose value is a media time in seconds. Maps timeline time to media time for warped playback (ramps, freeze frames, reverse). Replaces trim_start / trim_duration / playback_rate — use one approach or the other, not both. |
Each keyframe's time is a point on the element's local timeline and its value is the media position to show there. Hold the same value across two keyframes for a freeze frame; let value decrease over time to play in reverse.
Layout#
The fit semantics from images apply to video too: the runtime defaults to cover. Set the element's width/height explicitly to control framing.
Crop#
crop_x / crop_y / crop_width / crop_height select a normalized sub-rectangle (each 0–1, origin top-left) of the source frame, applied before fit. The element box is unchanged — crop chooses which part of the frame fills it. Omit the fields (or leave the identity 0,0,1,1) for no crop. The editor's Crop widget gives numeric fields plus a drag-to-resize frame over the video. Each component is keyframeable for a Ken Burns pan/zoom. Default crop_width / crop_height are 1; crop_x / crop_y are 0.
Examples#
Trimmed clip with audio fade-out#
{ "type": "video", "source": "https://example.com/intro.mp4", "trim_start": 2.5, "trim_duration": 6, "volume": [ { "time": 0, "value": 100 }, { "time": 5, "value": 100 }, { "time": 6, "value": 0 } ] }
Slow-motion clip#
{ "type": "video", "source": "https://example.com/explosion.mp4", "playback_rate": 0.25, "duration": 8 }
Speed ramp with a freeze frame (time remap)#
{ "type": "video", "source": "https://example.com/jump.mp4", "duration": 5, "time_remap": [ { "time": 0, "value": 0 }, { "time": 2, "value": 2 }, { "time": 3, "value": 2 }, { "time": 5, "value": 4 } ] }
Plays media seconds 0–2 in real time, freezes on media second 2 for a beat, then ramps through media seconds 2–4 over the final two timeline seconds.
Looped background plate#
{ "type": "video", "source": "https://example.com/loop.mp4", "layer": 100, "x": 0, "y": 0, "width": 1920, "height": 1080, "duration": "end", "loop": true, "volume": 0 }
A background plate sits behind everything, so give it a high layer number (layer 1 draws on top, higher numbers draw farther back). With the default top-left anchor, x: 0, y: 0 plus the canvas width/height fills the frame — do not place a full-frame element at the canvas center (x: 960, y: 540).
Notes#
- Frame-accurate seeking — the runtime uses WebCodecs for decode, so seeks are frame-accurate.
- Codec support in the hosted renderer matches Chromium's: H.264, H.265, VP8, VP9, AV1. Audio codecs: AAC, Opus, MP3.
- Multiple audio tracks — if your source has multiple tracks, only the default is mixed. Use ffmpeg to remux if you need to select a non-default track.