Template JSON reference

Every field of a template's pages_data, with types, limits and renderer rules, for creating or updating templates through the API or an AI agent.

Updated

A template's pages_data is an array of pages. Each page has a canvas, layers (elements) and, for video, audio tracks and subtitles. A film also has a template-level film object, sent next to pages_data; see Film. This page lists the fields, then the rules the renderer follows. For how the pieces fit together, start with Template anatomy.

Field reference#

Page#

FieldTypeRequiredDescription
idstringPage id. Stays the same when pages are added, moved or removed.
namestringPage name shown in the editor.
canvasobjectPage size and background. See Canvas.
elementsobject[]The page's layers. See Element.
thumbnail_urlstringPreview image of the page. Read only: set by Orshot.
audioTracksobject[] | nullThe page soundtrack, mixed into mp4 and webm renders. Each track has one source: an existing url, generated narration (tts) or a generated sound (sfx). A render is refused when a page has more tracks than the plan allows per page. See Audio track.
subtitleobject | nullBurned-in captions for video renders. source "auto-audio" captions the first enabled audio track that is not a sound effect. Render-time videoOptions subtitle* keys override these. See Subtitle.
videoDurationnumber | nullSaved video length of this page in seconds. A render's videoOptions.duration overrides it. In a film it is the scene's length.
transitionInobject | nullFilms only: how this page starts from the previous one. Leave it out (or null) for a hard cut; page 1 has none. See Page transition and Film.
motionobjectPage motion: look, camera and groups. Any motion on a page switches it to the motion timeline. See Motion.

Canvas#

FieldTypeRequiredLimitsDescription
widthnumberYesmin 1, max 5000Canvas width in pixels.
heightnumberYesmin 1, max 5000Canvas height in pixels.
backgroundColorstringPage background: a color or a CSS gradient.
backgroundImagestringBackground image URL.
borderWidthnumberPage border width in pixels.
borderColorstringPage border color.
borderStylestringPage border style, such as solid or dashed.

Element#

FieldTypeRequiredDescription
idstringLayer id, unique among the layers of its page. A short readable string such as "headline" is fine. Generated when left out; keep it when you send the design again so references to it stay valid.
type"text" | "image" | "element" | "shape" | "video" | "waveform"YesLayer type. element is an SVG layer (with isSvg: true); waveform draws a page audio track.
videoOptionsobject[video layers] Playback options for the clip: loop, muted (default true), trim_start_time and trim_end_time, duration. See Video layer options.
waveTrackinteger[waveform elements only] Zero-based page audioTracks index to visualize (default 0)
shapeType"rectangle" | "circle" | "arrow" | "line"Shape kind, for type shape.
namestringLayer name shown in the editor.
contentstringText for text layers; a URL for image and video layers; a data:image/svg+xml URI or an .svg URL for SVG layers.
positionobjectYes{ x, y } in pixels from the page's top-left corner. Create and update also accept flat x and y.
dimensionsobjectYes{ width, height } in pixels. Create and update also accept flat width and height.
styleobjectStyle keys. See Element style.
zIndexnumberStacking order. Higher draws on top.
rotationnumberRotation in degrees.
parameterizablebooleantrue makes the layer a dynamic parameter that renders can change.
parameterIdstringThe key renders use to change this layer. Letters, digits and underscores; no spaces, @, . or :. Layers of the same type on a page may share an id. On a multi-page template the render key gets a page prefix, such as page2@headline; do not put the prefix in parameterId.
parameterTypestringWhat the parameter changes: text, imageUrl, videoUrl, fill, color, stroke or backgroundColor. It defaults from the layer type.
isSvgbooleantrue for SVG layers (type element).
motionobjectMotion for video pages: reveal, typewriter, count, wake, after, pulse, float, press, sleep, drawIn, svg and keyframes. Any motion switches the page to the motion timeline; static renders ignore it. See Motion.
transitionsobjectEnter, exit and ambient animation plus the showAt and hideAt visibility window, for video and GIF renders. showAt and hideAt sit next to enter and exit, not inside them. Static renders show the final state. See Transitions.

Element style#

