Video
Renders a playing video. Layout and child positioning are inherited from Rect: a Video lays out its children exactly like a Rect, with a playing video frame painted in place of the fill. The video's audio track is automatically scheduled alongside the picture.
Usage
import { createScene, Video } from 'motion-script';
export default createScene(function* (stage) {
stage.add(
<Video
src="./assets/clip.mp4"
fit="fill"
width={1280}
height={720}
cornerRadius={0}
/>
);
});
Props
Source & fit
| Prop | Type | Default | Description |
|---|---|---|---|
src | string | – | Path to the video file |
fit | 'fill' | 'fit' | 'tile' | 'stretch' | 'fill' | How the video frame fills the node's bounds. 'fill' covers + crops (centered), 'fit' letterboxes, 'tile' repeats, 'stretch' distorts to fill. |
crop | Insets | 0 | Window onto the source frame, in fractions of its own size, applied before fit |
zoom | number | 1 | Magnification on top of the fitted scale |
anchor | Anchor | 'center' | The point held fixed as zoom scales, and the alignment when the frame doesn't cover |
matrix | ImageMatrix | – | Raw frame→shape matrix; bypasses crop/fit/zoom/anchor and the bounds |
filters | MediaFilter[] | FilterChain | [] | Visual filters applied to the rendered frame |
crop, zoom and anchor behave exactly as they do on Image — same pipeline, same units, same caveats under 'tile'.
Playback
| Prop | Type | Default | Description |
|---|---|---|---|
playing | boolean | true | Whether playback advances with the node's clock |
timestamp | number | null | – | Explicit source time in seconds. Omit it and the picture is timed from the moment this node appeared; set it to drive the playhead yourself, or null to hand it back |
trimStart | number | – | Starting point within the source file in seconds |
trimEnd | number | – | Ending point within the source file in seconds |
speed | number | 1 | Playback-rate multiplier (affects both picture and audio) |
loop | 'forward' | 'reverse' | 'none' | – | Loop behaviour |
duration | number | – | Length of one loop cycle in seconds. Defaults to the trimmed clip length |
Audio
| Prop | Type | Default | Description |
|---|---|---|---|
volume | number | 1 | Audio volume in [0, 1] |
muted | boolean | false | Silence the audio track without affecting the picture |
audioFilters | AudioFilter | [] | Audio filters applied to the video's sound track (a single filter, an array, or an AudioFilterChain) |
Layout & appearance
| Prop | Type | Default | Description |
|---|---|---|---|
cornerRadius | number | CornerRadiusProps | 0 | Corner radius (clips the frame) |
cornerStyle | 'rounded' | 'angled' | CornerStyleProps | 'rounded' | Corner shape |
Playback timing
The frame shown is worked out as the node paints, from how long the node has
existed — a Video added 3s into a scene opens on its first frame, not 3s in.
Nothing advances it per frame, so a given frame renders the same whether you
scrubbed to it, exported it, or played into it.
timestamp overrides that: set it and the node shows exactly that source time.
It is the time-remap knob — tween it to scrub the clip, or pair it with
playing: false to hold a single frame. Set it to null to hand the playhead
back to the clock (set() merges a partial, so undefined won't clear it). A
paused clip with no timestamp sits on its first frame (trimStart).
Animating
Animate standard node props (opacity, scale, cornerRadius, etc.) with .to(). For playback control, set timestamp and playing:
import { createScene, Video, createRef, easeInOut, wait } from 'motion-script';
export default createScene(function* (stage) {
const clip = createRef<Video>();
stage.add(
<Video
ref={clip}
src="./assets/clip.mp4"
fit="fill"
width={960}
height={540}
cornerRadius={24}
opacity={0}
/>
);
// Fade in
yield* clip().to({ opacity: 1, cornerRadius: 0 }, 0.8, easeInOut);
// Freeze on the frame 3s into the clip
yield* wait(3);
clip().set({ playing: false, timestamp: 3 });
// Scrub it by hand while frozen
yield* clip().to({ timestamp: 8 }, 2, easeInOut);
// Hand the playhead back to the clock, which never stopped — so it resumes
// where an unpaused clip would be
clip().set({ playing: true, timestamp: null });
});
Trim and loop
<Video
src="./assets/clip.mp4"
trimStart={2.5}
trimEnd={8.0}
loop="forward"
width={640}
height={360}
/>
Speed change
<Video src="./assets/clip.mp4" speed={0.5} width={640} height={360} />
Notes
VideoextendsRect, so children can be placed on top of the video frame (for subtitles, overlays, etc.).- Set
muted: trueto suppress audio while keeping the picture, which is useful when compositing multiple video layers. playing: falsefreezes both picture and sound; add atimestampto choose which frame it holds.loop: 'reverse'plays the clip backward when it reaches the end.- See Audio Filters for the full list of audio filters.
- See Image Filters for the full list of visual filters that can be applied to the frame.