Movie, Scene & Layer Schema Reference
Every render job submitted to ViralSync is built around a Movie — a structured JSON document that describes the full video. A Movie contains one or more Scenes, and each Scene contains one or more Layers.
Movie
└── Scene[]
└── Layer[] (video | image | audio | text)
This page is the authoritative reference for every property in that hierarchy.
Submitting render jobs via the API requires a Pro plan or higher. Free-plan users can use the visual editor in the dashboard. See Plans & Limits.
Movie
The root object passed as movie in a render job request.
| Property | Type | Required | Default | Constraints | Description |
|---|---|---|---|---|---|
id | string | No | auto-generated | — | Stable identifier. Used for scene-level caching: if an identical scene was previously rendered, the cached output is reused. |
title | string | No | "Untitled Movie" | — | Display label. Not embedded in the video output. |
width | number | No | 1920 | 360–3840 | Canvas width in pixels. Must match your target aspect ratio. |
height | number | No | 1080 | 360–2160 | Canvas height in pixels. |
fps | number | No | 30 | 1–60 | Frames per second. Common values: 24 (cinematic), 30 (standard), 60 (smooth motion). |
scenes | Scene[] | Yes | — | At least 1 scene | Ordered list of scenes. Scenes are rendered in parallel; the final video preserves their index order. |
Common Resolutions
| Format | Width | Height | FPS |
|---|---|---|---|
| YouTube 4K | 3840 | 2160 | 30 |
| YouTube 1080p | 1920 | 1080 | 30 |
| YouTube 720p | 1280 | 720 | 30 |
| Instagram Reels / TikTok | 1080 | 1920 | 30 |
| Instagram Square | 1080 | 1080 | 30 |
| Twitter / X | 1280 | 720 | 30 |
Example
{
"id": "movie_summer_promo",
"title": "Summer Promo Video",
"width": 1920,
"height": 1080,
"fps": 30,
"scenes": []
}
Scene
A Scene is a self-contained segment of the video with its own duration, background colour, and layers. Scenes are executed in parallel across render workers; the output clips are concatenated in order during the assemble phase.
| Property | Type | Required | Default | Constraints | Description |
|---|---|---|---|---|---|
id | string | Yes | — | Unique within the movie | Stable identifier for this scene. Used for caching and progress reporting. |
name | string | No | — | — | Human-readable label. Useful for debugging; not embedded in output. |
duration | number | Yes | — | > 0 | Scene duration in seconds. All layers must have startTime + duration ≤ scene.duration. |
bgColor | string | No | "#000000" | Any CSS colour | Background colour rendered behind all layers. Supports hex (#1a1a2e), RGB (rgb(26,26,46)). |
width | number | No | inherits movie.width | 360–3840 | Override the canvas width for this scene only. |
height | number | No | inherits movie.height | 360–2160 | Override the canvas height for this scene only. |
layers | Layer[] | No | [] | — | Ordered list of layers. Rendered in zIndex order (lowest = bottom). |
description | string | No | — | — | Optional description. Ignored by the renderer; useful for AI-generated scripts. |
index | number | No | array position | — | Explicit ordering hint. Defaults to the scene's position in scenes[]. |
Example
{
"id": "scene_intro",
"name": "Introduction",
"duration": 5,
"bgColor": "#1a1a2e",
"layers": []
}
Layer
Layers are the building blocks of a scene. Every layer has a type that determines which properties are valid. There are four types:
| Type | Description | Requires source |
|---|---|---|
video | A video clip rendered at the specified position and size | Yes |
image | A static image or GIF | Yes |
audio | An audio track (no visual output) | Yes |
text | Rendered text using system fonts | No |
Base Properties (all layer types)
| Property | Type | Required | Default | Constraints | Description |
|---|---|---|---|---|---|
id | string | Yes | — | Unique within the scene | Stable identifier for this layer. |
name | string | No | — | — | Display label. Not embedded in output. |
type | "video" | "image" | "audio" | "text" | Yes | — | — | Layer type. Determines which additional properties are valid. |
role | "background" | "overlay" | No | "overlay" | — | Semantic role hint. "background" layers are placed behind overlay layers in the editor UI, but zIndex controls actual render ordering. |
startTime | number | Yes | — | ≥ 0 | Offset in seconds from the start of the scene at which this layer becomes visible/audible. |
duration | number | Yes | — | > 0 | How long this layer is visible/audible, in seconds. |
maxDuration | number | No | — | > 0 | For video and audio layers: the total available duration of the source clip. Used by the editor to enforce trim limits. Not validated by the renderer. |
zIndex | number | Yes | — | Any integer | Stacking order. Higher values render on top. Layers with the same zIndex are stacked in declaration order. |
position | { x: number, y: number } | Yes | — | — | Top-left corner of the layer in pixels, relative to the scene canvas. { x: 0, y: 0 } = top-left corner. |
size | { width: number, height: number } | Yes | — | > 0 | Dimensions of the layer in pixels. The layer is scaled to fit this box. |
transform | TransformObject | No | — | — | Additional geometric transformation applied after position and size. See Transform. |
source | string (URL) | For video / image / audio | — | Must be publicly accessible HTTP/HTTPS URL | The media file to render. Must be a stable, publicly accessible URL — no auth tokens, no short-lived presigned URLs. The render worker downloads this URL at render time. |
volume | number | No | 1.0 | 0.0–2.0 | Audio gain. 0 = silent, 1 = original volume, 2 = double gain. Applies to video and audio layers. |
mute | boolean | No | false | — | Mute the audio track. For video layers: video renders but audio is silent. Has no effect on image or text layers. |
The source field must be a publicly accessible URL that the render worker can download without authentication. Do not use:
- Auth-gated URLs (requiring
Authorizationheaders) - Short-lived presigned URLs (AWS S3, GCS) that may expire before the render starts
localhostor private network URLs
Use CDN URLs (Cloudflare R2, Cloudflare Images, Bunny CDN, public S3 buckets) for reliable rendering.
Transform
The optional transform object applies geometric modifications on top of position and size.
| Property | Type | Default | Description |
|---|---|---|---|
x | number | 0 | Additional horizontal offset in pixels, applied after position.x. |
y | number | 0 | Additional vertical offset in pixels, applied after position.y. |
scaleX | number | 1.0 | Horizontal scale multiplier. 2.0 = double width. Applied from the layer centre. |
scaleY | number | 1.0 | Vertical scale multiplier. 0.5 = half height. Applied from the layer centre. |
scale | number | 1.0 | Uniform scale applied after scaleX/scaleY. 1.5 = 50% larger. |
rotation | number | 0 | Rotation in degrees, clockwise. 180 = upside down. Applied from the layer centre. |
opacity | number | 1.0 | Opacity multiplier. 0 = fully transparent, 1 = fully opaque. |
Example:
"transform": {
"rotation": 15,
"opacity": 0.85,
"scale": 1.2
}
Text Layer Additional Properties
When type is "text", the following additional properties control how the text is rendered.
| Property | Type | Default | Description |
|---|---|---|---|
text | string | "" | The text content to render. Supports \n for line breaks. |
fontSize | number | 48 | Font size in pixels. |
fontFamily | string | "Inter, Arial, sans-serif" | CSS font-family stack. The renderer uses system fonts available on the render worker. |
color | string | "#ffffff" | Text colour. Any CSS colour string. |
textAlign | "left" | "center" | "right" | "center" | Horizontal alignment of text within the size bounding box. |
fontWeight | string | number | "normal" | CSS font-weight. Use "bold" or a numeric weight like 700. |
fontStyle | string | "normal" | "normal" or "italic". |
textDecoration | string | "none" | "underline" or "line-through". |
lineHeight | number | 1.1 | Line height multiplier relative to fontSize. |
letterSpacing | number | 0 | Additional space between characters in pixels. |
stroke | string | — | Outline colour for the text. Any CSS colour. Requires strokeWidth to be visible. |
strokeWidth | number | — | Width of the text outline in pixels. |
Layer Type Compatibility Matrix
| Property | video | image | audio | text |
|---|---|---|---|---|
source (URL) | ✅ Required | ✅ Required | ✅ Required | ❌ Not used |
position | ✅ | ✅ | ❌ Ignored | ✅ |
size | ✅ | ✅ | ❌ Ignored | ✅ |
zIndex | ✅ | ✅ | ❌ Ignored | ✅ |
transform | ✅ | ✅ | ❌ Ignored | ✅ |
volume / mute | ✅ | ❌ | ✅ | ❌ |
text, fontSize, color, … | ❌ | ❌ | ❌ | ✅ Required |
Complete Example
A 2-scene movie with all four layer types:
{
"id": "movie_demo",
"title": "API Demo Video",
"width": 1920,
"height": 1080,
"fps": 30,
"scenes": [
{
"id": "scene_opening",
"name": "Opening — Background Video with Title",
"duration": 6,
"bgColor": "#000000",
"layers": [
{
"id": "bg_video",
"type": "video",
"role": "background",
"source": "https://cdn.example.com/footage/city-timelapse.mp4",
"startTime": 0,
"duration": 6,
"zIndex": 0,
"position": { "x": 0, "y": 0 },
"size": { "width": 1920, "height": 1080 },
"transform": { "opacity": 0.6 },
"mute": true
},
{
"id": "bg_music",
"type": "audio",
"source": "https://cdn.example.com/audio/background-loop.mp3",
"startTime": 0,
"duration": 6,
"zIndex": 0,
"position": { "x": 0, "y": 0 },
"size": { "width": 0, "height": 0 },
"volume": 0.4
},
{
"id": "logo",
"type": "image",
"source": "https://cdn.example.com/brand/logo-white.png",
"startTime": 0.5,
"duration": 5,
"zIndex": 2,
"position": { "x": 80, "y": 60 },
"size": { "width": 200, "height": 60 }
},
{
"id": "headline",
"type": "text",
"text": "Welcome to ViralSync",
"startTime": 1,
"duration": 4,
"zIndex": 3,
"position": { "x": 360, "y": 420 },
"size": { "width": 1200, "height": 120 },
"fontSize": 80,
"fontFamily": "Inter, Arial, sans-serif",
"fontWeight": "bold",
"color": "#ffffff",
"textAlign": "center",
"transform": { "opacity": 0.95 }
}
]
},
{
"id": "scene_cta",
"name": "Call to Action",
"duration": 4,
"bgColor": "#0f172a",
"layers": [
{
"id": "cta_text",
"type": "text",
"text": "Start building at viralsync.io",
"startTime": 0,
"duration": 4,
"zIndex": 1,
"position": { "x": 360, "y": 460 },
"size": { "width": 1200, "height": 80 },
"fontSize": 52,
"color": "#00d4ff",
"textAlign": "center",
"fontWeight": "600"
}
]
}
]
}
Next Steps
- Render API Reference — how to submit this Movie JSON as a render job
- Render Options Reference — quality presets and concurrency settings
- Error Reference — what happens when a
sourceURL is unreachable or the JSON is invalid