FieldTypeRequiredLimitsDescription
backgroundColorstring[image/element] Background color or gradient behind the layer. Text ignores it and takes textBackgroundColor; shapes ignore it and paint fill (black when fill is missing).
backgroundImagestring[all] Background image URL or CSS gradient
backgroundSizestring[all] "cover" | "contain" | "100% 100%": how background image fills the element
backgroundPositionstring[all] "center" | "top left" etc.: background image position
borderRadiusstring[all] "0px", "12px", "50%": corner rounding
borderWidthstring[all] "0px", "2px": border thickness
borderColorstring[all] Border color as hex or rgba
boxShadowXstring[all] "0px": horizontal shadow offset
boxShadowYstring[all] "4px": vertical shadow offset
boxShadowBlurstring[all] "8px": shadow blur radius
boxShadowColorstring[all] "rgba(0,0,0,0.2)": shadow color
mixBlendModestring[all] "normal" | "multiply" | "screen" | "overlay" etc.
filterstring[all] CSS filter string e.g. "blur(5px)", "grayscale(100%)"
dropShadowXstring[all] "0px": drop shadow horizontal offset (CSS filter)
dropShadowYstring[all] "0px": drop shadow vertical offset
dropShadowBlurstring[all] "0px": drop shadow blur radius
dropShadowColorstring[all] "rgba(0,0,0,0.2)": drop shadow color
textMode"overflow" | "truncate" | "fit"overflow (default) lets text run past the box, truncate cuts it with an ellipsis, fit shrinks it down to minFontSize. A fourth mode, "flow", splits long text across extra pages; see Flowing content.
minFontSizestring[text only] Minimum font size when textMode is "fit": string like "8"
fontFamilystring[text only] Google Font name e.g. "Inter", "Prata", "DM Sans", "Playfair Display"
fontSizestring[text only] Font size with px e.g. "48px", "72px"
fontWeightstring[text only] "400", "700", "900"
fontStylestring[text only] "normal" | "italic"
colorstring[text, line and arrow] Text or line color: hex or gradient.
textAlignstring[text only] "left" | "center" | "right" | "justify"
verticalAlignstring[text only] "flex-start" (top) | "center" (middle) | "flex-end" (bottom)
lineHeightnumber[text only] Line height multiplier e.g. 1.2, 1.4
letterSpacingstring[text only] "0px", "2px": letter spacing
textTransformstring[text only] "none" | "uppercase" | "lowercase" | "capitalize"
textDecorationstring[text only] "none" | "underline" | "line-through"
paddingXstring[text only] Horizontal space between the text and its background highlight (textBackgroundColor), default "6px". It does not move the text.
paddingYstring[text only] Vertical space between the text and its background highlight, default "6px". It does not move the text.
textStrokeWidthstring[text only] "0px", "2px": text outline/stroke width
textStrokeColorstring[text only] "#000000": text outline/stroke color
textBackgroundColorstring[text only] The text box's background colour (a label, pill or highlight box). style.backgroundColor does nothing on text.
textBackgroundRadiusstring[text only] "0px", "4px": border radius of text background highlight
textBorderWidthstring[text only] "0px", "2px": border around the text element
textBorderColorstring[text only] Border color for text element
fillstring[shape/waveform] Shape paint or waveform bar color. For shapes, backgroundColor does nothing: use a color, rgba, or gradient such as "rgba(0,0,0,0.5)" or "linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.9) 100%)".
strokestring[shape only] Stroke color (rectangle/circle shapes)
strokeWidthstring[shape only] "0px", "2px": stroke width
lineStylestring[shape only] "solid" | "dashed" | "dotted": for line/arrow shapeType
objectFitstring[image only] "contain" | "cover" | "fill": how image fills the element
objectPositionstring[image only] "center" | "left top" | "right bottom" etc.
clipPathstring[image only] CSS clip-path for masking images
svgColorstring[SVG only] Hex color to tint SVG elements (type: element with isSvg: true)
barCountintegermin 4, max 160[waveform only] Number of bars, 4-160 (default 48)
barGapnumbermin 0, max 24[waveform only] Gap between bars in px, 0-24 (default 3)
waveAlign"center" | "bottom"[waveform only] "center" grows bars from the middle; "bottom" grows them from the baseline
minBarHeightnumbermin 0, max 60[waveform only] Quiet/minimum bar height as a percentage, 0-60 (default 8)

Transitions#

FieldTypeRequiredLimitsDescription
enterobjectEntrance animation
exitobjectExit animation
ambientobjectAmbient motion that plays for the layer's whole visible life, starting when its enter finishes. It combines with enter and exit. See Ambient transition.
showAtnumbermin 0Seconds from the start when the layer appears and its enter plays. A sibling of enter on transitions, not inside enter.
hideAtnumber | nullmin 0Seconds from the start when the exit starts; the layer is gone at hideAt + exit.duration. null or left out keeps it to the end (a video layer stays until its trimmed clip ends). A sibling of exit, not inside it.

Enter transition#

