How do I add cinematic motion to templates?
Choreographed text reveals, counters, camera moves, film texture and per-render motion control for video templates, via Studio or the API.
Published
Motion is the cinematic layer on top of animations: staggered text reveals, count-up stats, relational choreography, press and sleep beats, SVG draw-on, camera moves and film texture. Enter/exit/ambient animations describe how a single layer arrives, lives and leaves; motion describes how the whole page performs.
Motion is designed API-first: everything on this page can be authored as JSON (by hand or by an AI agent through the Orshot MCP), edited in Studio, and tuned per render through modifications.
How it activates#
Adding a motion object to any element (or to a page) switches that page's video rendering to the motion engine. Pages without motion render exactly as before, and legacy transitions on other layers keep working on a motion page. Static formats (png, jpg, pdf, …) ignore motion entirely and render the final designed state.
{
"id": "stat",
"type": "text",
"content": "0",
"motion": {
"count": { "from": 0, "to": 99.9, "duration": 1.4, "decimals": 1, "suffix": "%" }
}
}Element motion#
All keys and sub-params are optional unless marked. Out-of-range values are clamped, never rejected.
| Key | Params (defaults) | Effect |
|---|---|---|
reveal | by: "word"|"char" (word), stagger: 0.07, duration: 0.7, dy: 120 | Staggered text reveal. The headline entrance. Text layers only, up to 400 words |
typewriter | duration: 1.6, delay: 0 | Text types itself. Plain text layers only |
count | to (required), from: 0, duration: 1.4, decimals: 0, prefix, suffix | Number counts up in place. The stat primitive |
wake | duration: 0.6, from: up|down|left|right|none, distance: 34, offset: 0 | Directional entrance. Under 0.3s reads as a hard cut-in |
after | element (required, an element id), offset: 0 | Start relative to another element finishing its entrance. The choreography primitive: chain layers instead of hand-timing showAt |
pulse | from: 0.97, to: 1.03, rate: 1.6, axis: "y"|"both" | Ambient scale breathing (a live badge, a recording dot) |
float | amp: 6, rate: 2.4 | Ambient vertical drift |
press | at (required, seconds), dip: 6 | One-shot press dip in pixels: a button being clicked, on its exact frame |
sleep | at (required), duration: 0.45 | The layer exits early, freeing the stage |
drawIn | duration: 0.9, delay: 0 | SVG strokes draw themselves. SVG layers only |
svg | animate: true, targets: [{ selector, drawIn?, spin?, pulse? }] | Animate inside an SVG layer by CSS selector: animated textured backgrounds, spinning marks |
keyframes | [{ at (required), x, y, scale, rotate, opacity }] | Explicit track when no behavior fits. Offsets are from the layer's designed position |
after chains adapt automatically: if a render-time modification makes a headline longer, everything chained after it re-times to the real reveal length.
Page motion#
Set on the page itself as pages_data[n].motion:
| Key | Params | Effect |
|---|---|---|
look | grain: 0–0.2, vignette: 0–0.6 | Film texture over the whole page. Grain 0.04–0.08 and vignette 0.2–0.35 are tasteful ranges. Also editable in Studio from the timeline timer popover |
camera | moves: [{ at, duration, x, y, zoom, ease }] | Whole-canvas camera. Every layer moves together; the grain/vignette stay fixed like a real lens |
Camera moves are absolute-time (at and duration both required), play in sequence, and the camera holds where a move leaves it. To return, author an explicit move back to zoom: 1, x: 0, y: 0. Keep moves sparse and motivated: one push-in while a key element animates beats constant drift.
Studio controls#
Select a layer and open the Animate tab:
- Start (in the Visibility section) sets
after: choose "After layer" plus an Offset instead of computingShow Atby hand. Layers that already depend on the current one are excluded, so you cannot create a cycle - Behaviors hosts Count up, Press, Leave early (
sleep) and Draw in - Look (grain and vignette) lives in the timeline's timer popover
- The editor preview plays the exact same engine the renderer uses, so scrubbing the timeline shows the true output
Camera, wake, keyframes and svg targets are JSON/API-only by design.
Validation and warnings#
Template create and update validate motion at write time:
- Structural problems fail with a 400 listing each issue: a missing or cyclic
afterreference, a non-numericcount.to, more than 200 animated layers or 2000 tweens per page, over 120s - Unknown or misspelled keys never fail. They are ignored at render and returned in the response
warningsarray with a suggestion (unknown motion param 'pluse' — did you mean 'pulse'?). Always readwarnings: a warned param is doing nothing - Renders themselves never fail on motion. A page with an uncompilable plan falls back to rendering its legacy animations
Per-render motion control#
Every motion param is tunable at render time through modifications, using the stored schema's own path after a .motion. prefix:
{
"templateId": "your-template-id",
"modifications": {
"weekly_stat": "742",
"cta.motion.press.at": 3.2,
"hero.motion.wake.duration": 0.8,
"badge.motion.pulse": null,
"motion.look.grain": 0.1,
"motion.camera.moves": [{ "at": 2, "duration": 1.5, "zoom": 1.06 }]
}
}layerId.motion.<path>overrides (or adds) element motion;motion.<path>orpageN@motion.<path>addresses the pagenullremoves a behavior for that render- Array params (
camera.moves,keyframes,svg.targets) take the whole array as the value - Count inference: modifying a count layer's parameterized content to a number makes the counter count to that number.
"weekly_stat": "742"is all a data-driven stat needs - Unknown paths come back in the render response
warnings(and theX-Orshot-Warningsheader) with a did-you-mean; values are clamped exactly like stored params
Recipes#
- Results story — stat layers with
countandsuffix, supporting rows chained withafter, one camera push at the payoff - Launch hero — headline
reveal, subtitleafterthe headline (offset 0.15), cardswakeup 0.15s apart, CTA withpulseand apressat the end - Live badge —
pulseon a dot shape,floaton its card - Animated background — a full-canvas SVG layer with
svg.targetsdrawing paths in and spinning a ring, pluslook.grain0.05
For the machine-readable version of this page (agents), see the orshot://template-design-spec MCP resource.
Ready to automate?
Start rendering images, PDFs and videos from your templates in under 2 minutes. Free plan, no credit card.
Get your API key- Image, PDF and video generation via API
- Visual editor with AI and smart layouts
- Zapier, Make, MCP and 50+ integrations
- White-label embed for your own app
- 30 free credits — no credit card required