Video Designer
./references/characters.md — Primitive geometric character design (Hey Duggee style), constraints, emotions
./references/path-guidelines.md — Path element schema, arrow markers, composite paths, path animations
./references/3d-shapes.md — Design 3D cubes/boxes as single unified elements (not separate faces)
./references/path-references.md — Different types of paths along with their parameters and output JSON example
COORDINATE SYSTEM
Origin: Top-left corner (0, 0)
X-axis: Increases rightward →
Y-axis: Increases downward ↓
FOR SHAPES/TEXT/ICONS:
Position: Always refers to element's CENTER point
FOR PATHS:
All coordinates are ABSOLUTE screen positions
No position/size fields needed (implied by path coordinates)
ROTATION DIRECTIONS
0° = pointing up (↑)
90° = pointing right (→)
180° = pointing down (↓)
270° = pointing left (←)
Positive values = clockwise rotation
Negative values = counter-clockwise (-90° same as 270°)
SETTING TRANSFORMS BY ELEMENT TYPE
For shapes/text: Set `rotation` directly to the desired direction.
For assets: Check `asset_manifest` for `composition`.
- `composition` describes the asset's structure and orientation
- Infer the asset's base orientation from the composition description
- Use composition to determine if the asset is symmetric or asymmetric
SYMMETRIC ASSETS (no distinct top/bottom in composition):
- Use `rotation = desired_direction - inferred_orientation`
ASYMMETRIC ASSETS (composition mentions distinct top/bottom):
- If NO direction change needed, omit rotation/flip fields entirely
- If direction change is small (e.g., 45°) and won't invert, use `rotation` only
- If direction change would invert the asset (180°), use `flipX`/`flipY` instead of rotation
- `flipX: true` = horizontal mirror (for LEFT↔RIGHT flip)
- `flipY: true` = vertical mirror (for UP↔DOWN flip)
- Can combine flip + rotation for diagonal directions
Example: Asset with composition: "Wedge-shaped vehicle pointing RIGHT. Flat BOTTOM, angled TOP"
- Inferred orientation: 90° (pointing RIGHT)
- To point RIGHT: No transform needed (omit rotation/flip fields)
- To point UP-RIGHT: Use `rotation: -45` (small angle, no inversion)
- To point BOTTOM-RIGHT: Use `rotation: 45` (small angle, no inversion)
- To point LEFT: Use `flipX: true` (not rotation: 180 which inverts it)
- To point UP-LEFT: Use `flipX: true, rotation: 45`
- To point DOWN-LEFT: Use `flipX: true, rotation: -45`
EXAMPLE (1920×1080 viewport)
Screen center: x = 960, y = 540
Top-center: x = 960, y = 100
Bottom-left quadrant: x = 480, y = 810
Right edge center: x = 1820, y = 540
"video_metadata": {
"viewport_size": "1920x1080",
"backgroundColor": "#HEXCOLOR",
"layout": {
"strategy": "three-column|centered|freeform"
}
},
"elements": [
{
"id": "unique_element_id",
"type": "shape|text|asset|pattern|path",
"enterOn": 0,
"exitOn": 7664,
"content": "CRITICAL: Complete visual specification (see content_field_requirements)",
"x": number,
"y": number,
"width": number,
"height": number,
"rotation": number,
"flipX": boolean,
"flipY": boolean,
"orientation": number,
"scale": number,
"opacity": number,
"fill": "#HEXCOLOR",
"stroke": "#HEXCOLOR",
"strokeWidth": number,
"fontSize": number,
"fontWeight": number,
"textAlign": "left|center|right",
"lineHeight": number,
"zIndex": number,
"animation": {
"entrance": {
"type": "string (e.g., pop-in, fade-in, slide-in-left, slide-in-right, slide-in-top, slide-in-bottom, draw-on, cut, scale-in, scale-up, path-draw, etc.)",
"duration": number
},
"exit": {
"type": "string (e.g., fade-out, pop-out, slide-out-left, slide-out-right, slide-out-top, slide-out-bottom, cut, etc.)",
"duration": number
},
"actions": [
{
"on": number,
"duration": number,
"targetProperty": "opacity|scale|x|y|rotation|fill|stroke",
"value": number,
"easing": "linear|easeIn|easeOut|easeInOut"
},
{
"type": "follow-path",
"pathId": "path_element_id",
"autoRotate": boolean,
"on": number,
"duration": number,
"easing": "linear|easeIn|easeOut|easeInOut"
}
]
}
}
]
}
**Required fields per element:** `id`, `type`, `enterOn`, `exitOn`, `content`, `zIndex`
**Required for assets with follow-path:** `orientation` (inferred from composition, e.g., 0°=UP, 90°=RIGHT, 180°=DOWN, 270°=LEFT)
**Optional fields:** `animation` (but recommended for visual engagement)
</output-example>
<multiple-instances-pattern>
When you need multiple similar elements with only a few varying properties, use the `instances` pattern to avoid duplication and reduce token count.
<syntax>
All base properties go at the root level. The `instances` array specifies overrides for each copy.
```json
{
"id": "arrow",
"type": "shape",
"content": "Right-pointing arrow",
"enterOn": 1000,
"exitOn": 5000,
"x": 100,
"y": 200,
"width": 50,
"height": 50,
"fill": "#3B82F6",
"opacity": 1,
"instances": [
{ "useDefaults": true },
{ "x": 200 },
{ "x": 300, "fill": "#FF5722" },
{ "y": 400 }
]
}
This creates 4 elements:
arrow_0: x=100, y=200, fill=#3B82F6 (uses all base values)
arrow_1: x=200, y=200, fill=#3B82F6 (overrides only x)
arrow_2: x=300, y=200, fill=#FF5722 (overrides x and fill)
arrow_3: x=100, y=400, fill=#3B82F6 (overrides only y)
✅ MUST use instances for:
- Any 2+ elements of the same
type with similar structure
- This includes: shapes, paths, text labels, assets - ANY element type
Creates 3 dots:
dot_0: x=660, animation.entrance.duration=300 (base values)
dot_1: x=960, animation.entrance.duration=200, type="pop-in" inherited (deep merge)
dot_2: x=1260, fill=#FF5722, animation.entrance.duration=300 inherited
Creates 4 arc paths with different radii, positions, and directions while sharing the base coordinates.
- Logical organization - Related elements stay together (e.g., all parts of a smartphone)
- Simplified management - Transform entire groups at once (move, rotate, scale)
- Shared timing - Parent
enterOn/exitOn applies to all children unless overridden
- Reduced repetition - Common properties defined at parent level
❌ Don't use groups when:
- Single standalone element
- Elements are unrelated or independent
- No shared timing or transformation needed
Individual children can override timing and have their own animations.
CRITICAL: Timing Values Must Be Absolute
All timing values (enterOn, exitOn, action.on) must use absolute video timestamps, NOT relative scene timestamps.
Given:
scene.startTime = 18192 (absolute video time)
- Audio transcript shows word "dust" at
1777ms (relative to scene start)
Your timing should be:
"enterOn": 19969, // 18192 + 1777 = absolute video time
"exitOn": 24589 // matches scene.endTime (absolute)
Formula: absolute_time = scene.startTime + audio_relative_time
The content field is the most critical part. It must answer ALL of these:
| Aspect |
What to Specify |
Example |
| Shape/Form |
Exact geometry, proportions |
"Asymmetrical rounded blob—right lobe shorter, left lobe extends 2x downward" |
| Visual Details |
Colors, textures, features |
"Deep orange center (#E65100) fading to bright orange (#FF9800) edges, 3 subtle lighter spots" |
| Face/Expression |
If character: eyes, mouth, emotion |
"Wide white eyes with violet pupils, V-shaped pink eyebrows angled inward expressing anger" |
| Position Context |
Where in frame, relative to what |
"Centered in belly area of silhouette, taking 75% of belly's width" |
| Initial State |
Starting appearance |
"Begins as small concentrated core at liver's center" |
| Transformations |
What changes and how |
"On inhale: body compresses, eyes shrink, mouth tightens to small 'o'; on exhale: expands, eyes widen, mouth stretches to tall oval" |
| Interaction |
How it relates to other elements |
"Scales at same rate as silhouette to maintain relative position inside belly" |
Precision Test: Could someone draw this without seeing the original? If uncertain, add more detail.
Before designing anything, analyze the example to establish your style guide:
- Background color
- Layout strategy
Audio: "bat" at 5908ms (relative)
Element enterOn: 7000 + 5800 = 12800ms (absolute, with anticipation)
**Always add scene.startTime to audio timestamps.**
</sync-to-transcript>
</phase-2-design-new-scene>
</design-process>
</designer-general-guidelines>
<text-guidelines>
<text-separation>
Always create text elements when you need to show the text on the screen.
</text-separation>
<auto-sizing>
Text elements are auto-sized based on content and fontSize. Position elements with adequate spacing to avoid overlaps.
</auto-sizing>
<text-align-clarification>
**CRITICAL: textAlign is CSS only, NOT positioning**
The `textAlign` field controls how text lines flow within the text container. It does NOT change where the container is positioned.
**Universal coordinate system rule applies:**
- x,y is ALWAYS the center point of the text element
- textAlign affects internal text alignment, not container position
**When textAlign matters:**
- Multi-line text (e.g., "MONKEY\nTALKS") - shorter lines align left/center/right within the container width
- Single-line text with `containerWidth` set - text aligns within that fixed width
**When textAlign has NO visible effect:**
- Single-line text without containerWidth - the container shrinks to fit the text exactly, leaving no extra space for alignment to matter
**Example - Multi-line text at x=540 (viewport center):**
textAlign: "center" textAlign: "left"
┌──────┐ ┌──────┐
│MONKEY│ │MONKEY│
│ TALKS│ │TALKS │
└──────┘ └──────┘
↑ ↑
x=540 x=540
Both containers are centered on x=540. PHASED (longest line) fills container width. Only ARRAY (shorter line) shifts based on textAlign.
</text-align-clarification>
<text-sizing-guidelines>
<proportional-sizing>
Calculate text sizes as a percentage of viewport height for consistency across resolutions.
**Calculation formula:**
fontSize = viewport_height × percentage
**Principle:** Choose percentage based on:
- Visual hierarchy (headlines larger than body text)
- Readability requirements (ensure text is readable at target viewport size)
- Direction requirements (follow what the direction specifies)
- Context (technical content may need different sizing than casual content)
</proportional-sizing>
<container-usage>
**IMPORTANT:** Never create separate shape elements for text backgrounds. Use the `container` object instead.
The `container` object gives text its own background, border, and padding. The background automatically sizes to fit the text content.
</container-usage>
</text-sizing-guidelines>
<readability-rules>
- Keep text within viewport boundaries
</readability-rules>
<text-schema>
```json
{
"id": "Unique identifier for this text element",
"type": "Element type must be text",
"content": "Brief description of what this text represents or its purpose in the scene",
"text": "The actual text content to display",
"bgID": "ID of parent/background element - ONLY for positioning text ON diagram elements (optional)",
"enterOn": "ABSOLUTE video time in ms (scene.startTime + relative_time)",
"exitOn": "ABSOLUTE video time in ms (typically scene.endTime)",
"x": number,
"y": number,
"rotation": number,
"opacity": number,
"fontColor": "#HEXCOLOR",
"fontSize": number,
"textAlign": "left|center|right",
"fontWeight": number,
"lineHeight": number, // Optional, default 1.5
"containerWidth": number, // Optional, for fixed-width text that wraps
"padding": number, // Optional, default 8
"zIndex": number,
"animation": {
"entrance": {
"type": "Entry animation style (string - e.g., pop-in, fade-in, slide-in-left, slide-in-right, slide-in-top, slide-in-bottom, draw-on, cut, scale-in, etc.)",
"duration": "Entry animation duration in milliseconds"
},
"exit": {
"type": "Exit animation style (string - e.g., fade-out, pop-out, slide-out-left, slide-out-right, slide-out-top, slide-out-bottom, cut, etc.)",
"duration": "Exit animation duration in milliseconds"
}
},
"container": {
"padding": number,
"background": {
"type": "none|solid|gradient|frosted-glass|highlight",
"color": "#HEXCOLOR",
"opacity": number,
"gradient": {
"from": "#HEXCOLOR",
"to": "#HEXCOLOR",
"direction": "to-right|to-left|to-bottom|to-top|to-br|to-bl"
}
},
"border": {
"radius": number,
"color": "#HEXCOLOR",
"width": number
},
"backdropBlur": "sm|md|lg|xl"
}
}
{
"id": "code_example_label",
"type": "text",
"content": "Label displaying code example with gradient background",
"text": "Hello World!",
"bgID": "",
"enterOn": 1000,
"exitOn": 5000,
"x": 960,
"y": 540,
"rotation": 0,
"opacity": 1,
"fontColor": "#FFFFFF",
"fontSize": 96,
"textAlign": "center",
"fontWeight": 700,
"lineHeight": 1.4,
"zIndex": 5,
"animation": {
"entrance": {
"type": "pop-in",
"duration": 500
},
"exit": {
"type": "fade-out",
"duration": 400
}
},
"container": {
"padding": 20, // Calculate as percentage of fontSize or viewport dimension
"background": {
"type": "gradient",
"color": "#3B82F6",
"opacity": 0.9,
"gradient": {
"from": "#3B82F6",
"to": "#8B5CF6",
"direction": "to-right"
}
},
"border": {
"radius": 12, // Calculate as percentage of viewport dimension
"color": "#FFFFFF",
"width": 2 // Calculate as percentage of viewport dimension
},
"backdropBlur": "md"
}
}
1---2name: video-designer3description: Expert video designer that generates comprehensive design specifications based on video direction. Creates precise JSON schemas for scenes including elements, animations, timing, and styling following strict design guidelines.4---5
6# Video Designer
7
8<overview>
9This skill provides design guidelines for creating consistent, high-quality video content by following a specific schema. Load the relevant reference files based on what needs to be designed.
10
11`./references/characters.md` — Primitive geometric character design (Hey Duggee style), constraints, emotions
12`./references/path-guidelines.md` — Path element schema, arrow markers, composite paths, path animations
13`./references/3d-shapes.md` — Design 3D cubes/boxes as single unified elements (not separate faces)
14`./references/path-references.md` — Different types of paths along with their parameters and output JSON example
15</overview>
16<designer-general-guidelines>
17<coordinate-system>
18```
19COORDINATE SYSTEM
20Origin: Top-left corner (0, 0)
21X-axis: Increases rightward →
22Y-axis: Increases downward ↓
23
24FOR SHAPES/TEXT/ICONS:
25 Position: Always refers to element's CENTER point
26
27FOR PATHS:
28 All coordinates are ABSOLUTE screen positions
29 No position/size fields needed (implied by path coordinates)
30
31ROTATION DIRECTIONS
320° = pointing up (↑)
3390° = pointing right (→)
34180° = pointing down (↓)
35270° = pointing left (←)
36
37Positive values = clockwise rotation
38Negative values = counter-clockwise (-90° same as 270°)
39
40SETTING TRANSFORMS BY ELEMENT TYPE
41
42For shapes/text: Set `rotation` directly to the desired direction.
43
44For assets: Check `asset_manifest` for `composition`.
45- `composition` describes the asset's structure and orientation
46- Infer the asset's base orientation from the composition description
47- Use composition to determine if the asset is symmetric or asymmetric
48
49SYMMETRIC ASSETS (no distinct top/bottom in composition):
50- Use `rotation = desired_direction - inferred_orientation`
51
52ASYMMETRIC ASSETS (composition mentions distinct top/bottom):
53- If NO direction change needed, omit rotation/flip fields entirely
54- If direction change is small (e.g., 45°) and won't invert, use `rotation` only
55- If direction change would invert the asset (180°), use `flipX`/`flipY` instead of rotation
56- `flipX: true` = horizontal mirror (for LEFT↔RIGHT flip)
57- `flipY: true` = vertical mirror (for UP↔DOWN flip)
58- Can combine flip + rotation for diagonal directions
59
60Example: Asset with composition: "Wedge-shaped vehicle pointing RIGHT. Flat BOTTOM, angled TOP"
61- Inferred orientation: 90° (pointing RIGHT)
62- To point RIGHT: No transform needed (omit rotation/flip fields)
63- To point UP-RIGHT: Use `rotation: -45` (small angle, no inversion)
64- To point BOTTOM-RIGHT: Use `rotation: 45` (small angle, no inversion)
65- To point LEFT: Use `flipX: true` (not rotation: 180 which inverts it)
66- To point UP-LEFT: Use `flipX: true, rotation: 45`
67- To point DOWN-LEFT: Use `flipX: true, rotation: -45`
68
69EXAMPLE (1920×1080 viewport)
70Screen center: x = 960, y = 540
71Top-center: x = 960, y = 100
72Bottom-left quadrant: x = 480, y = 810
73Right edge center: x = 1820, y = 540
74```
75</coordinate-system>
76
77<output-example>
78Your output must be a valid JSON object matching this schema:
79```json
80{
81 "scene": 0,
82 "startTime": 0,
83 "endTime": 7664,
84
85 "video_metadata": {
86 "viewport_size": "1920x1080",
87 "backgroundColor": "#HEXCOLOR",
88 "layout": {
89 "strategy": "three-column|centered|freeform"
90 }
91 },
92 "elements": [
93 {
94 "id": "unique_element_id",
95 "type": "shape|text|asset|pattern|path",
96 "enterOn": 0,
97 "exitOn": 7664,
98 "content": "CRITICAL: Complete visual specification (see content_field_requirements)",
99 "x": number,
100 "y": number,
101 "width": number,
102 "height": number,
103 "rotation": number,
104 "flipX": boolean,
105 "flipY": boolean,
106 "orientation": number,
107 "scale": number,
108 "opacity": number,
109 "fill": "#HEXCOLOR",
110 "stroke": "#HEXCOLOR",
111 "strokeWidth": number,
112 "fontSize": number,
113 "fontWeight": number,
114 "textAlign": "left|center|right",
115 "lineHeight": number,
116 "zIndex": number,
117 "animation": {
118 "entrance": {
119 "type": "string (e.g., pop-in, fade-in, slide-in-left, slide-in-right, slide-in-top, slide-in-bottom, draw-on, cut, scale-in, scale-up, path-draw, etc.)",
120 "duration": number
121 },
122 "exit": {
123 "type": "string (e.g., fade-out, pop-out, slide-out-left, slide-out-right, slide-out-top, slide-out-bottom, cut, etc.)",
124 "duration": number
125 },
126 "actions": [
127 {
128 "on": number,
129 "duration": number,
130 "targetProperty": "opacity|scale|x|y|rotation|fill|stroke",
131 "value": number,
132 "easing": "linear|easeIn|easeOut|easeInOut"
133 },
134 {
135 "type": "follow-path",
136 "pathId": "path_element_id",
137 "autoRotate": boolean,
138 "on": number,
139 "duration": number,
140 "easing": "linear|easeIn|easeOut|easeInOut"
141 }
142 ]
143 }
144 }
145 ]
146}
147```
148
149**Required fields per element:** `id`, `type`, `enterOn`, `exitOn`, `content`, `zIndex`
150**Required for assets with follow-path:** `orientation` (inferred from composition, e.g., 0°=UP, 90°=RIGHT, 180°=DOWN, 270°=LEFT)
151**Optional fields:** `animation` (but recommended for visual engagement)
152</output-example>
153<multiple-instances-pattern>
154When you need multiple similar elements with only a few varying properties, use the `instances` pattern to avoid duplication and reduce token count.
155<syntax>
156All base properties go at the root level. The `instances` array specifies overrides for each copy.
157
158```json
159{
160 "id": "arrow",
161 "type": "shape",
162 "content": "Right-pointing arrow",
163 "enterOn": 1000,
164 "exitOn": 5000,
165 "x": 100,
166 "y": 200,
167 "width": 50,
168 "height": 50,
169 "fill": "#3B82F6",
170 "opacity": 1,
171 "instances": [
172 { "useDefaults": true },
173 { "x": 200 },
174 { "x": 300, "fill": "#FF5722" },
175 { "y": 400 }
176 ]
177}
178```
179
180**This creates 4 elements:**
181- `arrow_0`: x=100, y=200, fill=#3B82F6 (uses all base values)
182- `arrow_1`: x=200, y=200, fill=#3B82F6 (overrides only x)
183- `arrow_2`: x=300, y=200, fill=#FF5722 (overrides x and fill)
184- `arrow_3`: x=100, y=400, fill=#3B82F6 (overrides only y)
185</syntax>
186
187<rules>
1881. **First instance is always `{ "useDefaults": true }`** - Represents an element with no overrides (uses all base values)
1892. **Deep merge for nested objects** - Nested objects (`animation`, `path_params`, `container`) merge recursively; unspecified fields inherit from base
1903. **Field ordering** - `instances` must be the **last field** in the object
1914. **IDs generated automatically** - Pattern: `${baseId}_${index}` (e.g., `arrow_0`, `arrow_1`)
1925. **Works on any level** - Can be used on elements, `path_params`, or any nested object
1936. **Array length = instance count** - The array length determines how many copies are created
194</rules>
195<when-to-use>
196**⚠️ CRITICAL: Instances are MANDATORY when 2+ similar elements exist.**
197
198✅ **MUST use instances for:**
199- Any 2+ elements of the same `type` with similar structure
200- This includes: shapes, paths, text labels, assets - ANY element type
201</when-to-use>
202<examples>
203<grid-example>
204```json
205{
206 "id": "dot",
207 "type": "shape",
208 "content": "Small circular dot",
209 "enterOn": 1000,
210 "exitOn": 5000,
211 "x": 660,
212 "y": 290,
213 "width": 30,
214 "height": 30,
215 "fill": "#3B82F6",
216 "opacity": 1,
217 "animation": {
218 "entrance": { "type": "pop-in", "duration": 300 }
219 },
220 "instances": [
221 { "useDefaults": true },
222 { "x": 960, "animation": { "entrance": { "duration": 200 } } },
223 { "x": 1260, "fill": "#FF5722" }
224 ]
225}
226```
227
228Creates 3 dots:
229- `dot_0`: x=660, animation.entrance.duration=300 (base values)
230- `dot_1`: x=960, animation.entrance.duration=200, type="pop-in" inherited (deep merge)
231- `dot_2`: x=1260, fill=#FF5722, animation.entrance.duration=300 inherited
232</grid-example>
233<path-parameters-example>
234```json
235{
236 "id": "arc_element",
237 "type": "path",
238 "enterOn": 1000,
239 "exitOn": 5000,
240 "x": 400,
241 "y": 300,
242 "stroke": "#FFFFFF",
243 "strokeWidth": 3, // Calculate as percentage of viewport dimension
244 "path_params": {
245 "type": "arc",
246 "start_x": 100,
247 "start_y": 300,
248 "end_x": 400,
249 "end_y": 300,
250 "radius": 2, // Calculate as percentage of viewport dimension
251 "sweep": 1,
252 "instances": [
253 { "useDefaults": true },
254 { "radius": 4 },
255 { "start_x": 200, "end_x": 500 },
256 { "sweep": 0 }
257 ]
258 }
259}
260```
261
262Creates 4 arc paths with different radii, positions, and directions while sharing the base coordinates.
263</path-parameters-example>
264</examples>
265<benefits>
266- **Logical organization** - Related elements stay together (e.g., all parts of a smartphone)
267- **Simplified management** - Transform entire groups at once (move, rotate, scale)
268- **Shared timing** - Parent `enterOn`/`exitOn` applies to all children unless overridden
269- **Reduced repetition** - Common properties defined at parent level
270</benefits>
271</multiple-instances-pattern>
272<when-to-use-groups>
273✅ **Use groups when:**
274- Multiple elements logically belong together (parts of an icon, device, or object)
275- Elements share common timing or transformations
276- Scene has many elements that need organization
277- Elements form a composite object (phone = body + screen + buttons)
278
279❌ **Don't use groups when:**
280- Single standalone element
281- Elements are unrelated or independent
282- No shared timing or transformation needed
283</when-to-use-groups>
284<group-properties>
285Groups support these properties:
286- **Timing:** `enterOn`, `exitOn` (inherited by children unless overridden)
287- **Transform:** `x`, `y`, `rotation`, `scale` (applied to entire group)
288- **Animation:** `entrance`, `exit` (for the group as a whole)
289
290Individual children can override timing and have their own animations.
291</group-properties>
292<timing-values>
293**CRITICAL: Timing Values Must Be Absolute**
294
295**All timing values (`enterOn`, `exitOn`, `action.on`) must use absolute video timestamps, NOT relative scene timestamps.**
296
297Given:
298- `scene.startTime = 18192` (absolute video time)
299- Audio transcript shows word "dust" at `1777ms` (relative to scene start)
300
301Your timing should be:
302```json
303"enterOn": 19969, // 18192 + 1777 = absolute video time
304"exitOn": 24589 // matches scene.endTime (absolute)
305```
306
307**Formula:** `absolute_time = scene.startTime + audio_relative_time`
308</timing-values>
309<content-field-requirements>
310The `content` field is the most critical part. It must answer ALL of these:
311
312| Aspect | What to Specify | Example |
313|--------|-----------------|---------|
314| **Shape/Form** | Exact geometry, proportions | "Asymmetrical rounded blob—right lobe shorter, left lobe extends 2x downward" |
315| **Visual Details** | Colors, textures, features | "Deep orange center (#E65100) fading to bright orange (#FF9800) edges, 3 subtle lighter spots" |
316| **Face/Expression** | If character: eyes, mouth, emotion | "Wide white eyes with violet pupils, V-shaped pink eyebrows angled inward expressing anger" |
317| **Position Context** | Where in frame, relative to what | "Centered in belly area of silhouette, taking 75% of belly's width" |
318| **Initial State** | Starting appearance | "Begins as small concentrated core at liver's center" |
319| **Transformations** | What changes and how | "On inhale: body compresses, eyes shrink, mouth tightens to small 'o'; on exhale: expands, eyes widen, mouth stretches to tall oval" |
320| **Interaction** | How it relates to other elements | "Scales at same rate as silhouette to maintain relative position inside belly" |
321
322**Precision Test**: Could someone draw this without seeing the original? If uncertain, add more detail.
323</content-field-requirements>
324<design-process>
325<phase-1-extract-design-system>
326Before designing anything, analyze the example to establish your style guide:
327<global-properties>
328- Background color
329- Layout strategy
330</global-properties>
331<typography>
332Document font styles and calculate proportional sizes (fontSize ÷ viewport_height).
333</typography>
334<animation-library>
335Document each animation type with duration:
336```
337pop-in: Xms
338fade-in: Xms
339scale-grow: Xms
340color-transition: Xms
341```
342</animation-library>
343<visual-personality>
344| Attribute | Example's Approach |
345|-----------|-------------------|
346| Mood | (playful/serious/technical) |
347| Shapes | (rounded/sharp, thick/thin strokes) |
348| Characters | (Kawaii faces, googly eyes, expressive) |
349| Motion | (organic wobbles, breathing animations) |
350| Metaphors | (how abstract concepts become visual) |
351</visual-personality>
352</phase-1-extract-design-system>
353<phase-2-design-new-scene>
354<parse-narrative>
355Reconstruct sentences from transcript. Identify:
356- Key moments needing visual emphasis
357- Natural timing beats for element entrances
358</parse-narrative>
359<identify-elements>
360From scene_direction, list:
361- Primary elements (must have)
362- Supporting elements (enhance clarity)
363- Labels/text (identify concepts)
364**CRITICAL: Only create elements mentioned in `videoDescription`.**
365</identify-elements>
366<design-each-element>
367For every element:
3681. Write complete `content` description (see content_field_requirements)
3692. Calculate exact position and size
3703. Assign zIndex for layer order
3714. Set timing synced to transcript
3725. Define entrance/exit animations
3736. Specify any mid-scene actions
374</design-each-element>
375<sync-to-transcript>
376Audio timestamps are relative to scene start. Convert to absolute video time:
377```
378Audio: "ball" at 4708ms (relative)
379Scene: startTime = 7000ms (absolute)
380Element enterOn: 7000 + 4600 = 11600ms (absolute, with anticipation)
381
382Audio: "bat" at 5908ms (relative)
383Element enterOn: 7000 + 5800 = 12800ms (absolute, with anticipation)
384```
385**Always add scene.startTime to audio timestamps.**
386</sync-to-transcript>
387
388</phase-2-design-new-scene>
389
390</design-process>
391
392</designer-general-guidelines>
393
394<text-guidelines>
395
396<text-separation>
397Always create text elements when you need to show the text on the screen.
398</text-separation>
399
400<auto-sizing>
401Text elements are auto-sized based on content and fontSize. Position elements with adequate spacing to avoid overlaps.
402</auto-sizing>
403
404<text-align-clarification>
405**CRITICAL: textAlign is CSS only, NOT positioning**
406
407The `textAlign` field controls how text lines flow within the text container. It does NOT change where the container is positioned.
408
409**Universal coordinate system rule applies:**
410- x,y is ALWAYS the center point of the text element
411- textAlign affects internal text alignment, not container position
412
413**When textAlign matters:**
414- Multi-line text (e.g., "MONKEY\nTALKS") - shorter lines align left/center/right within the container width
415- Single-line text with `containerWidth` set - text aligns within that fixed width
416
417**When textAlign has NO visible effect:**
418- Single-line text without containerWidth - the container shrinks to fit the text exactly, leaving no extra space for alignment to matter
419
420**Example - Multi-line text at x=540 (viewport center):**
421```
422textAlign: "center" textAlign: "left"
423┌──────┐ ┌──────┐
424│MONKEY│ │MONKEY│
425│ TALKS│ │TALKS │
426└──────┘ └──────┘
427 ↑ ↑
428 x=540 x=540
429```
430
431Both containers are centered on x=540. PHASED (longest line) fills container width. Only ARRAY (shorter line) shifts based on textAlign.
432</text-align-clarification>
433
434<text-sizing-guidelines>
435
436<proportional-sizing>
437Calculate text sizes as a percentage of viewport height for consistency across resolutions.
438
439**Calculation formula:**
440```
441fontSize = viewport_height × percentage
442```
443
444**Principle:** Choose percentage based on:
445- Visual hierarchy (headlines larger than body text)
446- Readability requirements (ensure text is readable at target viewport size)
447- Direction requirements (follow what the direction specifies)
448- Context (technical content may need different sizing than casual content)
449</proportional-sizing>
450
451<container-usage>
452**IMPORTANT:** Never create separate shape elements for text backgrounds. Use the `container` object instead.
453
454The `container` object gives text its own background, border, and padding. The background automatically sizes to fit the text content.
455</container-usage>
456
457</text-sizing-guidelines>
458
459<readability-rules>
460- Keep text within viewport boundaries
461</readability-rules>
462
463<text-schema>
464```json
465{
466 "id": "Unique identifier for this text element",
467 "type": "Element type must be text",
468 "content": "Brief description of what this text represents or its purpose in the scene",
469 "text": "The actual text content to display",
470 "bgID": "ID of parent/background element - ONLY for positioning text ON diagram elements (optional)",
471 "enterOn": "ABSOLUTE video time in ms (scene.startTime + relative_time)",
472 "exitOn": "ABSOLUTE video time in ms (typically scene.endTime)",
473 "x": number,
474 "y": number,
475 "rotation": number,
476 "opacity": number,
477 "fontColor": "#HEXCOLOR",
478 "fontSize": number,
479 "textAlign": "left|center|right",
480 "fontWeight": number,
481 "lineHeight": number, // Optional, default 1.5
482 "containerWidth": number, // Optional, for fixed-width text that wraps
483 "padding": number, // Optional, default 8
484 "zIndex": number,
485
486 "animation": {
487 "entrance": {
488 "type": "Entry animation style (string - e.g., pop-in, fade-in, slide-in-left, slide-in-right, slide-in-top, slide-in-bottom, draw-on, cut, scale-in, etc.)",
489 "duration": "Entry animation duration in milliseconds"
490 },
491 "exit": {
492 "type": "Exit animation style (string - e.g., fade-out, pop-out, slide-out-left, slide-out-right, slide-out-top, slide-out-bottom, cut, etc.)",
493 "duration": "Exit animation duration in milliseconds"
494 }
495 },
496
497 "container": {
498 "padding": number,
499 "background": {
500 "type": "none|solid|gradient|frosted-glass|highlight",
501 "color": "#HEXCOLOR",
502 "opacity": number,
503 "gradient": {
504 "from": "#HEXCOLOR",
505 "to": "#HEXCOLOR",
506 "direction": "to-right|to-left|to-bottom|to-top|to-br|to-bl"
507 }
508 },
509 "border": {
510 "radius": number,
511 "color": "#HEXCOLOR",
512 "width": number
513 },
514 "backdropBlur": "sm|md|lg|xl"
515 }
516 }
517 ```
518
519 ```json
520 {
521 "id": "code_example_label",
522 "type": "text",
523 "content": "Label displaying code example with gradient background",
524 "text": "Hello World!",
525 "bgID": "",
526 "enterOn": 1000,
527 "exitOn": 5000,
528 "x": 960,
529 "y": 540,
530 "rotation": 0,
531 "opacity": 1,
532 "fontColor": "#FFFFFF",
533 "fontSize": 96,
534 "textAlign": "center",
535 "fontWeight": 700,
536 "lineHeight": 1.4,
537 "zIndex": 5,
538
539 "animation": {
540 "entrance": {
541 "type": "pop-in",
542 "duration": 500
543 },
544 "exit": {
545 "type": "fade-out",
546 "duration": 400
547 }
548 },
549
550 "container": {
551 "padding": 20, // Calculate as percentage of fontSize or viewport dimension
552 "background": {
553 "type": "gradient",
554 "color": "#3B82F6",
555 "opacity": 0.9,
556 "gradient": {
557 "from": "#3B82F6",
558 "to": "#8B5CF6",
559 "direction": "to-right"
560 }
561 },
562 "border": {
563 "radius": 12, // Calculate as percentage of viewport dimension
564 "color": "#FFFFFF",
565 "width": 2 // Calculate as percentage of viewport dimension
566 },
567 "backdropBlur": "md"
568 }
569 }
570 ```
571</text-schema>
572<key-points>
5731. **Auto-fit backgrounds**: When using container.background, the background automatically sizes to fit the text + padding
5742. **Padding makes backgrounds auto-size**: The container.padding property creates spacing and makes the background fit perfectly
5753. **No separate shape elements needed**: Text backgrounds are built into the text element itself
576</key-points>
577</text-guidelines>