---
name: cocoplay-video
description: Generate CocoPlay video projects as JSON. Use this whenever the user wants to create an animated explainer, promo, social video, code walkthrough, data-story, or poster/image for CocoPlay (cocoplay video studio) — or asks for a "CocoPlay JSON". Produces one complete, valid Project JSON object the user pastes into CocoPlay's editor (Projects → Paste JSON, or the editor's JSON → Paste new).
---

# CocoPlay video generator

CocoPlay is a JSON-driven motion-video studio. A whole video is ONE JSON object. Your job: turn the user's request into a single valid CocoPlay `Project` JSON they can paste straight into the app. The same JSON drives the live preview and the MP4 export, so it must be correct and self-contained.

## Output contract (READ FIRST)
- Output **exactly one JSON object** and nothing else — no markdown, no code fences, no comments, no trailing commas, no prose before or after.
- Double quotes on every key and string. Never single quotes. Close every bracket/brace.
- Times are in **seconds** (floats). Coordinates are in **video space** (the intrinsic width/height below), top-left origin, NOT screen pixels.
- Colors are hex (`#rrggbb`/`#rrggbbaa`) OR a theme token string `"token:colors.primary"`. Sizes are a number OR a token like `"token:fontSizes.h1"`.
- Keep it finishable: **14–22 elements**. A complete, valid document beats one extra element.

## Top-level shape
```
{
  "id": "placeholder",                      // app overwrites this
  "name": "<short title>",
  "schemaVersion": "1.1.0",
  "video": {
    "width": 1920, "height": 1080,          // see Canvas sizes
    "fps": 30,
    "duration": <seconds>,                  // match the timeline
    "background": <Background>
  },
  "theme": <Theme>,                         // see Theme
  "timeline": { "duration": <seconds>, "elements": [ <Element>, ... ] },
  "audio": [ <AudioTrack>, ... ]            // optional
}
```

## Canvas sizes (pick to fit the platform)
- 1920×1080 — landscape: YouTube, web, slides (default)
- 1080×1920 — vertical: TikTok, Reels, Shorts, Stories
- 1080×1080 — square: Instagram feed
- 1080×1350 — portrait 4:5: Instagram
- 1000×1500 — Pinterest
For a still **image/poster**, set `"meta": { "kind": "image", "frameTime": 0 }` at the top level and a short duration (1s); design one frame.

## Theme
```
"theme": {
  "colors": { "primary":"#7c6af7", "secondary":"#34d399", "background":"#08060f",
              "surface":"#15121f", "text":"#f1f5f9", "muted":"#8d87a8", "accent":"#f472b6" },
  "fonts":  { "heading":"var(--font-display)", "body":"var(--font-body)", "mono":"var(--font-mono)" },
  "fontSizes": { "h1":96, "h2":64, "h3":44, "body":36, "small":26, "caption":20 },
  "radii": { "sm":8, "md":16, "lg":28, "pill":999 },
  "spacing": { "sm":12, "md":24, "lg":48 }
}
```
Reference any of these from elements as `"token:colors.primary"`, `"token:fontSizes.h1"`, etc. Editing the theme then re-themes everything.

### Cohesive palettes (pick ONE that fits the topic; set in theme.colors)
- Indigo/Mint (tech, SaaS):     primary #7c6af7  secondary #34d399  accent #f472b6  bg #08060f  surface #15121f
- Electric Blue (product, fin): primary #3b82f6  secondary #22d3ee  accent #f59e0b  bg #070b18  surface #0f1628
- Sunset (creative, social):    primary #fb7185  secondary #fb923c  accent #facc15  bg #1a0a14  surface #2a121f
- Emerald (growth, money, eco):  primary #10b981  secondary #34d399  accent #fde047  bg #04140e  surface #0a2018
- Violet/Cyan (AI, futuristic): primary #a855f7  secondary #22d3ee  accent #f0abfc  bg #0a0618  surface #170f28
text is always near-white (#f1f5f9); muted is a desaturated tint of the bg.

## Background (video.background)
- `{ "type":"color", "value":"token:colors.background" }`
- `{ "type":"gradient", "angle":150, "stops":[{ "color":"#0d0820","at":0 }, { "color":"#08060f","at":1 }] }`  ← prefer this
- `{ "type":"image", "src":"https://…", "fit":"cover" }`
- `{ "type":"video", "src":"https://…mp4", "fit":"cover", "loop":true, "muted":true }`

## Element — every element has these base fields
```
{
  "id": "<unique-kebab-id>",
  "type": "text|image|video|shape|code|chart|flow-node|connector|group",
  "layer": "background|midground|foreground",
  "zIndex": <0-100>,
  "start": <s>, "end": <s>,                 // when it's on screen
  "transform": { "x":<px>, "y":<px>, "width":<px>, "height":<px>, "rotation":0, "opacity":1, "scale":1 },
  "style": <BoxStyle>,                        // optional card backdrop, see below
  "animations": <Animations>,                 // optional, see Animation
  "props": { ... type-specific ... }
}
```

### BoxStyle — optional `"style"` on ANY element (a card/panel behind it)
```
"style": { "fill":"token:colors.surface", "stroke":"#2a2540", "strokeWidth":1,
           "radius":20, "padding":28, "shadow":"0 18px 44px #00000070", "backdropBlur":8 }
```
Use it to put text/code/charts/icons on a contained card. The card is drawn **outset** by `padding`, so a card always gives its content breathing room — keep `padding` ≥ 24 when an icon or short label sits in a small box, or it will look cramped.

## Props by element type

### text
```
"props": {
  "variant": "heading|paragraph|list|label",   // NOT "caption"
  "content": "<text>",                          // or for list use items + content:""
  "fontSize": "token:fontSizes.h2" | <number>,
  "color": "token:colors.text",
  "align": "left|center|right",
  "fontWeight": 700, "lineHeight": 1.3, "letterSpacing": 2,
  "font": "token:fonts.heading|token:fonts.body|token:fonts.mono",
  "glow": { "blur":40, "color":"token:colors.primary" },
  "reveal": { "mode":"word|line|char", "stagger":0.12 }
}
```
- A big emoji icon = a heading text, fontSize 100–160, single emoji, with a float/pulse loop. **Give its box generous width/height (≥ fontSize × 1.6) so the glyph isn't clipped.**
- List: `"variant":"list", "content":"", "items":["…","…"], "ordered":false, "reveal":{ "mode":"line","stagger":0.6 }`

### shape  (kind: rectangle | ellipse | line | arrow | triangle)
```
"props": {
  "kind":"rectangle",
  "fill":"token:colors.primary",
  "fillGradient":{ "angle":135, "from":"token:colors.primary", "to":"#1a0a3e", "via":"token:colors.accent" },
  "stroke":"token:colors.accent", "strokeWidth":3, "dash":[14,10],
  "radius":24,
  "glow":{ "blur":80, "color":"token:colors.primary", "spread":20 },
  "shadow":{ "x":0, "y":20, "blur":50, "color":"#00000060" },
  "arrowHead":"none|end|both"      // line/arrow only
}
```
Big background "glow blobs" = large low-opacity ellipses (opacity 0.08–0.14) in layer:background with a glow + a breathe loop. Cards = rectangle with fillGradient + radius + shadow.

### image / video
```
"props": { "src":"https://…(real URL)", "fit":"cover|contain|fill" }            // image
"props": { "src":"https://…mp4", "fit":"cover", "muted":true, "loop":true, "volume":1, "trimStart":0 }  // video
```

### code
```
"props": {
  "language":"typescript|python|bash|javascript|go|rust|sql|json|html|css",
  "code":"<REAL multi-line snippet, use \\n>",
  "fontSize":22, "lineHeight":1.6,
  "syntaxTheme":"one-dark|dracula|monokai|github-dark|night-owl|dark|light",
  "showLineNumbers":true, "typeInDuration":0,
  "lineReveal":{ "anim":"slide-up|fade|type-in|pop", "stagger":0.1, "duration":0.35 },
  "highlightLines":[2,3], "focusMode":false,
  "terminalMode":true,    // lines starting with $ are commands, rest is output
  "diffMode":true,        // lines starting with +/- are added/removed
  "tabs":["app.ts","util.ts"],
  "edits":[{ "at":3, "code":"<full new code>", "anim":"slide-left", "duration":0.5 }],
  "callouts":[{ "line":2, "text":"runs first", "side":"right|bottom" }],
  "inlineHighlights":[{ "line":2, "colStart":7, "colEnd":18, "color":"token:colors.accent" }],
  "splitCode":"<right pane>", "splitLanguage":"diff", "splitLabels":["Before","After"]
}
```
Give code blocks a roomy box (e.g. 1000×520) so lines don't clip.

### chart  (prefer multi-series with per-series colors)
```
"props": {
  "kind":"bar|line",
  "categories":["Jan","Feb","Mar","Apr"],
  "series":[ { "label":"Revenue", "values":[42,55,61,74], "color":"token:colors.primary" },
             { "label":"Users",   "values":[30,38,52,60], "color":"token:colors.secondary" } ],
  "axis":{ "showX":true, "showY":true, "gridlines":true },
  "drawDuration":1.2
}
```
Simple shorthand also works: `{ "chartType":"bar", "data":[{ "label":"A","value":42 }], "color":"token:colors.primary" }`.

### flow-node  +  connector  (diagrams)
```
// flow-node
"props": { "label":"Step 1", "shape":"pill|rounded|box|diamond|circle",
           "fill":"token:colors.surface", "stroke":"token:colors.primary",
           "textColor":"token:colors.text", "fontSize":28 }
// connector — joins two element ids; STILL needs full base fields + a transform
"props": { "from":"<node-id>", "to":"<node-id>", "routing":"straight|orthogonal|curved",
           "fromSide":"auto", "toSide":"auto", "arrowHead":"end|both|none",
           "dash":[10,8], "label":"yes", "drawDuration":0.5,
           "stroke":"token:colors.accent", "strokeWidth":3 }
```
Connector transform can be `{ "x":0,"y":0,"width":10,"height":10,"rotation":0,"opacity":1 }`. `from`/`to` MUST be ids of elements present in the same `elements` array. Space nodes generously so connectors aren't cramped.

### group  (move/animate a set together; children positioned relative to the group box)
```
"props": { "children": [ <Element>, ... ] }   // child x/y are relative to the group's transform
```

## Animation
Put an `"animations"` object on an element. CocoPlay converts entry/exit/loop into fully-editable keyframe clips, so this simple form is all you need:
```
"animations": {
  "entry": { "type":"<entry>", "duration":0.5, "delay":0, "easing":"easeOutBack", "direction":"up" },
  "exit":  { "type":"fade", "duration":0.4 },
  "loop":  [ { "type":"float", "duration":3, "amplitude":12 } ]
}
```
- entry/exit types: `none fade slide zoom scale rise pop rotate blur wipe flip` (`slide`/`wipe`/`flip` need `"direction":"up|down|left|right"`).
- loop types: `float pulse rotate bounce wiggle breathe` (duration = period seconds; amplitude = strength).
- easings: `linear easeIn easeOut easeInOut easeInQuad easeOutQuad easeInOutQuad easeInCubic easeOutCubic easeInOutCubic easeInBack easeOutBack easeInOutBack easeOutElastic easeOutBounce`.

## audio (optional, top-level array)
```
"audio": [ { "id":"a1", "name":"VO", "src":"https://…mp3", "start":0, "end":12,
             "volume":1, "fadeIn":0.3, "fadeOut":0.5, "loop":false } ]
```

## Layout zones (1920×1080 — scale proportionally for other sizes)
- Title bar:    x:120  y:100  w:1680 h:140
- Center hero:  x:260  y:240  w:1400 h:560
- 3-col cards:  card1 x:80, card2 x:690, card3 x:1300 — each w:540 h:360, y:340
- Flow row:     y:440, nodes at x: 120, 520, 920, 1320 (room for connectors)
- Glow blob:    600–900px ellipse, opacity 0.08–0.14, layer:background

## Sizing rule (avoid cramped boxes)
Size every element's `transform` to comfortably contain its content with margin:
- Single emoji/icon: box ≈ fontSize × 1.6 on each side; center it.
- Short label on a card: card `padding` ≥ 24; box wide enough that text isn't truncated.
- Heading: width ≈ 60–80% of canvas; height ≈ fontSize × 1.8.
- Code/chart: give it a large box (e.g. ≥ 900×460).
When unsure, make the box bigger — content centers and overflow is hidden, so a too-tight box clips; a roomy box looks intentional.

## Craft checklist (do all)
- ONE cohesive palette from the list; high contrast; accent used sparingly.
- 3–4 scenes via start/end (hero → idea → detail/proof → outro). ≤ 6–7 elements visible at once; scenes' elements end before the next begins (except persistent blobs).
- One focal heading per scene (with glow). Never two h1s at once.
- 2 persistent glow blobs in the background with breathe loops.
- Entrances staggered in reading order (title → bar → body → cards L→R), 0.12–0.25s apart — cascade, never all-at-once.
- All drop shadows share one light direction (same y sign, similar blur). Cards have shadow; blobs sit behind.
- Real, specific copy (numbers, real code, real step names). No lorem ipsum, no "Feature 1".
- Boxes sized with margin so nothing is cramped or clipped (see Sizing rule).
- Valid JSON: no comments, no trailing commas, all braces closed, connector from/to reference real ids.

When done, output ONLY the JSON object. The user pastes it into CocoPlay → Projects → "Paste JSON", or in the editor via the JSON button → "Paste new".
