All docs
JSON format
JSON examples
Complete openCanviz JSON files you can copy, import and edit: a flow diagram, a two-scene explainer and a vertical Short.
Last updated
Each example below is a complete file that imports as is. Copy one, paste it into Import on the Create page, and click Import project. Each one passes the schema and plays from start to end. Change the text, colors and positions to make it your own, and keep the structure. For what each field means, see the schema reference.
The examples include narration lines as text. After you import, open the Narration panel and click Generate All Scenes to give them a voice.
A four-step flow diagram
A 16:9 scene that walks a web request from the browser to a database. It shows:
- A title and three boxes, with the server and database starting at
opacity0. - Edges that attach to the side of a box with
fromPortandtoPort, and draw in with anedgeTweenontrim. - Delta steps that set
opacityto 1 for each box they reveal, alongside afadeInaction. - A last step that changes fill colors with no action at all, so the change tweens on its own, and turns on a moving ball along each edge with
animationStyle.
{
"$schema": "https://opencanviz.com/schema/v1.json",
"title": "How a web request travels",
"artboard": {
"x": -960,
"y": -540,
"width": 1920,
"height": 1080,
"background": "#0f172a"
},
"showCaptions": true,
"diagrams": [
{
"nodes": [
{
"id": "title",
"kind": "text",
"x": -700,
"y": -420,
"w": 1400,
"h": 100,
"text": "How a web request travels",
"fontSize": 64,
"fontWeight": 700,
"color": "#f8fafc",
"textAlign": "center",
"verticalAlign": "middle",
"fill": "transparent",
"stroke": "none",
"opacity": 1
},
{
"id": "client",
"kind": "rect",
"x": -820,
"y": -100,
"w": 360,
"h": 200,
"cornerRadius": 20,
"fill": "#1e293b",
"stroke": "#38bdf8",
"strokeWidth": 4,
"label": "Browser",
"labelColor": "#f8fafc",
"labelFontSize": 44,
"opacity": 1
},
{
"id": "server",
"kind": "rect",
"x": -180,
"y": -100,
"w": 360,
"h": 200,
"cornerRadius": 20,
"fill": "#1e293b",
"stroke": "#a78bfa",
"strokeWidth": 4,
"label": "API server",
"labelColor": "#f8fafc",
"labelFontSize": 44,
"opacity": 0
},
{
"id": "db",
"kind": "rect",
"x": 460,
"y": -100,
"w": 360,
"h": 200,
"cornerRadius": 20,
"fill": "#1e293b",
"stroke": "#34d399",
"strokeWidth": 4,
"label": "Database",
"labelColor": "#f8fafc",
"labelFontSize": 44,
"opacity": 0
}
],
"edges": [
{
"id": "client_to_server",
"from": "client",
"to": "server",
"fromPort": "E",
"toPort": "W",
"stroke": "#cbd5e1",
"width": 5,
"arrow": "end",
"lineType": "straight",
"trim": 0
},
{
"id": "server_to_db",
"from": "server",
"to": "db",
"fromPort": "E",
"toPort": "W",
"stroke": "#cbd5e1",
"width": 5,
"arrow": "end",
"lineType": "straight",
"trim": 0
}
],
"steps": [
{
"id": "browser",
"durationMs": 2500,
"caption": "A request starts in the browser.",
"nodes": [
{
"id": "title",
"kind": "text",
"x": -700,
"y": -420,
"w": 1400,
"h": 100,
"text": "How a web request travels",
"fontSize": 64,
"fontWeight": 700,
"color": "#f8fafc",
"textAlign": "center",
"verticalAlign": "middle",
"fill": "transparent",
"stroke": "none",
"opacity": 1
},
{
"id": "client",
"kind": "rect",
"x": -820,
"y": -100,
"w": 360,
"h": 200,
"cornerRadius": 20,
"fill": "#1e293b",
"stroke": "#38bdf8",
"strokeWidth": 4,
"label": "Browser",
"labelColor": "#f8fafc",
"labelFontSize": 44,
"opacity": 1
},
{
"id": "server",
"kind": "rect",
"x": -180,
"y": -100,
"w": 360,
"h": 200,
"cornerRadius": 20,
"fill": "#1e293b",
"stroke": "#a78bfa",
"strokeWidth": 4,
"label": "API server",
"labelColor": "#f8fafc",
"labelFontSize": 44,
"opacity": 0
},
{
"id": "db",
"kind": "rect",
"x": 460,
"y": -100,
"w": 360,
"h": 200,
"cornerRadius": 20,
"fill": "#1e293b",
"stroke": "#34d399",
"strokeWidth": 4,
"label": "Database",
"labelColor": "#f8fafc",
"labelFontSize": 44,
"opacity": 0
}
],
"edges": [
{
"id": "client_to_server",
"from": "client",
"to": "server",
"fromPort": "E",
"toPort": "W",
"stroke": "#cbd5e1",
"width": 5,
"arrow": "end",
"lineType": "straight",
"trim": 0
},
{
"id": "server_to_db",
"from": "server",
"to": "db",
"fromPort": "E",
"toPort": "W",
"stroke": "#cbd5e1",
"width": 5,
"arrow": "end",
"lineType": "straight",
"trim": 0
}
],
"actions": [
{
"type": "visibility",
"target": {
"kind": "node",
"id": "title"
},
"mode": "fadeIn",
"durationMs": 500
},
{
"type": "visibility",
"target": {
"kind": "node",
"id": "client"
},
"mode": "fadeIn",
"durationMs": 500,
"offsetMs": 400
}
]
},
{
"id": "to_server",
"delta": true,
"durationMs": 2500,
"caption": "It travels to the API server.",
"nodes": [
{
"id": "server",
"opacity": 1
}
],
"edges": [
{
"id": "client_to_server",
"trim": 1
}
],
"actions": [
{
"type": "edgeTween",
"target": {
"kind": "edge",
"id": "client_to_server"
},
"from": {
"trim": 0
},
"to": {
"trim": 1
},
"durationMs": 600,
"ease": "ease-out"
},
{
"type": "visibility",
"target": {
"kind": "node",
"id": "server"
},
"mode": "fadeIn",
"durationMs": 400,
"offsetMs": 600
}
]
},
{
"id": "to_db",
"delta": true,
"durationMs": 2500,
"caption": "The server asks the database for the data.",
"nodes": [
{
"id": "db",
"opacity": 1
}
],
"edges": [
{
"id": "server_to_db",
"trim": 1
}
],
"actions": [
{
"type": "edgeTween",
"target": {
"kind": "edge",
"id": "server_to_db"
},
"from": {
"trim": 0
},
"to": {
"trim": 1
},
"durationMs": 600,
"ease": "ease-out"
},
{
"type": "visibility",
"target": {
"kind": "node",
"id": "db"
},
"mode": "fadeIn",
"durationMs": 400,
"offsetMs": 600
}
]
},
{
"id": "answer",
"delta": true,
"durationMs": 3000,
"caption": "The database answers, and the page loads.",
"easing": "ease-out",
"nodes": [
{
"id": "db",
"fill": "#065f46"
},
{
"id": "client",
"fill": "#0c4a6e"
}
],
"edges": [
{
"id": "client_to_server",
"animationStyle": "ball"
},
{
"id": "server_to_db",
"animationStyle": "ball"
}
]
}
],
"narration": [
{
"id": "line_0",
"text": "A request starts in the browser.",
"stepIndex": 0,
"voice": {
"provider": "openai",
"voiceId": "alloy",
"voiceName": "Alloy"
}
},
{
"id": "line_1",
"text": "It travels to the API server.",
"stepIndex": 1,
"voice": {
"provider": "openai",
"voiceId": "alloy",
"voiceName": "Alloy"
}
},
{
"id": "line_2",
"text": "The server asks the database for the data.",
"stepIndex": 2,
"voice": {
"provider": "openai",
"voiceId": "alloy",
"voiceName": "Alloy"
}
},
{
"id": "line_3",
"text": "The database answers, and the page loads.",
"stepIndex": 3,
"voice": {
"provider": "openai",
"voiceId": "alloy",
"voiceName": "Alloy"
}
}
]
}
]
}A two-scene explainer
Two scenes joined by a crossfade, with captions in a band. It shows:
sceneNames,transitionsandcaptionStyleat the project level.- Scene 1: a typewriter title, made with a
transformTweenontypeProgressfrom 0 to 1, and a subtitle that enters from below withslideInDownafter a delay set byoffsetMs. - Scene 2: an app box and a database
cylinderthat slide in from opposite sides, joined by a dashed edge. - A delta step that moves the database to a new
x(set in the step and timed with atransformTween), adds a cache box withaddNodesand a new edge withaddEdges. - A last step that removes the slow edge with
removeEdgeIdsand zooms the camera in. - Ids prefixed per scene,
s1_ands2_, so they never clash.
{
"$schema": "https://opencanviz.com/schema/v1.json",
"title": "Caching in two minutes",
"artboard": {
"x": -960,
"y": -540,
"width": 1920,
"height": 1080,
"background": "#0f172a"
},
"sceneNames": [
"Title",
"Why caches help"
],
"transitions": [
{
"kind": "crossfade",
"durationMs": 600
}
],
"captionStyle": {
"variant": "band",
"fontScale": 1.1,
"textColor": "#f8fafc",
"bandColor": "#020617",
"bandOpacity": 0.8
},
"diagrams": [
{
"nodes": [
{
"id": "s1_title",
"kind": "text",
"x": -800,
"y": -160,
"w": 1600,
"h": 160,
"text": "Caching in two minutes",
"fontSize": 96,
"fontWeight": 800,
"color": "#f8fafc",
"textAlign": "center",
"verticalAlign": "middle",
"fill": "transparent",
"stroke": "none",
"typeProgress": 1
},
{
"id": "s1_subtitle",
"kind": "text",
"x": -600,
"y": 40,
"w": 1200,
"h": 80,
"text": "Why the second request is faster",
"fontSize": 44,
"fontWeight": 400,
"color": "#94a3b8",
"textAlign": "center",
"verticalAlign": "middle",
"fill": "transparent",
"stroke": "none"
}
],
"edges": [],
"steps": [
{
"id": "s1_type",
"durationMs": 2600,
"caption": "Caching, in two minutes.",
"nodes": [
{
"id": "s1_title",
"kind": "text",
"x": -800,
"y": -160,
"w": 1600,
"h": 160,
"text": "Caching in two minutes",
"fontSize": 96,
"fontWeight": 800,
"color": "#f8fafc",
"textAlign": "center",
"verticalAlign": "middle",
"fill": "transparent",
"stroke": "none",
"typeProgress": 1
},
{
"id": "s1_subtitle",
"kind": "text",
"x": -600,
"y": 40,
"w": 1200,
"h": 80,
"text": "Why the second request is faster",
"fontSize": 44,
"fontWeight": 400,
"color": "#94a3b8",
"textAlign": "center",
"verticalAlign": "middle",
"fill": "transparent",
"stroke": "none"
}
],
"edges": [],
"actions": [
{
"type": "transformTween",
"target": {
"kind": "node",
"id": "s1_title"
},
"from": {
"typeProgress": 0
},
"to": {
"typeProgress": 1
},
"durationMs": 1400,
"ease": "linear"
},
{
"type": "visibility",
"target": {
"kind": "node",
"id": "s1_subtitle"
},
"mode": "slideInDown",
"durationMs": 500,
"offsetMs": 1500,
"slideDistance": 60
}
]
}
],
"narration": [
{
"id": "s1_line_0",
"text": "Caching, in two minutes.",
"stepIndex": 0,
"voice": {
"provider": "openai",
"voiceId": "alloy",
"voiceName": "Alloy"
}
}
]
},
{
"nodes": [
{
"id": "s2_app",
"kind": "rect",
"x": -760,
"y": -110,
"w": 340,
"h": 220,
"cornerRadius": 20,
"fill": "#1e293b",
"stroke": "#38bdf8",
"strokeWidth": 4,
"label": "App",
"labelColor": "#f8fafc",
"labelFontSize": 44
},
{
"id": "s2_db",
"kind": "cylinder",
"x": 420,
"y": -140,
"w": 300,
"h": 280,
"fill": "#1e293b",
"stroke": "#f59e0b",
"strokeWidth": 4,
"label": "Database",
"labelColor": "#f8fafc",
"labelFontSize": 40
}
],
"edges": [
{
"id": "s2_slow",
"from": "s2_app",
"to": "s2_db",
"stroke": "#f59e0b",
"width": 5,
"arrow": "both",
"strokeStyle": "dashed",
"label": "200 ms",
"labelColor": "#fbbf24",
"labelFontSize": 32,
"labelPosition": "above"
}
],
"steps": [
{
"id": "s2_slow_path",
"durationMs": 3000,
"caption": "Without a cache, every request goes all the way to the database.",
"nodes": [
{
"id": "s2_app",
"kind": "rect",
"x": -760,
"y": -110,
"w": 340,
"h": 220,
"cornerRadius": 20,
"fill": "#1e293b",
"stroke": "#38bdf8",
"strokeWidth": 4,
"label": "App",
"labelColor": "#f8fafc",
"labelFontSize": 44
},
{
"id": "s2_db",
"kind": "cylinder",
"x": 420,
"y": -140,
"w": 300,
"h": 280,
"fill": "#1e293b",
"stroke": "#f59e0b",
"strokeWidth": 4,
"label": "Database",
"labelColor": "#f8fafc",
"labelFontSize": 40
}
],
"edges": [
{
"id": "s2_slow",
"from": "s2_app",
"to": "s2_db",
"stroke": "#f59e0b",
"width": 5,
"arrow": "both",
"strokeStyle": "dashed",
"label": "200 ms",
"labelColor": "#fbbf24",
"labelFontSize": 32,
"labelPosition": "above"
}
],
"actions": [
{
"type": "visibility",
"target": {
"kind": "node",
"id": "s2_app"
},
"mode": "slideInLeft",
"durationMs": 500
},
{
"type": "visibility",
"target": {
"kind": "node",
"id": "s2_db"
},
"mode": "slideInRight",
"durationMs": 500,
"offsetMs": 200
},
{
"type": "edgeTween",
"target": {
"kind": "edge",
"id": "s2_slow"
},
"from": {
"trim": 0
},
"to": {
"trim": 1
},
"durationMs": 700,
"offsetMs": 800
}
]
},
{
"id": "s2_add_cache",
"delta": true,
"durationMs": 3200,
"caption": "A cache keeps recent answers close to the app.",
"nodes": [
{
"id": "s2_db",
"x": 520
}
],
"edges": [],
"addNodes": [
{
"id": "s2_cache",
"kind": "rect",
"x": -170,
"y": -100,
"w": 300,
"h": 200,
"cornerRadius": 20,
"fill": "#3b0764",
"stroke": "#c084fc",
"strokeWidth": 4,
"label": "Cache",
"labelColor": "#f8fafc",
"labelFontSize": 44,
"opacity": 1
}
],
"addEdges": [
{
"id": "s2_fast",
"from": "s2_app",
"to": "s2_cache",
"stroke": "#c084fc",
"width": 5,
"arrow": "both",
"label": "2 ms",
"labelColor": "#e9d5ff",
"labelFontSize": 32,
"labelPosition": "above",
"trim": 0
}
],
"actions": [
{
"type": "transformTween",
"target": {
"kind": "node",
"id": "s2_db"
},
"to": {
"x": 520
},
"durationMs": 600,
"ease": "ease-out"
},
{
"type": "visibility",
"target": {
"kind": "node",
"id": "s2_cache"
},
"mode": "fadeIn",
"durationMs": 500,
"offsetMs": 600
},
{
"type": "edgeTween",
"target": {
"kind": "edge",
"id": "s2_fast"
},
"from": {
"trim": 0
},
"to": {
"trim": 1
},
"durationMs": 600,
"offsetMs": 1100
}
]
},
{
"id": "s2_zoom",
"delta": true,
"durationMs": 3000,
"caption": "The next request is answered in two milliseconds.",
"nodes": [],
"edges": [
{
"id": "s2_fast",
"trim": 1
}
],
"removeEdgeIds": [
"s2_slow"
],
"camera": {
"x": -300,
"y": 0,
"zoom": 1.4
},
"cameraDurationMs": 1200,
"cameraEasing": "ease-out"
}
],
"narration": [
{
"id": "s2_line_0",
"text": "Without a cache, every request goes all the way to the database.",
"stepIndex": 0,
"voice": {
"provider": "openai",
"voiceId": "alloy",
"voiceName": "Alloy"
}
},
{
"id": "s2_line_1",
"text": "A cache keeps recent answers close to the app.",
"stepIndex": 1,
"voice": {
"provider": "openai",
"voiceId": "alloy",
"voiceName": "Alloy"
}
},
{
"id": "s2_line_2",
"text": "The next request is answered in two milliseconds.",
"stepIndex": 2,
"voice": {
"provider": "openai",
"voiceId": "alloy",
"voiceName": "Alloy"
}
}
]
}
]
}A vertical Short
A 9:16 single-scene file in the flat form, with nodes, edges and steps at the top level. It shows:
- A headline that enters from above with
slideInUp. - Three cards that start at
opacity0 and are revealed one per step withslideInLeftand abackease. - A
progressbar whosevaluerises in each step and tweens on its own.
{
"$schema": "https://opencanviz.com/schema/v1.json",
"title": "3 habits of fast teams",
"artboard": {
"x": -540,
"y": -960,
"width": 1080,
"height": 1920,
"background": "#fffbeb"
},
"nodes": [
{
"id": "headline",
"kind": "text",
"x": -460,
"y": -760,
"w": 920,
"h": 240,
"text": "3 habits of\nfast teams",
"fontSize": 96,
"fontWeight": 800,
"lineHeight": 1.1,
"color": "#1c1917",
"textAlign": "center",
"verticalAlign": "middle",
"fill": "transparent",
"stroke": "none"
},
{
"id": "habit_0",
"kind": "rect",
"x": -440,
"y": -380,
"w": 880,
"h": 240,
"cornerRadius": 28,
"fill": "#fde68a",
"stroke": "#1c1917",
"strokeWidth": 4,
"label": "Ship small",
"labelColor": "#1c1917",
"labelFontSize": 52,
"labelFontWeight": "bold",
"opacity": 0
},
{
"id": "habit_1",
"kind": "rect",
"x": -440,
"y": -80,
"w": 880,
"h": 240,
"cornerRadius": 28,
"fill": "#bbf7d0",
"stroke": "#1c1917",
"strokeWidth": 4,
"label": "Review fast",
"labelColor": "#1c1917",
"labelFontSize": 52,
"labelFontWeight": "bold",
"opacity": 0
},
{
"id": "habit_2",
"kind": "rect",
"x": -440,
"y": 220,
"w": 880,
"h": 240,
"cornerRadius": 28,
"fill": "#bfdbfe",
"stroke": "#1c1917",
"strokeWidth": 4,
"label": "Automate tests",
"labelColor": "#1c1917",
"labelFontSize": 52,
"labelFontWeight": "bold",
"opacity": 0
},
{
"id": "progress",
"kind": "progress",
"x": -440,
"y": 620,
"w": 880,
"h": 28,
"value": 0,
"fill": "#e7e5e4",
"stroke": "none",
"progressColor": "#f59e0b"
}
],
"edges": [],
"steps": [
{
"id": "hook",
"durationMs": 1800,
"caption": "Three habits of fast teams.",
"nodes": [
{
"id": "headline",
"kind": "text",
"x": -460,
"y": -760,
"w": 920,
"h": 240,
"text": "3 habits of\nfast teams",
"fontSize": 96,
"fontWeight": 800,
"lineHeight": 1.1,
"color": "#1c1917",
"textAlign": "center",
"verticalAlign": "middle",
"fill": "transparent",
"stroke": "none"
},
{
"id": "habit_0",
"kind": "rect",
"x": -440,
"y": -380,
"w": 880,
"h": 240,
"cornerRadius": 28,
"fill": "#fde68a",
"stroke": "#1c1917",
"strokeWidth": 4,
"label": "Ship small",
"labelColor": "#1c1917",
"labelFontSize": 52,
"labelFontWeight": "bold",
"opacity": 0
},
{
"id": "habit_1",
"kind": "rect",
"x": -440,
"y": -80,
"w": 880,
"h": 240,
"cornerRadius": 28,
"fill": "#bbf7d0",
"stroke": "#1c1917",
"strokeWidth": 4,
"label": "Review fast",
"labelColor": "#1c1917",
"labelFontSize": 52,
"labelFontWeight": "bold",
"opacity": 0
},
{
"id": "habit_2",
"kind": "rect",
"x": -440,
"y": 220,
"w": 880,
"h": 240,
"cornerRadius": 28,
"fill": "#bfdbfe",
"stroke": "#1c1917",
"strokeWidth": 4,
"label": "Automate tests",
"labelColor": "#1c1917",
"labelFontSize": 52,
"labelFontWeight": "bold",
"opacity": 0
},
{
"id": "progress",
"kind": "progress",
"x": -440,
"y": 620,
"w": 880,
"h": 28,
"value": 0,
"fill": "#e7e5e4",
"stroke": "none",
"progressColor": "#f59e0b"
}
],
"edges": [],
"actions": [
{
"type": "visibility",
"target": {
"kind": "node",
"id": "headline"
},
"mode": "slideInUp",
"durationMs": 600
}
]
},
{
"id": "habit_0_in",
"delta": true,
"durationMs": 1800,
"caption": "One: ship small changes.",
"nodes": [
{
"id": "habit_0",
"opacity": 1
},
{
"id": "progress",
"value": 0.333
}
],
"edges": [],
"actions": [
{
"type": "visibility",
"target": {
"kind": "node",
"id": "habit_0"
},
"mode": "slideInLeft",
"durationMs": 500,
"ease": "back"
}
]
},
{
"id": "habit_1_in",
"delta": true,
"durationMs": 1800,
"caption": "Two: review them the same day.",
"nodes": [
{
"id": "habit_1",
"opacity": 1
},
{
"id": "progress",
"value": 0.667
}
],
"edges": [],
"actions": [
{
"type": "visibility",
"target": {
"kind": "node",
"id": "habit_1"
},
"mode": "slideInLeft",
"durationMs": 500,
"ease": "back"
}
]
},
{
"id": "habit_2_in",
"delta": true,
"durationMs": 1800,
"caption": "Three: let tests catch the mistakes.",
"nodes": [
{
"id": "habit_2",
"opacity": 1
},
{
"id": "progress",
"value": 1.0
}
],
"edges": [],
"actions": [
{
"type": "visibility",
"target": {
"kind": "node",
"id": "habit_2"
},
"mode": "slideInLeft",
"durationMs": 500,
"ease": "back"
}
]
}
]
}Make your own from a backup
The fastest way to learn the format is to look at a real project. Open any project in the editor, choose More > Download data (backup), and open the file in a text editor. A backup carries extra fields the app fills in, such as recorded audio. The rules for AI agents list which ones to leave out when you write a file from scratch.