Video

Video describes an animation whose frames are ordinary Gum elements. Return it at the top level of a JSX source. It is provided by @gum-jsx/mp4 and is available in the gum CLI without imports.

PropertyDefaultMeaning
sizeRequired[width, height] in pixels for every frame
fpsRequiredPositive frame rate in frames per second
children—Nonempty sequence of elements, one per frame
frame—Synchronous ({ time, frame, fps }) => element generator
durationRequired with framePositive animation duration in seconds
backgroundwhiteCanvas background behind each frame

Choose either children, or a frame generator with duration. Combining the two forms is an error. Video is a timeline component, so keep layout and style props on its frame elements; it cannot be nested inside a Box or stack.

Frame children

Children play in source order. Their count determines the duration: frame_count / fps. Arrays and fragments flatten into the sequence; null and boolean children are ignored. At least one element must remain.

The example below plays four explicitly written frames at 2 fps, lasting two seconds. For an existing array, place {frames} between the Video tags. The component snapshots the children at construction. Omit duration when supplying children.

Frame generator

Use frame for animations computed from time. It receives:

  • frame: the zero-based integer frame index.
  • time: frame / fps, in seconds.
  • fps: the video's frame rate.

Return a Gum element synchronously:

The source is evaluated once; the generator runs only when a frame is requested. There are ceil(duration * fps) frames, with no sample at the exact end time. The encoded duration is the frame count divided by fps.

Frames can be requested out of order. Base them on their inputs and precomputed data; avoid advancing mutable counters or drawing random numbers inside frame. For a simulation such as Game of Life, precompute its states and index them by frame. Use setSeed before generating any initial random data.

The CLI also provides lerp(a, b, progress), progress(time, start, duration), and ease_in_out(progress) for interpolation and timing.

Export and preview

Gum Studio plays videos in its preview pane. Use the play/pause button or frame slider to inspect individual frames.

Save the source as video.jsx, then run:

Without an output format or filename, the CLI shows a Kitty frame preview. --time defaults to 0, selects floor(time * fps), and must be less than the duration. Omit it for MP4 export. Other frame-preview formats include PDF, PPTX, tree, and JSON.

-W and -H override the frame dimensions, while --theme and --background override its appearance. The video size overrides dimensions declared by a frame's Page. MP4 requires even integer dimensions from 2 to 4096 and an fps in [0.001, 1000]. --qp sets an integer quantizer from 10 to 51; lower values increase quality and file size, with a default of 18.

MP4 export uses the bundled encoder and needs no FFmpeg. Output is H.264 in fragmented MP4, without audio. --ratio, --select, and --stats apply to frame previews rather than MP4 export.

In a host script, import Video, render_mp4, and create_renderer from @gum-jsx/mp4. Construct a video with new Video({ size, fps, children }) or new Video({ size, fps, duration, frame }). Export it with await render_mp4(video, 'video.mp4'), or render a PNG frame with create_renderer(video).png(index).