FieldTypeRequiredLimitsDescription
type"none" | "fadeIn" | "slideInLeft" | "slideInRight" | "slideInUp" | "slideInDown" | "scaleIn" | "scaleInUp" | "scaleInDown" | "rotateIn" | "flipInX" | "flipInY" | "textReveal" | "typewriter"YesEnter animation. textReveal and typewriter are staggered per word or character and work on text layers only. flipInX and flipInY are 3D flips.
durationnumbermin 0Duration in seconds (default: 0.5)
easing"linear" | "ease" | "ease-in" | "ease-out" | "ease-in-out" | "cubic-bezier(0.175, 0.885, 0.32, 1.275)" | "spring(180, 12, 0)" | "spring(stiffness, damping, velocity)"Easing (default "ease-out"). "cubic-bezier(0.175, 0.885, 0.32, 1.275)" = classic bounce; "spring(stiffness, damping, velocity)" = physics spring, e.g. "spring(180, 12, 0)" (stiffness 20-400, damping 2-40, velocity -20..20).
reversebooleanPlay the animation direction reversed
customValuesobjectOverride the preset's intensity instead of using the fixed default. Per-preset intensity knobs (all optional, defaults in parens): - opacity: start/end opacity 0-1 (0): all fade/slide/scale/rotate/flip presets - offset: slide distance as % of element size, -500..500 (±100; scaleInUp/Down ±50; textReveal rise 100): sign sets direction - scale: start/end scale 0-5 (scaleIn 0: set 1.15 for a subtle zoom-punch to 1.0; scaleInUp/Down 0.5) - angle: rotation degrees (rotateIn -180, rotateOut 180, range ±720; flipInX/Y ±90, range ±180) - granularity: "word" | "char": textReveal/typewriter split unit (STRING value) - overlap: 10-90: textReveal per-item duration % of total (40); lower = tighter stagger

Exit transition#

FieldTypeRequiredLimitsDescription
type"none" | "fadeOut" | "slideOutLeft" | "slideOutRight" | "slideOutUp" | "slideOutDown" | "scaleOut" | "scaleOutUp" | "scaleOutDown" | "rotateOut" | "flipOutX" | "flipOutY" | "textRevealOut"YesExit animation; it starts at hideAt. textRevealOut works on text layers only.
durationnumbermin 0Duration in seconds (default: 0.5)
easing"linear" | "ease" | "ease-in" | "ease-out" | "ease-in-out" | "cubic-bezier(0.175, 0.885, 0.32, 1.275)" | "spring(180, 12, 0)" | "spring(stiffness, damping, velocity)"Easing (default "ease-in"). Named value, bounce cubic-bezier, or "spring(stiffness, damping, velocity)".
reversebooleanPlay the animation direction reversed
customValuesobjectOverride the preset's END state intensity. Per-preset intensity knobs (all optional, defaults in parens): - opacity: start/end opacity 0-1 (0): all fade/slide/scale/rotate/flip presets - offset: slide distance as % of element size, -500..500 (±100; scaleInUp/Down ±50; textReveal rise 100): sign sets direction - scale: start/end scale 0-5 (scaleIn 0: set 1.15 for a subtle zoom-punch to 1.0; scaleInUp/Down 0.5) - angle: rotation degrees (rotateIn -180, rotateOut 180, range ±720; flipInX/Y ±90, range ±180) - granularity: "word" | "char": textReveal/typewriter split unit (STRING value) - overlap: 10-90: textReveal per-item duration % of total (40); lower = tighter stagger

Ambient transition#

FieldTypeRequiredLimitsDescription
type"none" | "kenBurns" | "pulse" | "float" | "drift" | "wiggle" | "shake" | "slowRotate" | "breathe"YesAmbient animation. Loop types repeat every duration: pulse, float, drift, wiggle, shake, slowRotate, breathe. kenBurns runs once across the layer's visible window and ignores duration.
durationnumbermin 0Loop length in seconds (default 4). kenBurns ignores it and spans the layer's visible window.
easing"linear" | "ease" | "ease-in" | "ease-out" | "ease-in-out" | "cubic-bezier(0.175, 0.885, 0.32, 1.275)" | "spring(180, 12, 0)" | "spring(stiffness, damping, velocity)"Easing. Defaults to "linear" for shake and slowRotate (a seamless loop needs linear) and "ease-in-out" for the rest.
customValuesobjectOverride the type's intensity. Per-type intensity knobs (all optional, defaults in parens): - kenBurns: zoom = end scale 0.5-2 (1.12), panX / panY = drift across the frame in %, -30..30 (0) - pulse: amount = peak scale 1-1.5 (1.04) - float: offset = vertical travel in px, 1-100 (12) - drift: offset = circular radius in px, 1-100 (10) - wiggle: angle = sway in degrees, 0.5-30 (2) - shake: offset = horizontal jitter in px, 1-40 (4) - breathe: opacity = dimmest point 0.1-1 (0.85), scale = peak scale 1-1.3 (1.02) - slowRotate: no knobs: use duration for speed

Video layer options#

FieldTypeRequiredDescription
loopbooleanReplay the clip when it ends (default false)
mutedbooleanMute the clip's own audio. Default true; set false to keep the clip's sound, which mixes with the page audio tracks.
trim_start_timenumber | stringClip trim start: seconds (4.5) or "MM:SS". Applies only with trim_end_time, and only to the template's own clip. At render time use the camelCase key "paramId.trimStart".
trim_end_timenumber | stringClip trim end: seconds or "MM:SS". Applies only with trim_start_time. At render time use "paramId.trimEnd".
durationnumber | nullThe source clip's own length in seconds. Set it when known; page length is worked out from it.

Audio track#

FieldTypeRequiredLimitsDescription
idstringTrack id (uuid; the editor assigns one if missing)
urlstring | nullExisting public audio URL, such as a brand-audio direct_url. Omit when authoring a new tts or sfx source; resolved generated tracks may later contain both their recipe and cached URL.
ttsobjectGenerated narration. Orshot synthesizes and caches it when the template is saved or rendered. See Narration (tts).
sfxobjectGenerated sound or ambient bed, not speech. Orshot synthesizes and caches it when the template is saved or rendered. See Sound effect (sfx).
filenamestring | nullDisplay filename
labelstringDisplay label, e.g. "Audio 1"
durationnumber | nullSource file duration in seconds: set it when known (URL-added audio can't be probed automatically)
volumenumbermin 00-1+, default 1. 0 disables the track.
fadeInnumbermin 0Fade-in length in seconds (default 0)
fadeOutnumbermin 0Fade-out length in seconds (default 0)
trimStartnumbermin 0Start inside the SOURCE file, seconds (default 0): use with trimEnd to bundle only the "best part" of a track
trimEndnumber | nullmin 0End inside the source file, seconds (null = play to the end)
offsetnumbermin 0Placement on the VIDEO timeline, seconds from the start (default 0)
loopbooleanLoop the (trimmed/generated) track to fill the video. Usually true for ambient SFX and false for narration.
mutedbooleanMute this track (default false)
enabledbooleanTrack on/off (default true)

Narration (tts)#

FieldTypeRequiredLimitsDescription
textstringYesNarration script. Up to 40,000 characters with flash, 10,000 with multilingual and 5,000 with expressive; your plan may set a lower cap. Keep it short enough for the page's videoDuration.
voiceIdstringmin length 1Voice id. Leave it out to use the default voice.
modelId"flash" | "expressive" | "multilingual"Speech model: flash (fast, the default), expressive or multilingual. Premium models depend on the plan.
stabilitynumbermin 0, max 1Delivery consistency, 0-1. Leave it out for the voice default.
similaritynumbermin 0, max 1Voice similarity, 0-1. Leave it out for the voice default.
stylenumbermin 0, max 1Expressive style strength, 0-1. Leave it out for the voice default.
speakerBoostbooleanSpeaker clarity boost. Leave it out for the voice default.
parameterIdstring (no spaces, @, . or :)Makes the spoken script a render parameter. With "voiceover", a render sends modifications: { "voiceover": "New script" }; on a multi-page template the render key is "page1@voiceover".

Sound effect (sfx)#

FieldTypeRequiredLimitsDescription
promptstringYesmin length 1, max length 450Describe a non-verbal sound or ambient bed, e.g. "warm modern technology pulse, instrumental, no vocals, seamless loop".
secondsintegermin 1, max 22Generated source length in seconds (1-22, default 12). Set loop:true on the track when it should fill a longer page.

Subtitle#

FieldTypeRequiredDescription
urlstring | nullSubtitle .srt/.vtt URL, or a media URL to transcribe (when source is "upload")
filenamestring | nullDisplay filename.
source"upload" | "auto-audio""auto-audio" captions the page's first enabled audio track that is not a sound effect (narration reuses its word timing; other audio is transcribed at render). "upload" uses url.
enabledbooleandefault true
timingOffsetnumber | nullSeconds added to every cue (+ = captions later). Nudge for transcription timing drift on dense music.
mode"phrase" | "word-by-word" | null"word-by-word" = karaoke-style single highlighted word
maxWordsPerLineinteger | nullWords per caption line.
maxCharsPerLineinteger | nullCharacter budget per caption line. When set, it overrides maxWordsPerLine; 1 creates a one-word-at-a-time look.
fontSizestring | number | nulle.g. "30px" (numbers get px appended); null = renderer default
fontFamilystring | nullBrand fonts work by family name
fontWeightstring | number | nullCaption font weight.
colorstring | nullCaption text color.
backgroundstring | nullHighlight-block background, e.g. "rgba(0,0,0,0.6)"
position"top" | "center" | "bottom" | nullWhere captions sit on the frame.
offsetstring | number | nullDistance from the top or bottom edge. Ignored when position is center.
paddingXstring | number | nullHorizontal padding inside the caption background.
paddingYstring | number | nullVertical padding inside the caption background.
borderRadiusstring | number | nullCorner rounding of the caption background.
shadowXnumber | nullCaption text shadow, horizontal offset.
shadowYnumber | nullCaption text shadow, vertical offset.
shadowBlurnumber | nullCaption text shadow blur.
shadowColorstring | nullCaption text shadow color.
wordSpacingstring | number | nullSpace between caption words.
preset"pill" | "minimal" | "karaoke" | "bold" | "outline" | "neon" | nullSet by Studio: the caption preset that was applied. It does not apply a look on its own; the styling keys do.
wordReveal"appear" | "highlight" | "align" | nullword-by-word only. "highlight" dims the full line and lights up the current word; "appear" pops each word in; "align" reveals spoken words and re-centers the growing line.
split"word" | "letter" | nullReveal granularity for word-by-word captions. "letter" reveals each letter; default "word".
activeColorstring | nullColor of the current word in word-by-word mode: the karaoke highlight.
activeBackgroundstring | nullBackground behind the current word in word-by-word mode.
textTransformstring | null"uppercase" | "lowercase" | "capitalize"
strokeWidthnumber | nullCaption outline thickness in px (default 0).
strokeColorstring | nullOutline color (default "#000000")

Page transition (transitionIn)#

FieldTypeRequiredLimitsDescription
type"none" | "fade" | "slide" | "push" | "wipe" | "iris" | "clockWipe" | "flip" | "cube" | "whip" | "blurSwap" | "zoom" | "colorFlood" | "dip" | "grow" | "match" | "zoomThrough"How this page starts from the previous one. Overlapping: fade, dip, slide, push, wipe, iris, clockWipe, flip, cube, whip, blurSwap, zoom, colorFlood. Continuity (a layer of the previous page carries the cut): grow (+from), zoomThrough (+target), match (+pairs). "none" = hard cut, used with overlay. Omit transitionIn for a plain cut.
durationnumbermin 0, max 3Seconds, 0-3. Defaults per type: whip 0.6; fade, slide, push and wipe 0.9; dip, iris, blurSwap, zoom, clockWipe, flip, cube and zoomThrough 1.0; colorFlood and match 1.1; grow 1.2. Must stay under half of the shorter neighbouring scene. Both scenes play during it, so the film is shorter by this amount.
easestringmin length 1, max length 120Ease names such as power2.inOut, expo.out, sine.in or back.out, "none" or "linear", or cubic-bezier(). Unknown names play as power2.inOut.
direction"left" | "right" | "up" | "down"For slide, push, wipe, flip, cube, whip. Default left (the new page comes from the right).
colorstring (CSS color)min length 1, max length 100For dip (default "#000000") and colorFlood (default "#111111"): the colour the cut passes through.
originobjectFor iris, clockWipe, zoom and colorFlood: the point the effect opens from, { x, y } as fractions of the frame (default 0.5, 0.5).
fromstringmin length 1, max length 200grow only, REQUIRED: the id of the layer on the PREVIOUS page that grows to fill the frame and becomes this page (a card, a stamp, a phone). Falls back to zoom if the layer is gone.
targetstringmin length 1, max length 200zoomThrough only, REQUIRED: the id of the layer on the PREVIOUS page the camera dives through (a door, a screen, a window) into this page.
pairsobject[]max 24 itemsmatch only, up to 24: each pair moves from its place on the previous page to its place on this page. Default: layers that have the SAME id on both pages. A paired layer on this page must not have its own entrance (wake, reveal, typewriter, transitions.enter); it arrives through the morph.
others"clear" | "dissolve"match only: what the unpaired layers do. "clear" (default) fades the old ones out first and the new ones in last; "dissolve" crossfades everything.
reveal"iris" | "fade"match only: how this page appears around the morph. "iris" (default) opens a circle from behind the paired layer; "fade" crossfades.
overlayobjectAn effect laid over a HARD cut: { "type": "none", "overlay": { "type": "flash" } }. Never together with a transition type; never changes timing.

Film (template level)#

FieldTypeRequiredLimitsDescription
v1YesAlways 1.
backgroundstring (CSS color)min length 1, max length 100CSS colour shown under the scenes while a transition reveals part of the frame (slide, push, zoom, grow). Default: page 1's canvas backgroundColor, else #000000. Set it to the film's base colour.
outputobjectEncoding: { profile: "default" | "film", fps: 24 | 25 | 30 }. "film" is the higher quality tier and usually a smaller file.
tracksobject[]max 8 itemsUp to 8 tracks that run across scenes: audio (music, ambience) and layers (a logo, a progress bar, a background video under the cuts). Page audioTracks still play inside each scene. See Film.

Elements#

Note. Each element: { id, type, position: { x, y }, dimensions: { width, height }, style: { ... }, zIndex, content }. position and dimensions are nested objects. Send style lengths as px strings ("48px"), fontWeight and minFontSize as unitless strings ("700", "8"), and lineHeight as a number multiplier (1.2).

Types

  • text
  • image
  • shape (shapeType: rectangle|circle|arrow|line)
  • element (inline SVG, isSvg:true)
  • video
  • waveform (visualizes a page audio track)

Parameterizable. Set parameterizable: true, a parameterId and a parameterType (text, imageUrl, videoUrl, backgroundColor, fill, color or stroke). A parameterId uses letters, digits and underscores, such as "headline"; it cannot contain spaces, @, . or :. Layers of the same type on a page may share a parameterId, so one value fills them all; a clash with a layer of another type is renamed with _1, _2. On a multi-page template the render key gets the page prefix, such as page2@headline. The prefix goes on the render key only, never in parameterId.

Opacity. Layer opacity is style.opacity, from 0 to 1.

Flat fields. Create and update also accept flat x, y, width and height on a layer (saved as position and dimensions) and src or src_url (saved as content).

Text#

Line breaks. In plain text use \n for line breaks; <br> also works. Inside rich text (content with tags such as <span> or <b>) \n is not converted, so use <br> there. Use a single <p> or none: several <p> blocks overlap.

Fit. style.textMode: "fit" with style.minFontSize (such as "8") shrinks the text to fit its box when a render sends longer copy.

Align. style.verticalAlign places text in its box: "flex-start" (top), "center" or "flex-end" (bottom).

Shapes#

Outline. A rectangle or circle paints its fill, which defaults to black (#000000). For an outline, set fill to "transparent" and set stroke and strokeWidth. Lines and arrows are drawn with color and strokeWidth.

SVG layers#

Content. type: "element" with isSvg: true. content is an inline data:image/svg+xml URI or the https URL of an .svg file. Percent-encode an inline SVG (encodeURIComponent): a raw # or < cuts the URI short and the layer renders blank. style.svgColor tints a single-colour SVG.

Parts. To animate parts of a drawing, give each moving part an id and target it with motion.svg (see Motion). Keep static placement on an outer <g> and put the id on the inner one.

Waveforms#

Structure. type:"waveform", waveTrack:0 (zero-based page audioTracks index), with normal position/dimensions/zIndex. It needs no content.

Style. style:{ fill, barCount:4-160, barGap:0-24, borderRadius, waveAlign:'center'|'bottom', minBarHeight:0-60 }. The image preview draws a deterministic static waveform; video renders animate it from the selected track.

Dynamic. A parameterized waveform can expose its flat style keys through normal dot modifications such as 'wave1.fill' or 'wave1.barCount'.

Animation#

When. Transitions play in video (mp4, webm) and GIF renders. Static renders show every layer in its final state.

Example

JSON
{
  "transitions": {
    "enter": {
      "type": "slideInUp",
      "easing": "ease-out",
      "duration": 0.6
    },
    "showAt": 1.2,
    "ambient": {
      "type": "kenBurns",
      "customValues": {
        "zoom": 1.12,
        "panX": 8
      }
    },
    "exit": {
      "type": "fadeOut",
      "duration": 0.3
    },
    "hideAt": 3.6
  }
}

Visibility windows. showAt and hideAt give each layer a visibility window, in seconds. The exit animation starts at hideAt, so the layer is gone at hideAt + exit.duration. Leave hideAt out (or null) to keep the layer to the end. For a slideshow, stack full-canvas photos and give photo K showAt = cut K and hideAt = cut K+1.

Enter types

  • fadeIn
  • slideInUp
  • slideInDown
  • slideInLeft
  • slideInRight
  • scaleIn
  • scaleInUp
  • scaleInDown
  • rotateIn
  • flipInX (3D)
  • flipInY (3D)
  • textReveal (text only, staggered per word/char)
  • typewriter (text only)
  • none

Exit types

  • fadeOut
  • slideOutUp
  • slideOutDown
  • slideOutLeft
  • slideOutRight
  • scaleOut
  • scaleOutUp
  • scaleOutDown
  • rotateOut
  • flipOutX
  • flipOutY
  • textRevealOut
  • none

Easings

  • ease-out (the enter default)
  • ease-in (the exit default)
  • linear, ease, ease-in-out
  • cubic-bezier(0.175, 0.885, 0.32, 1.275) (a bounce)
  • spring(stiffness, damping, velocity), such as spring(180, 12, 0): stiffness 20-400, damping 2-40

Custom values. Per-phase intensity via enter/exit.customValues: opacity (start 0-1), offset (slide % ±500, sign = direction), scale (0-5; scaleIn default 0: use { scale: 1.15 } for a subtle zoom-punch to 1.0), angle (deg; rotateIn -180, flips ±90), granularity ('word'|'char': textReveal/typewriter), overlap (10-90 stagger % for textReveal). reverse: true flips a phase's direction.

Ambient. A third phase beside enter and exit: transitions.ambient = { type, duration, easing, customValues } plays for the layer's whole visible life, starting when its enter finishes. kenBurns runs once across the visible window and ignores duration (customValues: zoom 0.5-2, default 1.12; panX and panY ±30%). The loop types repeat every duration seconds (default 4): pulse (amount 1-1.5), float (offset px), drift (offset px), wiggle (angle deg), shake (offset px), slowRotate, and breathe (opacity and scale). shake and slowRotate loop without a visible seam only with easing "linear".

More motion. Transitions have no mid-life motion beyond the ambient types. For more, use element motion (see Motion), which also takes explicit keyframes.

Motion#

Overview. Per-element animation for video pages, beyond transitions. Any element.motion or page.motion switches the page to the motion timeline; transitions on other layers keep working in the same timeline. Static renders ignore motion. All keys are optional unless marked required; out-of-range values are clamped.

Element

NameValue
reveal{ by: "word" | "char" (default word), stagger: 0.07 (0.01-1 s between items), duration: 0.7, dy: 120 (rise in px), style: "rise" (default) | "fade" | "blur" | "scale" | "mask", ease }. Staggered text entrance. Text layers only, up to 400 words.
typewriter{ duration: 1.6, delay: 0 }. The text types itself. Plain-text layers only.
count{ to (required number), from: 0, duration: 1.4, decimals: 0 (0-4), prefix, suffix, ease (default power1.out; "none" counts at a steady pace), separator (true for "485,000", or "," "." " " "'"), point ("." or ",") }. The number counts up in place.
wake{ duration: 0.6, from: "up" | "down" | "left" | "right" | "none" (default up), distance: 34 (px), offset: 0 (s after showAt) }. A directional entrance.
after{ element (required layer id), offset: 0 (s, may be negative) }. Starts this layer when the named layer finishes its entrance. A missing id or a cycle of after references is rejected when you save.
pulse{ from: 0.97, to: 1.03, rate: 1.6 (cycles per s), axis: "y" | "both" (default y) }. A looping scale.
float{ amp: 6 (px, up to 200), rate: 2.4 }. A looping vertical drift.
press{ at (required s), dip: 6 (px, up to 60) }. One press dip, like a button being clicked.
sleep{ at (required s), duration: 0.45 }. The layer leaves early.
drawIn{ duration: 0.9, delay: 0, hidden: false }. SVG strokes draw themselves (SVG layers, up to 300 nodes drawn). hidden: true keeps a stroke-only shape invisible until its draw starts.
svg{ animate: true, targets: [...] } (up to 20 targets). Animates parts inside an SVG layer. Each target: selector (a CSS selector, up to 120 characters), drawIn, spin: { rate: 8 (degrees per s) }, pulse, origin (transform origin such as "50% 100%"), keyframes (up to 40: at, x and y in viewBox units, scale, scaleX, scaleY, rotate, opacity, ease), clock: "page" (default) | "layer" (from the layer's showAt) | "entered" (from the end of its entrance), repeat (1-100 extra plays, or -1 until the layer hides), repeatDelay (s). The SVG may be up to 512 KB and 1,500 nodes; it is sanitized, and animations inside the markup are removed. Part motion is clipped to the viewBox.
keyframes[{ at (required s), x, y (px offsets), scale: 1, rotate (deg), opacity: 1, ease, scaleX, scaleY, blur (px, 0-200), skewX, skewY (deg, ±89), clip: { top, right, bottom, left } (% inset), color (text), fill (shape), z (stacking order from this keyframe on) }] (up to 40). An explicit track. The first keyframe holds until its at; a channel a keyframe leaves out returns to rest, so repeat a value to hold it. ease takes names such as "expo.out", "back.out(1.7)" or "none", CSS keywords or cubic-bezier(); the default is power1.inOut.

Page

NameValue
look{ grain: 0-0.2, vignette: 0-0.6 }. Film grain and edge darkening over the whole page.
camera{ moves: [{ at (required s), duration (required s), x, y (pan in px), zoom: 1, ease: "ease-in-out" }] } (up to 20 moves). Moves the whole canvas.
groups{ "<groupId>": { keyframes | wake | float | pulse | press | sleep | after } }. Moves every layer whose groupId matches as one piece, about their joint centre. A group id must not equal a layer id. Members keep their own entrances and text effects.

Validation. Create and update reject structural problems with a 400 that lists each one: a missing or cyclic after reference, a non-numeric count.to, keyframes that are not an array, more than 200 animated layers or 2,000 tweens on a page, or a page over 120 s. Unknown keys never fail: they are ignored at render and listed in the response warnings with a suggested spelling. Renders never fail on motion; at worst the page falls back to its transitions.

Limits. reveal and typewriter work on text layers only, and typewriter needs plain text. Caps: 200 animated layers per page, 400 reveal words, 512 KB of SVG.

Page audio#

Overview. Video templates carry their own soundtrack: each page has audioTracks (an array) mixed into every mp4 and webm render. A track's source is an existing url, generated narration (tts) or a generated sound (sfx). audioTracks is a page field, next to elements and canvas, not a layer. A render is refused when a page has more tracks than the plan allows per page.

Track fields

NameValue
urlexisting public audio URL (e.g. brand audio direct_url)
tts{ text, voiceId?, modelId?: flash|expressive|multilingual, stability?, similarity?, style?, speakerBoost?, parameterId? }
sfx{ prompt (max 450 chars), seconds? (1-22, default 12) }
trimStartstart inside the SOURCE file, s
trimEndend inside source, s (null = to the end)
fadeIns
fadeOuts
volume0-1+ (narration usually 1; ambient usually 0.08-0.18)
offsetplacement on VIDEO timeline, s
loopusually true for ambient and false for narration
mutedfalse
enabledtrue
durationsource duration s: set when known for URL audio

Example

JSON
{
  "audioTracks": [
    {
      "label": "Narration",
      "tts": {
        "text": "Meet the campaign built for you.",
        "voiceId": "<catalog voice id>",
        "modelId": "flash",
        "parameterId": "voiceover"
      },
      "volume": 1
    },
    {
      "label": "Ambient",
      "sfx": {
        "prompt": "warm modern technology pulse, instrumental, no vocals, seamless loop",
        "seconds": 12
      },
      "volume": 0.12,
      "loop": true,
      "fadeIn": 0.5,
      "fadeOut": 1
    }
  ]
}

Dynamic narration. tts.parameterId makes the spoken text a render parameter. Single page: modifications: { "voiceover": "New script" }; multi-page: { "page1@voiceover": "..." }. The render synthesizes, caches and bills the new script.

Render swap. videoOptions.audioSource replaces an existing URL track at render time while keeping saved trim/fade/volume/offset/loop. It is for soundtrack-file swaps; generated narration uses tts.parameterId instead. There is no generic audio parameterType.

Mixing. Page tracks auto-mix with unmuted video-element audio. videoOptions.muted: true strips all audio from a render.

Subtitles#

Saved. page.subtitle (a page field): { source: "auto-audio" | "upload", url, enabled, mode: "phrase" | "word-by-word", maxWordsPerLine or maxCharsPerLine, wordReveal: "appear" | "highlight" | "align", split: "word" | "letter", fontSize, fontFamily, fontWeight, color, background, position: "top" | "center" | "bottom", offset, paddingX, paddingY, borderRadius, shadowX, shadowY, shadowBlur, shadowColor, wordSpacing }. maxCharsPerLine overrides the word budget; align re-centres the line as spoken words appear. source "auto-audio" captions the page's first enabled audio track that is not a sound effect.

Render time. videoOptions.subtitleSource on a render (a media URL is transcribed automatically; an .srt or .vtt file is used as it is; the array form targets pages and takes trimStart and offset) and the subtitle* keys override the saved config: subtitleWordReveal appear | highlight | align, subtitleSplit word | letter, subtitleMaxWordsPerLine or subtitleMaxCharsPerLine. For captions that follow the audio, save the audio in audioTracks and set the subtitle source to "auto-audio". Do not use the output trimStart and trimEnd to align captions: the caption and video clocks drift apart.

Video layers#

Options. element.videoOptions: { loop, muted (default true; set false to keep the clip's sound), trim_start_time, trim_end_time (snake_case; seconds or "MM:SS"), duration (the clip's own length in s) }. The saved trim applies only when both trim_start_time and trim_end_time are set, and only to the template's own clip; when a render swaps the clip URL, trim it with the render-time keys below.

Render time. Per render: 'clipParam.trimStart': 4.5, 'clipParam.trimEnd': 12, 'clipParam.muted': false, 'clipParam.loop': true (camelCase at the modification layer).

Video render options#

Options. videoOptions on a render request: duration, fps (up to 30), trimStart and trimEnd (together), quality, muted, audioSource, subtitleSource with the subtitle* styling keys, subtitleMode, subtitleWordReveal: appear | highlight | align, subtitleSplit: word | letter, subtitleMaxWordsPerLine or subtitleMaxCharsPerLine, combinePages, pageTransition with its duration, and captionPreset: pill | minimal | karaoke | bold | outline | neon.

Multi page. Multi-scene videos are films: one video whose pages are scenes joined by each page's transitionIn, with music and layers that stay across cuts on film tracks. See Film. combinePages: true still joins the pages of a plain multi-page template, and a string audioSource lays one soundtrack across it.

Canvas sizes#

Note. Suggested canvas sizes in pixels. A canvas side can be up to 5,000 px.

Common

NameValue
ig-post1080x1080
ig-portrait/carousel1080x1350
ig-story/tiktok/short1080x1920
linkedin-post1200x1200
pinterest1000x1500
youtube-thumb1280x720
og/blog1200x630
twitter-header1500x500
presentation1920x1080
doc-a4-portrait1000x1414
certificate1414x1000

Common mistakes#

  • Putting showAt or hideAt inside enter or exit. They are siblings of enter and exit on transitions; inside them the renderer ignores the timing and every layer shows at once. The property is transitions, not animation.
  • Mixing unit forms: lengths are px strings ("48px") and lineHeight is a number (1.2).
  • Reusing a parameterId on layers of different types on one page (the later one is renamed with _1).
  • Several <p> blocks in one text layer (they overlap), or \n inside rich text (it is not converted; use <br>).
  • An inline SVG data URI with a raw # or < (it is cut short and renders blank; percent-encode it).
  • A shape with backgroundColor and no fill: shapes paint fill, which defaults to black.
  • A generic audio parameterType. Swap a soundtrack URL with videoOptions.audioSource; change spoken copy through the track's tts.parameterId.
  • videoOptions.trimStart without trimEnd on a render (it needs both).
  • Expecting a video layer's own audio to play: its videoOptions.muted defaults to true; set it to false.
Was this page helpful?

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 40+ integrations
  • White-label embed for your own app
  • 100 free credits a month, no credit card required