Animations and Videos
Enable animations, preview on canvas, export as GIF, and render MP4 via Cloud API
How to enable animation support?
import { setAnimationsEnabled } from 'polotno/config';
setAnimationsEnabled(true);When you enable animations, Polotno will add additional UI in the toolbar to change animation properties of the selected object, show a “Videos” side panel with a library of stock videos, and adapt the Pages component with scene preview.
Define an animation
Each element has an animations array with at most one enter, one exit and one loop. All times are in milliseconds, measured on the element's page. A page plays for its duration, 5000 by default.
{
"schemaVersion": 5,
"width": 1080,
"height": 1080,
"pages": [
{
"id": "page-1",
"background": "#0B2545",
"duration": 4000,
"children": [
{
"id": "headline",
"type": "text",
"x": 100,
"y": 420,
"width": 880,
"height": 120,
"text": "Summer sale",
"fontSize": 96,
"fontFamily": "Inter",
"fill": "#ffffff",
"align": "center",
"animations": [
{
"type": "enter",
"name": "move",
"duration": 600,
"easing": "easeOut",
"data": { "direction": "up" }
},
{
"type": "exit",
"name": "fade",
"delay": 200,
"duration": 400,
"easing": "easeIn"
}
]
},
{
"id": "badge",
"type": "text",
"x": 340,
"y": 580,
"width": 400,
"height": 80,
"text": "40% off",
"fontSize": 64,
"fontFamily": "Inter",
"fill": "#13A89E",
"align": "center",
"animations": [
{
"type": "enter",
"name": "pop",
"delay": 500,
"duration": 500,
"easing": "backOut"
},
{ "type": "loop", "name": "pulse", "delay": 1000, "duration": 1500 }
]
}
]
}
]
}enterstartsdelayafter the page starts. The element is hidden until then.exitfinishesdelaybefore the page ends. The element stays hidden after it.loopstartsdelayafter the page starts, anddurationis the length of one cycle.- A missing
easingplays linear, so set it. The editor useseaseOuton an enter andeaseInon an exit.
Every preset, its options and the elements it works on are in the Animation schema.
Group animations
A group can own its own enter, exit, and loop animations directly: selecting a group shows the same animation controls as a single element. Group animations are composed on top of each child's own animation, so you can animate a whole group (for example, slide it in) without setting up each child individually, while children keep any animations of their own.
You can preview animations and export an animated scene as a GIF on the client side programmatically:
store.play();
store.stop();
await store.saveAsGIF();A lottie element ({ type: 'lottie', src }, where src is the URL or data URL of a JSON Lottie or a .lottie file) plays in that preview and in GIF and video export. It loops from the moment the element enters, as a video does.
Video Export Options
Polotno supports two methods for exporting animated designs to MP4 video:
Client-Side Video Export
For browser-based video generation without a server, use the @polotno/video-export package. All encoding happens directly in the user's browser:
npm install @polotno/video-exportimport { storeToVideo } from '@polotno/video-export';
const videoBlob = await storeToVideo({
store,
fps: 30,
pixelRatio: 2,
onProgress: (progress) => {
console.log(`Progress: ${Math.round(progress * 100)}%`);
},
});
// Download the video
const url = URL.createObjectURL(videoBlob);
const link = document.createElement('a');
link.href = url;
link.download = 'video.mp4';
link.click();Export speed depends on user hardware. High FPS or complex designs may take longer to process.
See the full Video Export documentation for complete API reference and live demo.
Server-Side Video Export
For production-grade video rendering with consistent performance, use the Cloud Render API. This option provides faster rendering and doesn't depend on client device capabilities.
Audio Support
Add background music or sound effects to your animated designs. Audio tracks sync with the video timeline and export together with animations.
See the Audio documentation for complete details on adding and managing audio tracks.
Generating video JSON with an LLM
Pass these rules to the model together with the design schema.
Trim
startTime and endTime are fractions of the source clip (0 to 1). duration is the clip length in milliseconds. Seconds 3 to 5 of a 12-second clip:
{
"type": "video",
"src": "https://example.com/clip.mp4",
"duration": 12000,
"startTime": 0.25,
"endTime": 0.4167
}startTime = 3000 / duration, endTime = 5000 / duration. Measure duration for a new clip, or read it from the existing JSON for an edit.
Crop
cropX, cropY, cropWidth and cropHeight are fractions of the source size. With the default values, a clip with a different aspect ratio fills the element from its top-left corner. Centred crop:
const frameRatio = element.width / element.height;
const sourceRatio = sourceWidth / sourceHeight;
const cropWidth = sourceRatio > frameRatio ? frameRatio / sourceRatio : 1;
const cropHeight = sourceRatio > frameRatio ? 1 : sourceRatio / frameRatio;
const crop = {
cropX: (1 - cropWidth) / 2,
cropY: (1 - cropHeight) / 2,
cropWidth,
cropHeight,
};16:9 clip in a 9:16 frame: cropWidth = (9/16) / (16/9) ≈ 0.3164, cropX ≈ 0.3418, cropY = 0, cropHeight = 1.
Animation names
Use the names and options in the Animation schema. An unknown name logs a console error and the element does not animate.
Validation
store.loadJSON accepts out-of-range values without an error. validateDesign from @polotno/schema checks structure and types, but not ranges or animation names. Check these before loading:
startTime,endTime,cropX,cropY,cropWidth,cropHeightare between 0 and 1startTime < endTimecropX + cropWidth <= 1andcropY + cropHeight <= 1- every animation
nameis valid for itstype
Include every top-level key from store.toJSON(). Use audios: [] for a design without audio.
Live demo
Related Pages
- Video Export - Detailed client-side video export documentation
- Audio - Add background music to animated designs
- Cloud Render API - Server-side video rendering
- Import and Export - Overview of all export formats