Skip to content

Repository files navigation

rustmotion

A CLI tool that renders motion design videos from JSON scenarios. No browser, no Node.js — just a single Rust binary.

Crates.iodocs.rsLicense: MIT

Install

cargo install rustmotion

Requirements: Rust toolchain + C++ compiler (for openh264). Recommended:ffmpeg CLI for 10-bit H.264 and H.265/VP9/ProRes/WebM/GIF output.

Shell Completions

Generate and install completions for your shell:

# Zsh (add to ~/.zshrc)
rustmotion completions zsh >~/.zfunc/_rustmotion
# then add to .zshrc: fpath=(~/.zfunc $fpath) && autoload -Uz compinit && compinit# Or one-liner for Oh My Zsh:
rustmotion completions zsh >${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/rustmotion/_rustmotion
# Bash (add to ~/.bashrc)
rustmotion completions bash >~/.local/share/bash-completion/completions/rustmotion
# Fish
rustmotion completions fish >~/.config/fish/completions/rustmotion.fish

Quick Start

# Render a video
rustmotion render scenario.json -o video.mp4
# Render with a specific codec
rustmotion render scenario.json -o video.webm --codec vp9 --crf 30
# Export as PNG sequence
rustmotion render scenario.json -o frames/ --format png-seq
# Export as animated GIF
rustmotion render scenario.json -o output.gif --format gif
# Render a single frame for preview
rustmotion render scenario.json --frame 42 -o frame.png
# Validate without rendering
rustmotion validate scenario.json
# Export JSON Schema (for editor autocompletion or LLM prompts)
rustmotion schema -o schema.json
# Show scenario info
rustmotion info scenario.json

Claude Code Skills

rustmotion ships with built-in Claude Code skills — 30 rules and best practices for generating video scenarios with AI. After installing rustmotion, run:

# Install skills in your video project (recommended)cd my-video-project/
rustmotion skills install
# → .claude/skills/rustmotion/ (SKILL.md + 29 rules)# → CLAUDE.md (project instructions)# Or install globally (available in all projects)
rustmotion skills install --global
# → ~/.claude/skills/rustmotion/# Browse available rules
rustmotion skills list
# Read a specific rule
rustmotion skills show hex-colors

Once installed, Claude Code automatically loads the skills when you work in that directory. It will know how to generate valid rustmotion JSON scenarios, pick the right components, and follow all layout/animation constraints.

CLI Reference

rustmotion render

FlagDescriptionDefault
inputPath to the JSON scenario file(required)
-o, --outputOutput file pathoutput.mp4
--frame <N>Render a single frame to PNG (0-indexed)
--codec <CODEC>Video codec: h264, h265, vp9, proresh264
--crf <0-51>Constant Rate Factor (lower = better quality)23
--format <FMT>Output format: mp4, webm, mov, gif, png-seqauto from extension
--transparentTransparent background (PNG sequence, WebM, ProRes 4444)false
--output-format jsonMachine-readable JSON output for CI pipelines
-q, --quietSuppress all output except errors

JSON Scenario Format

{
"version": "1.0",
"video": { ... },
"audio": [ ... ],
"scenes": [ ... ]
}

Video Config

{
"video": {
"width": 1080,
"height": 1920,
"fps": 30,
"background": "#0f172a",
"codec": "h264",
"crf": 23
}
}
FieldTypeDefaultDescription
widthu32(required)Video width in pixels (must be even)
heightu32(required)Video height in pixels (must be even)
fpsu3230Frames per second
backgroundstring"#000000"Default background color (hex)
codecstring"h264"Video codec: h264, h265, vp9, prores
crfu823Constant Rate Factor (0-51, lower = better quality)

Audio Tracks

{
"audio": [
{
"src": "music.mp3",
"start": 0.0,
"end": 10.0,
"volume": 0.8,
"fade_in": 1.0,
"fade_out": 2.0
}
]
}
FieldTypeDefaultDescription
srcstring(required)Path to audio file (MP3, WAV, OGG, FLAC, AAC)
startf640.0Start time in the output video (seconds)
endf64End time (omit for full track)
volumef321.0Volume multiplier (0.0 - 1.0)
fade_inf64Fade-in duration (seconds)
fade_outf64Fade-out duration (seconds)

Scenes

Each scene is an implicit flex container at video dimensions (default direction: column). Children participate in flex flow automatically. Use positioned container for absolute positioning.

{
"scenes": [
{
"duration": 3.0,
"background": "#1a1a2e",
"layout": {
"direction": "column",
"align_items": "center",
"justify_content": "center",
"gap": 24
},
"children": [ ... ],
"transition": {
"type": "fade",
"duration": 0.5
}
}
]
}
FieldTypeDefaultDescription
durationf64(required)Scene duration in seconds
backgroundstringScene background (overrides video.background)
freeze_atf64Freeze the scene at this time (seconds)
childrenComponent[][]Components rendered bottom-to-top
layoutobjectScene-level flex layout options
transitionTransitionTransition effect from the previous scene

layout options:direction (column/row), gap, align_items (start/center/end/stretch), justify_content (start/center/end/space_between/space_around/space_evenly), padding


Transitions

Transitions blend between two consecutive scenes. Set on the second scene.

{
"transition": {
"type": "clock_wipe",
"duration": 0.8
}
}
TypeDescription
fadeLinear crossfade between scenes
wipe_leftHorizontal wipe revealing scene B from the left
wipe_rightHorizontal wipe revealing scene B from the right
wipe_upVertical wipe revealing scene B from the top
wipe_downVertical wipe revealing scene B from the bottom
zoom_inScene A zooms in and fades out, revealing scene B
zoom_outScene B zooms out from larger to normal size
flip3D Y-axis flip simulation
clock_wipeCircular clockwise sweep from 12 o'clock
irisExpanding circle from the center reveals scene B
slideScene B pushes scene A to the left
dissolvePer-pixel noise dissolve
noneHard cut at the midpoint
FieldTypeDefaultDescription
typestring(required)One of the transition types above
durationf640.5Transition duration in seconds

Include (Composable Scenarios)

Scene entries can reference external scenario files to inject their scenes inline:

{
"scenes": [
{ "include": "shared/intro.json" },
{ "duration": 5.0, "children": [ ... ] },
{ "include": "shared/outro.json", "scenes": [0, 2] }
]
}
FieldTypeDefaultDescription
includestring(required)Path (relative to parent file) or URL to a scenario JSON
scenesusize[]Only include scenes at these 0-based indices. Omit to include all
configobjectConfig overrides to pass to a structural component (see below)
  • The included file's video config is ignored
  • Audio tracks from included files are merged
  • Includes can be nested (max depth: 8)

Structural Components (Variables)

Structural components are reusable scenario files with declared variables. When rendered standalone, default values apply. When included from a parent, the parent can override any variable.

Defining config

Add a config object at the root of a scenario. Each entry has a type, a default value, and an optional description:

{
"config": {
"cta_text": { "type": "string", "default": "Book your demo" },
"accent_color": { "type": "string", "default": "#5C39EE" },
"logo_src": { "type": "string", "default": "assets/logo.svg" },
"counter_target": { "type": "number", "default": 400 },
"tagline_spans": {
"type": "array",
"default": [
{ "text": "Don't ", "color": "#5C39EE" },
{ "text": "miss any lead" }
]
}
},
"video": { "width": 1080, "height": 1920, "fps": 30 },
"scenes": [
{
"duration": 7.0,
"children": [
{ "type": "svg", "src": "$logo_src" },
{ "type": "text", "content": "$cta_text", "style": { "color": "$accent_color" } },
{ "type": "counter", "from": 0, "to": { "$var": "counter_target" } },
{ "type": "rich_text", "spans": { "$var": "tagline_spans" } }
]
}
]
}

Supported types: string, number, boolean, object, array. Array and object types allow passing full components (e.g. rich_text spans, children arrays).

Referencing config values

SyntaxContextBehavior
"$var_name"Entire string valueReplaced by the config value (any type)
"prefix $var_name suffix"String interpolationReplaced inline (value must be string/number/boolean)
{ "$var": "var_name" }Any positionReplaced by the config value (for non-string types in object position)
"$$literal"EscapeProduces the literal string "$literal"

Including with overrides

{
"scenes": [
{ "duration": 5.0, "children": [ ... ] },
{
"include": "components/outro.json",
"config": {
"cta_text": "Try WhatsApp",
"accent_color": "#25D366",
"tagline_spans": [
{ "text": "Stop losing " },
{ "text": "customers", "color": "#25D366" }
]
}
}
]
}

Config entries not listed in overrides keep their default values. Referencing an undefined config key produces an error.

Standalone rendering

When rendering a structural component directly (rustmotion render components/outro.json), all default values are applied automatically.


Components

All components are discriminated by "type". Rendered in array order (first = bottom, last = top).

Common Fields

Available on all component types:

FieldTypeDefaultDescription
start_atf64Component appears at this time (seconds within scene)
end_atf64Component disappears after this time

Common Style Fields

All visual properties are inside a "style" object:

Style fieldTypeDefaultDescription
opacityf321.00.0 to 1.0
paddingf32 | {top, right, bottom, left}Inner spacing
marginf32 | {top, right, bottom, left}Outer spacing
animationarray | object[]Animation effects (see Animations)

Text

{
"type": "text",
"content": "Hello World",
"max_width": 800,
"style": {
"font-size": 48,
"color": "#FFFFFF",
"font-family": "Inter",
"font-weight": "bold",
"text-align": "center",
"line-height": 1.2,
"letter-spacing": 2.0,
"animation": [{ "name": "fade_in_up", "delay": 0.3, "duration": 0.6 }]
}
}

Root fields:content (required), max_width

Style fieldTypeDefaultDescription
font-sizef3248.0Font size in pixels
colorstring"#FFFFFF"Text color (hex)
font-familystring"Inter"Font family name
font-weightenum"normal""normal" or "bold"
font-styleenum"normal""normal", "italic", "oblique"
text-alignenum"left""left", "center", "right"
line-heightf32Line height multiplier
letter-spacingf32Additional spacing between characters
text-shadowobject{ "color": "#000", "offset_x": 2, "offset_y": 2, "blur": 4 }
strokeobject{ "color": "#000", "width": 2 }
text-backgroundobject{ "color": "#000", "padding": 4, "corner_radius": 4 }

Shape

{
"type": "shape",
"shape": "rounded_rect",
"size": { "width": 300, "height": 200 },
"style": {
"fill": "#3b82f6",
"border-radius": 16,
"stroke": { "color": "#ffffff", "width": 2 },
"animation": [{ "name": "scale_in", "duration": 0.6 }]
}
}

Root fields:shape (required), size, text

Style fieldTypeDefaultDescription
fillstring | gradientFill color (hex) or gradient object
stroke{color, width}Stroke outline
border-radiusf32Corner radius (for rounded_rect)

Shape types:rect, circle, rounded_rect, ellipse, triangle, star (with points, default 5), polygon (with sides, default 6), path (with data SVG path string)

Gradient fill:

{
"fill": {
"type": "linear",
"colors": ["#667eea", "#764ba2"],
"angle": 135,
"stops": [0.0, 1.0]
}
}

Types: linear, radial.

Embedded text in shapes (text field):

{
"type": "shape",
"shape": "circle",
"size": { "width": 56, "height": 56 },
"style": { "fill": "#2A74FF" },
"text": {
"content": "1",
"font_size": 22,
"color": "#FFFFFF",
"font_weight": "bold",
"align": "center",
"vertical_align": "middle"
}
}

vertical_align: "top", "middle", "bottom" (default: "middle").


Image

{
"type": "image",
"src": "photo.png",
"size": { "width": 1080, "height": 1080 },
"fit": "cover",
"style": {
"animation": [{ "name": "fade_in", "duration": 0.5 }]
}
}
FieldTypeDefaultDescription
srcstring(required)Path to image file (PNG, JPEG, WebP)
size{width, height}Target size (uses native image size if omitted)
fitstring"cover""cover", "contain", "fill", "none"

SVG

{
"type": "svg",
"src": "logo.svg",
"size": { "width": 200, "height": 200 }
}

Or with inline SVG:

{
"type": "svg",
"data": "<svg viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='red'/></svg>"
}
FieldTypeDefaultDescription
srcstringPath to .svg file
datastringInline SVG markup
size{width, height}Target size (uses SVG intrinsic size if omitted)

One of src or data is required.


Icon

Renders an icon from the Iconify open-source framework (200,000+ icons from 150+ sets). Icons are fetched from the Iconify API at render time.

Browse all available icons at icon-sets.iconify.design.

{
"type": "icon",
"icon": "lucide:home",
"size": { "width": 64, "height": 64 },
"style": { "color": "#38bdf8" }
}
FieldTypeDefaultDescription
iconstring(required)Iconify identifier "prefix:name" (e.g. "lucide:home", "mdi:account")
size{width, height}24x24Icon size in pixels

Style: color (default "#FFFFFF")

Common icon sets:

PrefixNameBest for
lucideLucideClean UI icons (default choice)
mdiMaterial Design IconsMaterial UI, Android
heroiconsHeroiconsTailwind projects
phPhosphorModern UI
tablerTabler IconsDashboards
simple-iconsSimple IconsBrand/company logos
deviconDeviconProgramming language logos

Video

Embeds a video clip as a component. Requires ffmpeg on PATH.

{
"type": "video",
"src": "clip.mp4",
"size": { "width": 1080, "height": 1920 },
"trim_start": 2.0,
"trim_end": 8.0,
"playback_rate": 0.5,
"fit": "cover",
"volume": 0.0
}
FieldTypeDefaultDescription
srcstring(required)Path to video file
size{width, height}(required)Display size
trim_startf640.0Start offset in the source clip (seconds)
trim_endf64End offset in the source clip (seconds)
playback_ratef641.0Playback speed (0.5 = half speed, 2.0 = double)
fitstring"cover""cover", "contain", "fill"
volumef321.0Audio volume (0.0 = mute)
loop_videoboolLoop the clip

GIF

Displays an animated GIF, synced to the scene timeline.

{
"type": "gif",
"src": "animation.gif",
"size": { "width": 300, "height": 300 },
"fit": "cover"
}
FieldTypeDefaultDescription
srcstring(required)Path to .gif file
size{width, height}Display size (uses GIF native size if omitted)
fitstring"cover""cover", "contain", "fill"
loop_gifbooltrueLoop the GIF animation

Caption

Timed word-by-word captions with active word highlighting.

{
"type": "caption",
"words": [
{ "text": "Hello", "start": 0.0, "end": 0.5 },
{ "text": "world!", "start": 0.5, "end": 1.0 }
],
"mode": "highlight",
"active_color": "#FFD700",
"max_width": 900,
"style": { "font-size": 48, "color": "#FFFFFF" }
}
FieldTypeDefaultDescription
wordsarray(required)[{ "text", "start", "end" }]
modeenum"default""default", "highlight", "karaoke", "bounce"
active_colorstring"#FFD700"Active word color
max_widthf32Maximum width before word-wrapping

Style: font-size (48.0), font-family, color (#FFFFFF)


Counter

Animated number counter. Must be used standalone (not inside a card).

{
"type": "counter",
"from": 0,
"to": 1250,
"decimals": 0,
"separator": "",
"suffix": "",
"easing": "ease_out",
"start_at": 0.5,
"end_at": 2.5,
"style": {
"font-size": 72,
"color": "#FFFFFF",
"font-weight": "bold",
"text-align": "center"
}
}
FieldTypeDefaultDescription
fromf64(required)Start value
tof64(required)End value
decimalsu80Number of decimal places
separatorstringThousands separator (e.g. " ", ",")
prefixstringText before the number (e.g. "$")
suffixstringText after the number (e.g. "%", "€")
easingstring"linear"Easing for the counter interpolation

Style: font-size, color, font-family, font-weight, text-align, letter-spacing, text-shadow, stroke


Positioned

Container that places children at fixed absolute coordinates (like Flutter's Stack/Positioned). Each child uses position: {x, y} relative to the container's top-left.

{
"type": "positioned",
"children": [
{ "type": "shape", "shape": "rect", "position": { "x": 0, "y": 0 }, "size": { "width": 400, "height": 300 }, "style": { "fill": "#1E293B", "border-radius": 16 } },
{ "type": "icon", "icon": "lucide:phone-off", "position": { "x": 170, "y": 120 }, "size": { "width": 64, "height": 64 }, "style": { "color": "#FFFFFF" } }
]
}

Container

Invisible wrapper that groups children for shared transforms (scale, opacity, rotation, etc.). Like an HTML <div> with no visual styling. When you scale a container, all children scale together from the container's center — no overlap or distortion.

Use container instead of card when you don't need background, border, or shadow.

{
"type": "container",
"size": { "width": "auto", "height": "auto" },
"style": {
"align-items": "center",
"gap": 36,
"timeline": [
{ "at": 3.5, "animation": [{ "name": "keyframes", "keyframes": [
{ "property": "scale", "keyframes": [{ "time": 0, "value": 1 }, { "time": 0.8, "value": 4 }], "easing": "ease_in" },
{ "property": "opacity", "keyframes": [{ "time": 0, "value": 1 }, { "time": 0.7, "value": 0 }], "easing": "ease_in" }
]}]}
]
},
"children": [
{ "type": "icon", "icon": "lucide:zap", "size": { "width": 80, "height": 80 }, "style": { "color": "#25D366" } },
{ "type": "text", "content": "All children scale together", "style": { "font-size": 48, "color": "#FFFFFF" } }
]
}

Supports all flex layout properties (flex-direction, align-items, justify-content, gap, padding) and all style properties (animation, timeline, opacity, margin). No background, border, box-shadow, or clipping — children can overflow freely.


Card / Flex

Visual container with CSS-like flex & grid layout. flex is an alias for card. Each dimension of size can be a number or "auto".

Flex example:

{
"type": "card",
"size": { "width": 800, "height": "auto" },
"style": {
"flex-direction": "row",
"align-items": "center",
"gap": 16,
"padding": 24,
"background": "#1E293B",
"border-radius": 16,
"animation": [{ "name": "fade_in_up", "delay": 0.3, "duration": 0.6 }]
},
"children": [
{ "type": "icon", "icon": "lucide:check-circle", "size": { "width": 48, "height": 48 }, "style": { "color": "#22C55E" } },
{ "type": "text", "content": "Feature enabled", "style": { "font-size": 32, "color": "#FFFFFF" } }
]
}

Grid example (2x2): Grid containers need an explicit height (not "auto") to prevent rows from stretching.

{
"type": "card",
"size": { "width": 600, "height": 400 },
"style": {
"display": "grid",
"grid-template-columns": [{ "fr": 1 }, { "fr": 1 }],
"grid-template-rows": [{ "fr": 1 }, { "fr": 1 }],
"gap": 16,
"padding": 24,
"background": "#1a1a2e"
},
"children": [
{ "type": "text", "content": "Cell 1", "style": { "color": "#FFFFFF" } },
{ "type": "text", "content": "Cell 2", "style": { "color": "#FFFFFF" } },
{ "type": "text", "content": "Cell 3", "style": { "color": "#FFFFFF" } },
{ "type": "text", "content": "Cell 4", "style": { "color": "#FFFFFF" } }
]
}

Style fields:

Style fieldTypeDefaultDescription
displayenum"flex""flex" or "grid"
backgroundstringBackground color (hex)
border-radiusf3212.0Corner radius
borderobject{ "color": "#E5E7EB", "width": 1 }
box-shadowobject{ "color": "#00000040", "offset_x": 0, "offset_y": 4, "blur": 12 }
paddingf32 | objectInner spacing
flex-directionenum"column""column", "row", "column_reverse", "row_reverse"
flex-wrapboolfalseWrap children to next line
align-itemsenum"start""start", "center", "end", "stretch"
justify-contentenum"start""start", "center", "end", "space_between", "space_around", "space_evenly"
gapf320Spacing between children
grid-template-columnsarray[{"px": N}, {"fr": N}, "auto"]
grid-template-rowsarraySame format as columns

Per-child layout properties (in child "style"):

  • flex-grow (f32) — default 0
  • flex-shrink (f32) — default 1
  • flex-basis (f32) — defaults to natural size
  • align-self (enum) — "start", "center", "end", "stretch"
  • grid-column (object) — { "start": 1, "span": 2 } (1-indexed)
  • grid-row (object) — { "start": 1, "span": 2 } (1-indexed)

Codeblock

Code block with syntax highlighting, chrome, reveal animations, and animated diff transitions.

{
"type": "codeblock",
"code": "fn main() {\n println!(\"Hello\");\n}",
"language": "rust",
"theme": "base16-ocean.dark",
"show_line_numbers": true,
"chrome": { "enabled": true, "title": "main.rs" },
"reveal": { "mode": "typewriter", "start": 0, "duration": 2.5 },
"style": { "font-size": 18, "border-radius": 12, "padding": 16 },
"states": [
{
"code": "fn main() {\n println!(\"Hello, world!\");\n}",
"at": 5.0,
"duration": 2.0,
"cursor": { "enabled": true, "blink": true }
}
]
}

Root fields:code (required), language, theme, size, show_line_numbers, chrome, highlights, reveal, states

Style fieldTypeDefault
font-familystring"JetBrains Mono"
font-sizef3214.0
font-weightenum"normal"
line-heightf321.5 (multiplier)
backgroundstring(uses theme)
border-radiusf3212.0
paddingf32 | object16

Chrome (Title Bar)

FieldTypeDefaultDescription
chrome.enabledbooltrueShow the title bar
chrome.titlestringTitle text (e.g. filename)

Line Highlights

{ "highlights": [{ "lines": [2], "color": "#FFFF0022", "start": 3.0, "end": 4.5 }] }

Reveal Animation

FieldTypeDefaultDescription
reveal.modestring(required)"typewriter" or "line_by_line"
reveal.startf640.0Start time (seconds)
reveal.durationf641.0Duration (seconds)

Code States (Diff Transitions)

Animate between code versions with automatic diff detection.

FieldTypeDefaultDescription
states[].codestring(required)New code content
states[].atf64(required)Transition start time
states[].durationf640.6Transition duration
states[].cursor.enabledbooltrueShow editing cursor
states[].cursor.blinkbooltrueBlink the cursor

Available Themes (72)

Syntect built-in:base16-ocean.dark, base16-ocean.light, base16-eighties.dark, base16-mocha.dark, InspiredGitHub, Solarized (dark), Solarized (light)

Catppuccin:catppuccin-latte, catppuccin-frappe, catppuccin-macchiato, catppuccin-mocha

Shiki / VS Code:andromeeda, aurora-x, ayu-dark, ayu-light, ayu-mirage, dark-plus, dracula, dracula-soft, everforest-dark, everforest-light, github-dark, github-dark-default, github-dark-dimmed, github-dark-high-contrast, github-light, github-light-default, github-light-high-contrast, gruvbox-dark-hard, gruvbox-dark-medium, gruvbox-dark-soft, gruvbox-light-hard, gruvbox-light-medium, gruvbox-light-soft, horizon, horizon-bright, houston, kanagawa-dragon, kanagawa-lotus, kanagawa-wave, laserwave, light-plus, material-theme, material-theme-darker, material-theme-lighter, material-theme-ocean, material-theme-palenight, min-dark, min-light, monokai, night-owl, night-owl-light, nord, one-dark-pro, one-light, plastic, poimandres, red, rose-pine, rose-pine-dawn, rose-pine-moon, slack-dark, slack-ochin, snazzy-light, solarized-dark, solarized-light, synthwave-84, tokyo-night, vesper, vitesse-black, vitesse-dark, vitesse-light

Divider

Visual separator line (horizontal or vertical).

{
"type": "divider",
"direction": "horizontal",
"thickness": 2,
"line_style": "solid",
"style": { "color": "#4B5563" }
}
FieldTypeDefaultDescription
directionenum"horizontal""horizontal" or "vertical"
thicknessf322.0Line thickness in pixels
line_styleenum"solid""solid", "dashed", "dotted"
lengthf32Fixed length (omit for 100% of parent)

Style: color (default "#FFFFFF")


Badge

Compact label with text and optional icon, pill-shaped.

{
"type": "badge",
"text": "New",
"icon": "lucide:star",
"variant": "solid",
"badge_size": "md",
"style": { "background": "#3B82F6" }
}
FieldTypeDefaultDescription
textstring(required)Badge text
iconstringIconify icon id (e.g. "lucide:star")
variantenum"solid""solid" (filled) or "outline" (border only)
badge_sizeenum"md""sm", "md", "lg"

Style: background (default "#3B82F6") — badge color for solid variant or border color for outline, font-size, font-family


Avatar

Circular image with optional border and status indicator.

{
"type": "avatar",
"src": "photo.jpg",
"size": 80,
"border_color": "#3B82F6",
"border_width": 3,
"status": "online"
}
FieldTypeDefaultDescription
srcstring(required)Path to image file
sizef3264.0Diameter in pixels
border_colorstringBorder color (hex)
border_widthf320.0Border thickness
statusenum"none""online", "offline", "away", "none"
status_colorstringOverride status dot color

Status colors: online=#22C55E, offline=#9CA3AF, away=#F59E0B


Callout

Speech bubble with directional arrow.

{
"type": "callout",
"text": "Hello!",
"arrow_direction": "bottom",
"style": {
"background": "#333333",
"color": "#FFFFFF",
"border-radius": 8,
"font-size": 16
}
}
FieldTypeDefaultDescription
textstring(required)Callout text
arrow_directionenum"bottom""top", "bottom", "left", "right"
arrow_sizef3212.0Arrow triangle size
size{width, height}Fixed size (auto-sized if omitted)

Style: background (default "#333333"), color (default "#FFFFFF"), border-radius (default 8), font-size (default 16), font-family


Terminal

Terminal/console window with colored lines and optional chrome.

{
"type": "terminal",
"title": "Terminal",
"theme": "dark",
"reveal": { "mode": "typewriter", "start": 0.5, "duration": 3.0 },
"lines": [
{ "text": "npm install", "line_type": "prompt" },
{ "text": "added 42 packages", "line_type": "output" }
],
"size": { "width": 600, "height": 300 }
}
FieldTypeDefaultDescription
linesarray(required)[{ "text", "line_type", "color" }]
themeenum"dark""dark" or "light"
titlestringWindow title
show_chromebooltrueShow title bar with traffic light dots
revealobject{ "mode": "typewriter"|"line_by_line", "start": 0, "duration": 1, "easing": "linear" }
size{width, height}Terminal size (default 500x auto)

Line types:"prompt" (shows $ prefix in green), "command" (white text), "output" (gray text)

Reveal modes:"typewriter" reveals characters one by one, "line_by_line" fades lines in sequentially.


Table

Data table with headers and styled rows.

{
"type": "table",
"headers": ["Name", "Role", "Status"],
"rows": [
["Alice", "Engineer", "Active"],
["Bob", "Designer", "Away"]
],
"size": { "width": 600, "height": 200 },
"style": { "color": "#FFFFFF", "font-size": 14 }
}
FieldTypeDefaultDescription
headersstring[](required)Column headers
rowsstring[][](required)Data rows
header_colorstring"#374151"Header row background
row_colorsstring[]["#1F2937", "#111827"]Alternating row colors
border_colorstring"#4B5563"Grid line color
header_text_colorstring"#FFFFFF"Header text color
size{width, height}Table size

Style: color (default "#FFFFFF") — cell text color, font-size (default 14), font-family, border-radius


Chart

Data visualization with bar, line, or pie charts. Animated by default.

{
"type": "chart",
"chart_type": "bar",
"data": [
{ "value": 85, "label": "Q1" },
{ "value": 120, "label": "Q2" },
{ "value": 95, "label": "Q3" },
{ "value": 150, "label": "Q4" }
],
"size": { "width": 400, "height": 300 }
}
FieldTypeDefaultDescription
chart_typeenum(required)"bar", "line", "pie"
dataarray(required)[{ "value", "label"?, "color"? }]
size{width, height}300x200Chart size
animatedbooltrueAnimate chart fill/draw
animation_durationf641.5Animation duration in seconds
colorsstring[]Custom color palette (hex)

Default palette: #3B82F6, #EF4444, #22C55E, #F59E0B, #8B5CF6, #EC4899, #06B6D4, #F97316


Mockup

Device frame (phone, laptop, browser) with image content inside.

{
"type": "mockup",
"device": "iphone",
"src": "screenshot.png",
"theme": "dark",
"size": { "width": 375, "height": 812 }
}
FieldTypeDefaultDescription
deviceenum(required)"iphone", "android", "laptop", "browser"
srcstring(required)Path to content image
themeenum"dark""dark" or "light" bezel color
size{width, height}Device size (defaults: iPhone 375x812, Android 360x800, Laptop 800x550, Browser 800x600)

Particle

Animated particle system for visual effects (confetti, snow, stars, bubbles).

{
"type": "particle",
"particle_type": "confetti",
"count": 80,
"speed": 1.2,
"seed": 42
}
FieldTypeDefaultDescription
particle_typeenum(required)"confetti", "snow", "stars", "bubbles", "halo"
countu3250Number of particles
colorsstring[]Custom colors (defaults vary by type)
speedf321.0Speed multiplier
size_range{min, max}{4, 12}Particle size range in pixels
seedu6442Random seed for reproducible results

Particle behaviors:

  • confetti: colored rectangles falling with rotation and horizontal wobble
  • snow: white circles falling gently with lateral drift
  • stars: fixed positions with twinkling opacity
  • bubbles: semi-transparent circles rising with oscillation
  • halo: soft glowing circles drifting slowly with pulsing opacity (great for backgrounds)

Arrow

Directional arrow with optional bezier curves. Use draw_in or stroke_reveal animation presets for drawing effects.

{
"type": "arrow",
"x1": 100, "y1": 300,
"x2": 500, "y2": 300,
"curve": 0.3,
"width": 3,
"color": "#58A6FF",
"arrow_end": true,
"style": {
"animation": [{ "name": "draw_in", "duration": 1.0 }]
}
}
FieldTypeDefaultDescription
x1, y1f320.0Start point
x2, y2f32(required)End point
cp{x, y}Quadratic bezier control point
cp1, cp2{x, y}Cubic bezier control points
curvef32Auto-curve (-1.0 to 1.0)
widthf323.0Stroke width
colorstring"#FFFFFF"Arrow color
arrow_endbooltrueArrowhead at end
arrow_startboolfalseArrowhead at start
arrow_sizef3212.0Arrowhead size
dashedf32[]Dash pattern (e.g. [8, 4])

Connector

Connects two points with automatic routing. Great for diagrams and flowcharts.

{
"type": "connector",
"from": { "x": 200, "y": 150 },
"to": { "x": 600, "y": 400 },
"routing": "curved",
"color": "#58A6FF",
"arrow_end": true,
"style": {
"animation": [{ "name": "stroke_reveal", "duration": 0.8 }]
}
}
FieldTypeDefaultDescription
from{x, y}(required)Start point
to{x, y}(required)End point
routingstring"straight""straight", "curved", "elbow" (L-shaped)
curvaturef320.4Curve intensity (for curved routing)
widthf322.0Stroke width
colorstring"#FFFFFF"Line color
arrow_endbooltrueArrowhead at end
arrow_startboolfalseArrowhead at start
arrow_sizef3210.0Arrowhead size
dashedf32[]Dash pattern

Timeline

Step-by-step timeline with animated progress bar and node icons.

{
"type": "timeline",
"width": 800,
"direction": "horizontal",
"fill_progress": 0.75,
"steps": [
{ "label": "Design", "sublabel": "Week 1", "color": "#58A6FF", "icon": "1" },
{ "label": "Build", "sublabel": "Week 2-3", "color": "#58A6FF", "icon": "2" },
{ "label": "Ship", "sublabel": "Week 4", "color": "#22C55E", "icon": "🚀" }
]
}
FieldTypeDefaultDescription
stepsarray(required)[{ "label", "sublabel"?, "color"?, "icon"? }]
widthf32800.0Timeline width
directionstring"horizontal""horizontal" or "vertical"
node_radiusf3224.0Circle radius
bar_colorstring"#333333"Background bar color
bar_fill_colorstring"#58A6FF"Filled bar color
fill_progressf321.0Progress 0.0 to 1.0

Lottie

Renders Lottie animations from pre-rendered PNG frame sequences.

{
"type": "lottie",
"src": "animation.json",
"frames_dir": "/path/to/frames",
"size": { "width": 300, "height": 300 },
"speed": 1.0,
"loop": true
}
FieldTypeDefaultDescription
srcstringPath to Lottie JSON (for timing metadata)
datastringInline Lottie JSON data
frames_dirstringDirectory with numbered PNGs (0000.png, 0001.png, ...)
size{width, height}Display size (falls back to Lottie intrinsic size)
speedf321.0Playback speed multiplier
loopbooltrueLoop the animation

Generate frames with: npx lottie-to-frames animation.json --output frames/


Cursor

Animated cursor with click effects, blinking, and smooth path animation.

{
"type": "cursor",
"color": "#FFFFFF",
"blink": 0.5,
"auto_path": [
{ "time": 0.5, "x": 100, "y": 200 },
{ "time": 1.5, "x": 400, "y": 300 },
{ "time": 2.5, "x": 600, "y": 150 }
],
"path_easing": "ease_in_out",
"position": { "x": 200, "y": 200 }
}
FieldTypeDefaultDescription
widthf323.0Cursor width
heightf3240.0Cursor height
colorstring"#FFFFFF"Cursor color
blinkf320.5Blink cycle (0 = no blink)
click_atf64[][]Click animation times (seconds)
auto_patharray[]Waypoints: [{ "time", "x", "y" }]
click_durationf320.3Click animation duration
path_easingstring"ease_in_out""linear", "ease_out", "ease_in_out"

When auto_path is set, clicks trigger at each waypoint. Uses Catmull-Rom spline interpolation for smooth curves.


Line

Simple line from point A to point B. Supports draw_in animation.

{
"type": "line",
"x1": 0, "y1": 0,
"x2": 400, "y2": 200,
"width": 2,
"color": "#58A6FF",
"style": { "animation": [{ "name": "draw_in", "duration": 0.8 }] }
}
FieldTypeDefaultDescription
x1, y1f320.0Start point
x2, y2f32(required)End point
widthf322.0Stroke width
colorstring"#FFFFFF"Line color
dashedf32[]Dash pattern

Rich Text

Multi-styled text with individually styled spans. Each span inherits unset properties from the component's style.

{
"type": "rich_text",
"spans": [
{ "text": "Hello ", "color": "#FFFFFF", "font-weight": "bold" },
{ "text": "World", "color": "#58A6FF", "font-size": 64 }
],
"max_width": 800,
"style": { "font-size": 48, "color": "#FFFFFF" }
}
FieldTypeDefaultDescription
spansarray(required)[{ "text", "color"?, "font-size"?, "font-weight"?, "font-family"?, "font-style"?, "letter-spacing"? }]
max_widthf32Maximum width before wrapping

Scene-Level Features

Virtual Camera

Scenes support a virtual camera with animatable pan, zoom, and rotation.

{
"duration": 5.0,
"camera": {
"x": 0, "y": 0, "zoom": 1.0, "rotation": 0,
"keyframes": [
{ "property": "zoom", "values": [{ "time": 0, "value": 1.0 }, { "time": 3, "value": 1.5 }], "easing": "ease_in_out" },
{ "property": "x", "values": [{ "time": 0, "value": 0 }, { "time": 3, "value": -100 }], "easing": "ease_out" }
]
},
"children": [...]
}
FieldTypeDefaultDescription
xf320.0Camera center X offset
yf320.0Camera center Y offset
zoomf321.0Zoom factor (2.0 = 2x in)
rotationf320.0Rotation in degrees
keyframesarray[][{ "property", "values": [{"time", "value"}], "easing" }]

Animated Background

Scenes can have animated gradient backgrounds. Gradients are interpolated in linear color space with subdivided color stops for smooth dark transitions.

The background field on a scene accepts:

  • String: "#000000" (solid color, backward compat)
  • Object: { "preset": "grid_dots", ... } (inline animated background)
  • Object with $ref: { "$ref": "dots", ... } (named template + overrides + transition)
  • Array: [{ ... }, { ... }] (multiple layered backgrounds)

The legacy animated-background field is still supported.

Background Templates

Define reusable backgrounds at the scenario level and reference them via $ref:

{
"backgrounds": {
"dots": { "preset": "grid_dots", "colors": ["#FF00FF"], "speed": 30, "spacing": 60 }
},
"scenes": [
{
"duration": 3,
"background": { "$ref": "dots", "colors": ["#00FFFF"] }
},
{
"duration": 3,
"background": {
"$ref": "dots",
"colors": ["#FF0000"],
"speed": 50,
"transition": { "duration": 1.0, "easing": "ease_in_out" }
}
}
]
}

When transition is specified, background properties interpolate smoothly from the previous scene's values over the given duration.

Inline Animated Background

{
"duration": 5.0,
"animated-background": {
"colors": ["#667eea", "#764ba2", "#f093fb"],
"speed": 30,
"gradient_type": "linear"
},
"children": [...]
}
FieldTypeDefaultDescription
colorsstring[][]Gradient colors (hex)
speedf3230.0Animation speed
gradient_typestring"linear""linear" or "radial"
presetstring"gradient_shift", "concentric_circles", "grid_dots", "halo"
element_sizef324.0Dot size for grid_dots; stroke width for concentric_circles
spacingf3260.0Element spacing for grid_dots/concentric_circles
countu32Number of circles for concentric_circles (overrides spacing)

Background transition fields (inside $ref entries):

FieldTypeDefaultDescription
$refstringName of a background template defined in backgrounds
transition.durationf64Interpolation duration in seconds from previous scene
transition.easingstring"ease_in_out"Easing function for the interpolation

Animations

All animation effects are defined inside style.animation as a typed array, each discriminated by "name". A single effect (without array) is also accepted.

{
"style": {
"animation": [
{ "name": "fade_in_up", "delay": 0.2, "duration": 0.8 },
{ "name": "glow", "color": "#6366F1", "radius": 20, "intensity": 2.0 },
{ "name": "wiggle", "property": "translate_y", "amplitude": 5, "frequency": 0.8, "seed": 42 }
]
}
}

Effect Types

Effect nameFieldsDescription
preset namedelay, duration, loop, overshootAny of the 39 presets (e.g. fade_in_up, scale_in)
char presetdelay, duration, stagger, granularity, easing, overshootPer-char/word text animation: char_scale_in, char_fade_in, char_wave, char_bounce, char_rotate_in, char_slide_up
glowcolor, radius, intensityLuminous halo effect
wiggleproperty, amplitude, frequency, mode, seed, ...Procedural noise animation
orbitradius_x, radius_y, speed, depth, tilt, ...Elliptical orbital motion with pseudo-3D
keyframeskeyframesCustom keyframe animations
motion_blurintensityMotion blur effect

Components also support a timeline field (array of { "at": f64, "animation": [...] } steps) for multi-phase sequential animations within a scene.

Animation Presets

{
"style": {
"animation": [{ "name": "fade_in_up", "delay": 0.2, "duration": 0.8, "loop": false }]
}
}
FieldTypeDefaultDescription
delayf640.0Delay before animation starts (seconds)
durationf640.8Animation duration (seconds)
loopboolfalseLoop the animation continuously
overshootf640.08Overshoot/anticipation intensity for scale_in/scale_out (0.0 = none)

Entrance Presets

PresetDescription
fade_inFade from transparent
fade_in_upFade in + slide up
fade_in_downFade in + slide down
fade_in_leftFade in + slide from left
fade_in_rightFade in + slide from right
slide_in_leftSlide in from far left
slide_in_rightSlide in from far right
slide_in_upSlide in from below
slide_in_downSlide in from above
scale_inScale up from 0 with overshoot (configurable via overshoot, default 8%)
bounce_inBouncy scale from small to normal
blur_inFade in from blurred
rotate_inRotate + scale from half size
elastic_inElastic underdamped spring scale

Exit Presets

PresetDescription
fade_outFade to transparent
fade_out_upFade out + slide up
fade_out_downFade out + slide down
slide_out_leftSlide out to the left
slide_out_rightSlide out to the right
slide_out_upSlide out upward
slide_out_downSlide out downward
scale_outScale down to 0 with anticipation (configurable via overshoot, default 8%)
bounce_outBouncy scale to small
blur_outFade out with blur
rotate_outRotate + scale to half size

Continuous Presets

Use "loop": true for continuous animation:

PresetDescription
pulseGentle scale oscillation
floatVertical floating motion
shakeHorizontal shake
spin360-degree continuous rotation
float_3dFloating + subtle 3D rotation with perspective

3D Presets

PresetDescription
flip_in_x3D flip around X axis (card flip from top)
flip_in_y3D flip around Y axis (card flip from side)
flip_out_x3D flip out around X axis
flip_out_y3D flip out around Y axis
tilt_in3D tilt entrance (rotate_x + rotate_y)

Stroke Presets

For arrows, connectors, and lines:

PresetDescription
draw_inAnimate draw_progress from 0 to 1 (stroke drawing effect)
stroke_revealdraw_in + fade-in opacity over first 20% of duration

Special Presets

PresetDescription
typewriterProgressive character reveal
wipe_leftSlide in from left with fade
wipe_rightSlide in from right with fade

Custom Keyframe Animations

{
"style": {
"animation": [
{
"name": "keyframes",
"keyframes": [
{
"property": "opacity",
"keyframes": [
{ "time": 0.0, "value": 0.0 },
{ "time": 0.5, "value": 1.0 }
],
"easing": "ease_out"
}
]
}
]
}
}

Animatable properties:opacity, translate_x, translate_y, scale_x, scale_y, scale (both axes), rotation, blur, color, rotate_x, rotate_y, perspective

11 easing functions:linear, ease_in, ease_out, ease_in_out, ease_in_quad, ease_out_quad, ease_in_cubic, ease_out_cubic, ease_in_expo, ease_out_expo, spring

Spring physics (when easing is spring):

{
"easing": "spring",
"spring": { "damping": 15, "stiffness": 100, "mass": 1 }
}

Glow

{
"style": {
"animation": [
{ "name": "glow", "color": "#ff00ff", "radius": 20, "intensity": 2.5 }
]
}
}
FieldTypeDefaultDescription
colorstring"#FFFFFF"Glow color (hex)
radiusf3210.0Blur radius
intensityf321.0Brightness multiplier

Wiggle (Procedural Noise)

Wiggle adds continuous organic movement. Offsets are applied additively on top of presets and keyframes.

{
"style": {
"animation": [
{ "name": "wiggle", "property": "translate_x", "amplitude": 5, "frequency": 3, "seed": 42 },
{ "name": "wiggle", "property": "rotation", "amplitude": 2, "frequency": 2, "seed": 99, "decay": 0.5 }
]
}
}
FieldTypeDefaultDescription
propertystring(required)Property to wiggle (same as animatable properties)
amplitudef64(required)Maximum deviation (pixels for translate, degrees for rotation)
frequencyf64(required)Cycles per second (Hz)
modestring"noise""noise" (layered simplex) or "sine" (pure sine wave)
seedu640Random seed for reproducible results (noise mode only)
octavesu323Noise complexity (noise mode only)
phasef640.0Phase offset
decayf64Exponential decay rate
easingstringRemap noise through an easing curve

Orbit (Circular/Elliptical Motion)

Creates continuous circular or elliptical orbital motion with pseudo-3D depth. Applied additively like wiggle.

{
"style": {
"animation": [
{ "name": "orbit", "radius_x": 30, "radius_y": 20, "speed": 0.5, "depth": 0.15, "tilt": 20 }
]
}
}
FieldTypeDefaultDescription
radius_xf6430.0Horizontal orbit radius
radius_yf6430.0Vertical orbit radius
speedf640.5Revolutions per second
start_anglef640.0Starting angle in degrees
depthf640.15Scale modulation for pseudo-3D
opacity_depthf640.0Opacity modulation for depth
tiltf640.0Orbit plane tilt in degrees
phasef640.0Phase offset (0.0 to 1.0)

Per-Character / Per-Word Text Animation

Animate each character or word independently with staggered timing. Use char_* animation presets inside style.animation on text components.

Char animation presets:char_scale_in, char_fade_in, char_wave, char_bounce, char_rotate_in, char_slide_up

{
"type": "text",
"content": "Hello World",
"style": {
"font-size": 64, "color": "#FFFFFF",
"animation": [{ "name": "char_scale_in", "stagger": 0.03, "duration": 0.4, "delay": 0.2 }]
}
}
FieldTypeDefaultDescription
staggerf640.03Delay between each unit (seconds)
durationf640.4Each unit's animation duration
delayf640.0Initial delay before first unit
easingstring"linear"Easing function
granularitystring"char""char" (per-character) or "word" (per-word)
overshootf640.08Overshoot intensity for char_scale_in/char_bounce (0.0 = none)

Per-word mode ("granularity": "word") splits text by whitespace and animates each word as a unit. Use larger stagger values (0.1–0.3s) for word reveals:

{
"type": "text",
"content": "One platform to rule them all",
"style": {
"font-size": 56, "color": "#FFFFFF", "font-weight": "bold",
"animation": [{ "name": "char_fade_in", "stagger": 0.15, "duration": 0.5, "granularity": "word" }]
}
}

3D Perspective Transforms

Any component can be rendered with true 3D perspective using keyframe animations on rotate_x, rotate_y, and perspective properties:

{
"style": {
"box-shadow": { "color": "#00000060", "offset_x": 0, "offset_y": 20, "blur": 60 },
"animation": [{
"name": "keyframes",
"keyframes": [
{ "property": "rotate_x", "keyframes": [{ "time": 0, "value": 20 }, { "time": 2, "value": 8 }], "easing": "ease_out" },
{ "property": "rotate_y", "keyframes": [{ "time": 0, "value": -15 }, { "time": 2, "value": -5 }], "easing": "ease_out" },
{ "property": "perspective", "keyframes": [{ "time": 0, "value": 800 }, { "time": 2, "value": 800 }], "easing": "linear" }
]
}]
}
}

Uses a Skia M44 4x4 matrix for real 3D rendering. Components with box-shadow get 3D adaptive shadows — the shadow automatically shifts and scales based on tilt angles.

Timeline Sequencing

Define sequential animation phases within a single scene using the timeline field:

{
"style": {
"animation": [{ "name": "fade_in_up", "duration": 0.6 }],
"timeline": [
{ "at": 2.0, "animation": [{ "name": "shake", "duration": 0.5 }] },
{ "at": 4.0, "animation": [{ "name": "fade_out", "duration": 0.8 }] }
]
}
}

Each step activates at step.at seconds, with animations resolved relative to that time. Steps merge additively with base animations.

Motion Blur

{
"style": {
"animation": [
{ "name": "motion_blur", "intensity": 0.8 }
]
}
}

Renders multiple sub-frames and composites them for physically-correct motion blur.


Output Formats

FormatCommandRequires
MP4 (H.264 10-bit)rustmotion render in.json -o out.mp4ffmpeg (auto-detected)
MP4 (H.264 8-bit)rustmotion render in.json -o out.mp4Built-in (fallback without ffmpeg)
MP4 (H.265)rustmotion render in.json -o out.mp4 --codec h265ffmpeg
WebM (VP9)rustmotion render in.json -o out.webm --codec vp9ffmpeg
MOV (ProRes)rustmotion render in.json -o out.mov --codec proresffmpeg
Animated GIFrustmotion render in.json -o out.gif --format gifBuilt-in
PNG Sequencerustmotion render in.json -o frames/ --format png-seqBuilt-in
Single Framerustmotion render in.json --frame 0 -o preview.pngBuilt-in

Transparency is supported with --transparent for PNG sequences, WebM (VP9), and ProRes 4444.

Gradient quality: When ffmpeg is available, H.264 uses 10-bit color depth (yuv420p10le, high10 profile) which greatly reduces banding on dark gradients. For maximum quality, use --codec prores. The built-in openh264 encoder (fallback without ffmpeg) outputs 8-bit only.


Full Example

{
"version": "1.0",
"video": {
"width": 1080,
"height": 1920,
"fps": 30,
"background": "#0f172a"
},
"scenes": [
{
"duration": 4.0,
"layout": { "align_items": "center", "justify_content": "center", "gap": 32 },
"children": [
{
"type": "shape",
"shape": "rounded_rect",
"size": { "width": 900, "height": 520 },
"style": {
"fill": {
"type": "linear",
"colors": ["#6366f1", "#8b5cf6"],
"angle": 135
},
"border-radius": 32,
"animation": [{ "name": "scale_in", "duration": 0.6 }]
}
},
{
"type": "icon",
"icon": "lucide:rocket",
"size": { "width": 80, "height": 80 },
"style": {
"color": "#FFFFFF",
"animation": [{ "name": "fade_in_up", "delay": 0.3, "duration": 0.6 }]
}
},
{
"type": "text",
"content": "Ship Faster",
"style": {
"font-size": 64,
"color": "#FFFFFF",
"font-weight": "bold",
"text-align": "center",
"animation": [{ "name": "fade_in_up", "delay": 0.5, "duration": 0.6 }]
}
},
{
"type": "text",
"content": "Build motion videos in Rust.\nNo browser needed.",
"max_width": 700,
"style": {
"font-size": 32,
"color": "#CBD5E1",
"text-align": "center",
"line-height": 1.5,
"animation": [{ "name": "fade_in_up", "delay": 0.7, "duration": 0.6 }]
}
}
]
},
{
"duration": 3.0,
"background": "#1e293b",
"transition": { "type": "iris", "duration": 0.8 },
"layout": { "align_items": "center", "justify_content": "center" },
"children": [
{
"type": "text",
"content": "No browser needed.",
"style": {
"font-size": 56,
"color": "#e2e8f0",
"text-align": "center",
"animation": [{ "name": "typewriter", "duration": 1.5 }]
}
}
]
}
]
}

Architecture

  • Rendering: skia-safe (same engine as Chrome/Flutter)
  • Video encoding: openh264 (Cisco BSD, compiled from source) + ffmpeg (optional, for H.265/VP9/ProRes)
  • Audio encoding: AAC via minimp4
  • SVG rendering: resvg + usvg
  • Icon rendering: Iconify API (200k+ icons)
  • GIF decoding/encoding: gif crate
  • MP4 muxing: minimp4
  • JSON Schema: schemars (auto-generated from Rust types)
  • Parallelism: rayon (multi-threaded frame rendering)

Architecture

rustmotion uses a Flutter-inspired measure → layout → paint pipeline built on Skia:

src/
├── components/ # 51 components (each implements Widget trait)
│ ├── chart/ # Chart sub-modules (bar, line, pie, radar, etc.)
│ └── *.rs # One file per component
├── engine/
│ ├── render/ # Render pipeline (component, scene, background, transforms)
│ ├── codeblock/ # Codeblock rendering (highlight, chrome, reveal, diff)
│ ├── animator.rs # Animation resolver, easing, spring solver
│ └── renderer.rs # Skia drawing primitives
├── schema/ # Data models
│ ├── scenario.rs # Scenario, View, Scene, VideoConfig
│ ├── style.rs # LayerStyle, FontWeight, layout types
│ ├── background.rs # Animated backgrounds
│ ├── animation.rs # EasingType, presets
│ └── video.rs # AnimationEffect, shapes, fills
├── layout/ # Flex/grid layout engines
├── traits/ # Widget, Styled, Animatable, Timed, Container
└── macros.rs # impl_traits! macro

Every component implements the Widget trait:

traitWidget{fnpaint(&self,canvas:&Canvas,ctx:&PaintContext) -> Result<()>;fnmeasure(&self,constraints:&Constraints) -> (f32,f32);fnlayout(&self,constraints:&Constraints) -> LayoutNode;}

PaintContext provides timing, layout dimensions, parent info, and resolved animated properties in a single struct.

License

MIT

About

A CLI tool that renders motion design videos from JSON scenarios. No browser, no Node.js — just a single Rust binary.

Topics

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages