motionrender 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +61 -2
- package/blocks/badge.json +68 -0
- package/blocks/big-stat.json +240 -0
- package/blocks/cta-end.json +283 -0
- package/blocks/feature-list.json +286 -0
- package/blocks/footage.json +43 -0
- package/blocks/fx-background-swap.json +170 -0
- package/blocks/fx-before-after.json +212 -0
- package/blocks/fx-captions.json +34 -0
- package/blocks/fx-chat.json +368 -0
- package/blocks/fx-checklist.json +609 -0
- package/blocks/fx-clone-wall.json +181 -0
- package/blocks/fx-color-pop.json +92 -0
- package/blocks/fx-command-bridge.json +358 -0
- package/blocks/fx-counter-hero.json +206 -0
- package/blocks/fx-cta-card.json +337 -0
- package/blocks/fx-day-to-night.json +174 -0
- package/blocks/fx-duo-title.json +245 -0
- package/blocks/fx-duotone.json +95 -0
- package/blocks/fx-extruded-title.json +309 -0
- package/blocks/fx-gallery.json +239 -0
- package/blocks/fx-glass-cards.json +921 -0
- package/blocks/fx-glitch-title.json +373 -0
- package/blocks/fx-grade-look.json +91 -0
- package/blocks/fx-highlighter.json +53 -0
- package/blocks/fx-icon-orbit.json +2694 -0
- package/blocks/fx-lens-flare.json +325 -0
- package/blocks/fx-letterbox.json +105 -0
- package/blocks/fx-light-leak.json +140 -0
- package/blocks/fx-map-journey.json +443 -0
- package/blocks/fx-neon-outline.json +743 -0
- package/blocks/fx-notifications.json +456 -0
- package/blocks/fx-progress-ring.json +143 -0
- package/blocks/fx-pull-out.json +68 -0
- package/blocks/fx-punch-in.json +81 -0
- package/blocks/fx-rgb-flash.json +166 -0
- package/blocks/fx-shake-hit.json +120 -0
- package/blocks/fx-split-reveal.json +110 -0
- package/blocks/fx-spotlight.json +1383 -0
- package/blocks/fx-stats-split.json +500 -0
- package/blocks/fx-step-tracker.json +528 -0
- package/blocks/fx-sticker-freeze.json +584 -0
- package/blocks/fx-strike-rewrite.json +79 -0
- package/blocks/fx-ticker.json +179 -0
- package/blocks/fx-timeline.json +1287 -0
- package/blocks/fx-tracked-label.json +167 -0
- package/blocks/fx-typewriter-quote.json +164 -0
- package/blocks/handle.json +43 -0
- package/blocks/icon-row.json +404 -0
- package/blocks/image-caption.json +248 -0
- package/blocks/lower-third.json +106 -0
- package/blocks/phone-mockup.json +459 -0
- package/blocks/progress-bar.json +51 -0
- package/blocks/quote.json +347 -0
- package/blocks/section-title.json +240 -0
- package/blocks/split-screen.json +283 -0
- package/blocks/stat-grid.json +315 -0
- package/blocks/steps.json +266 -0
- package/blocks/title-hero.json +287 -0
- package/dist/builder.d.ts +1 -0
- package/dist/builder.js +169 -0
- package/dist/cli.js +9301 -0
- package/dist/fonts/LICENSE-Inter.txt +93 -0
- package/dist/fonts/inter-cyrillic-ext-opsz-italic.woff2 +0 -0
- package/dist/fonts/inter-cyrillic-ext-opsz-normal.woff2 +0 -0
- package/dist/fonts/inter-cyrillic-opsz-italic.woff2 +0 -0
- package/dist/fonts/inter-cyrillic-opsz-normal.woff2 +0 -0
- package/dist/fonts/inter-greek-ext-opsz-italic.woff2 +0 -0
- package/dist/fonts/inter-greek-ext-opsz-normal.woff2 +0 -0
- package/dist/fonts/inter-greek-opsz-italic.woff2 +0 -0
- package/dist/fonts/inter-greek-opsz-normal.woff2 +0 -0
- package/dist/fonts/inter-latin-ext-opsz-italic.woff2 +0 -0
- package/dist/fonts/inter-latin-ext-opsz-normal.woff2 +0 -0
- package/dist/fonts/inter-latin-opsz-italic.woff2 +0 -0
- package/dist/fonts/inter-latin-opsz-normal.woff2 +0 -0
- package/dist/fonts/inter-vietnamese-opsz-italic.woff2 +0 -0
- package/dist/fonts/inter-vietnamese-opsz-normal.woff2 +0 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2330 -0
- package/dist/player.d.ts +1 -0
- package/dist/player.js +80 -0
- package/dist/react.d.ts +1 -0
- package/dist/react.js +140 -0
- package/dist/runtime/canvas-runtime.js +261 -0
- package/dist/runtime/lottie_light_canvas.min.js +1 -0
- package/dist/runtime/text-layout.js +50 -0
- package/dist/sfx/click.wav +0 -0
- package/dist/sfx/ding.wav +0 -0
- package/dist/sfx/pop.wav +0 -0
- package/dist/sfx/riser.wav +0 -0
- package/dist/sfx/thump.wav +0 -0
- package/dist/sfx/tick.wav +0 -0
- package/dist/sfx/whoosh.wav +0 -0
- package/dist/types/packages/ai/src/assets.d.ts +26 -0
- package/dist/types/packages/ai/src/batch.d.ts +19 -0
- package/dist/types/packages/ai/src/compose.d.ts +34 -0
- package/dist/types/packages/ai/src/history.d.ts +37 -0
- package/dist/types/packages/ai/src/image.d.ts +19 -0
- package/dist/types/packages/ai/src/index.d.ts +13 -0
- package/dist/types/packages/ai/src/json.d.ts +2 -0
- package/dist/types/packages/ai/src/narrate.d.ts +63 -0
- package/dist/types/packages/ai/src/patch.d.ts +15 -0
- package/dist/types/packages/ai/src/project.d.ts +27 -0
- package/dist/types/packages/ai/src/prompt.d.ts +30 -0
- package/dist/types/packages/ai/src/providers.d.ts +109 -0
- package/dist/types/packages/ai/src/template.d.ts +80 -0
- package/dist/types/packages/ai/src/voiceover.d.ts +49 -0
- package/dist/types/packages/animation/src/index.d.ts +27 -0
- package/dist/types/packages/assets/src/cache.d.ts +26 -0
- package/dist/types/packages/assets/src/index.d.ts +8 -0
- package/dist/types/packages/builder/src/index.d.ts +270 -0
- package/dist/types/packages/cli/src/ai-studio-page.d.ts +1 -0
- package/dist/types/packages/cli/src/ai-studio.d.ts +35 -0
- package/dist/types/packages/cli/src/ai.d.ts +3 -0
- package/dist/types/packages/cli/src/args.d.ts +9 -0
- package/dist/types/packages/cli/src/blocks-cli.d.ts +3 -0
- package/dist/types/packages/cli/src/blocks.d.ts +50 -0
- package/dist/types/packages/cli/src/build.d.ts +1 -0
- package/dist/types/packages/cli/src/dev-page.d.ts +1 -0
- package/dist/types/packages/cli/src/dev.d.ts +13 -0
- package/dist/types/packages/cli/src/index.d.ts +2 -0
- package/dist/types/packages/cli/src/init.d.ts +10 -0
- package/dist/types/packages/cli/src/serve.d.ts +2 -0
- package/dist/types/packages/cli/src/studio.d.ts +36 -0
- package/dist/types/packages/cli/src/templates.d.ts +63 -0
- package/dist/types/packages/cli/src/thumbnail.d.ts +20 -0
- package/dist/types/packages/cli/src/track.d.ts +35 -0
- package/dist/types/packages/cli/src/worker.d.ts +13 -0
- package/dist/types/packages/contracts/src/index.d.ts +377 -0
- package/dist/types/packages/core/src/audio.d.ts +14 -0
- package/dist/types/packages/core/src/distributed.d.ts +50 -0
- package/dist/types/packages/core/src/index.d.ts +63 -0
- package/dist/types/packages/core/src/media.d.ts +11 -0
- package/dist/types/packages/core/src/review.d.ts +29 -0
- package/dist/types/packages/ffmpeg/src/index.d.ts +68 -0
- package/dist/types/packages/graphics/src/fonts.d.ts +3 -0
- package/dist/types/packages/graphics/src/index.d.ts +121 -0
- package/dist/types/packages/graphics/src/runtime.d.ts +10 -0
- package/dist/types/packages/jobs/src/env.d.ts +78 -0
- package/dist/types/packages/jobs/src/handlers.d.ts +17 -0
- package/dist/types/packages/jobs/src/index.d.ts +5 -0
- package/dist/types/packages/jobs/src/input.d.ts +18 -0
- package/dist/types/packages/jobs/src/queue.d.ts +70 -0
- package/dist/types/packages/jobs/src/redis.d.ts +42 -0
- package/dist/types/packages/player/src/bundle.d.ts +4 -0
- package/dist/types/packages/player/src/canvas-runtime.d.ts +11 -0
- package/dist/types/packages/player/src/index.d.ts +29 -0
- package/dist/types/packages/react/src/index.d.ts +76 -0
- package/dist/types/packages/renderer/src/index.d.ts +35 -0
- package/dist/types/packages/renderer/src/pool.d.ts +7 -0
- package/dist/types/packages/renderer/src/video.d.ts +9 -0
- package/dist/types/packages/schema/src/captions.d.ts +24 -0
- package/dist/types/packages/schema/src/expression.d.ts +1 -0
- package/dist/types/packages/schema/src/index.d.ts +1213 -0
- package/dist/types/packages/schema/src/variables.d.ts +115 -0
- package/dist/types/packages/storage/src/index.d.ts +78 -0
- package/dist/types/packages/timeline/src/index.d.ts +104 -0
- package/package.json +78 -4
- package/skills/ove/SKILL.md +356 -0
- package/skills/ove-ai-compose/SKILL.md +36 -0
- package/skills/ove-batch-videos/SKILL.md +23 -0
- package/skills/ove-code-video/SKILL.md +68 -0
- package/skills/ove-effects/SKILL.md +65 -0
- package/skills/ove-make-reel/SKILL.md +30 -0
- package/skills/ove-voiceover-captions/SKILL.md +31 -0
- package/studio/assets/index-CzOvWv9w.css +1 -0
- package/studio/assets/index-J_fvKgng.js +11 -0
- package/studio/index.html +16 -0
- package/templates/case-study/composition.json +358 -0
- package/templates/case-study/template.json +8 -0
- package/templates/event-invite/composition.json +245 -0
- package/templates/event-invite/template.json +8 -0
- package/templates/explainer-square/composition.json +1335 -0
- package/templates/explainer-square/template.json +13 -0
- package/templates/keynote-wide/composition.json +1458 -0
- package/templates/keynote-wide/template.json +13 -0
- package/templates/launch-wide/composition.json +1306 -0
- package/templates/launch-wide/template.json +13 -0
- package/templates/product-card/composition.json +196 -0
- package/templates/product-card/data.example.json +5 -0
- package/templates/product-card/template.json +8 -0
- package/templates/product-wide/composition.json +1609 -0
- package/templates/product-wide/template.json +14 -0
- package/templates/quote-card/composition.json +123 -0
- package/templates/quote-card/template.json +8 -0
- package/templates/sale-promo/composition.json +290 -0
- package/templates/sale-promo/template.json +8 -0
- package/templates/showcase-tall/composition.json +1646 -0
- package/templates/showcase-tall/template.json +14 -0
- package/templates/slideshow/composition.json +167 -0
- package/templates/slideshow/template.json +8 -0
- package/templates/social-reel/composition.json +252 -0
- package/templates/social-reel/template.json +8 -0
- package/templates/starter/assets/logo.svg +4 -0
- package/templates/starter/composition.json +84 -0
- package/templates/story-tall/composition.json +1379 -0
- package/templates/story-tall/template.json +13 -0
- package/themes/espresso.json +15 -0
- package/themes/midnight.json +15 -0
- package/themes/neon.json +15 -0
- package/themes/paper.json +15 -0
- package/themes/signal.json +15 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ove
|
|
3
|
+
description: Create and edit videos with Open Video Engine (OVE) by writing a JSON composition, then validating, previewing stills and rendering MP4. Use whenever the user asks to make, change, animate or render a video in a project that has an OVE composition.json.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Making videos with Open Video Engine
|
|
7
|
+
|
|
8
|
+
Short task guides sit beside this one: `ove-code-video` (video written as code, code layers), `ove-make-reel` (social video from blocks), `ove-voiceover-captions`, `ove-batch-videos` and `ove-ai-compose` (AI drafts, refine, history). Use the one that matches the request, and come back here for field details.
|
|
9
|
+
|
|
10
|
+
An OVE video is one JSON file, `composition.json`. You never write rendering code: you edit data, check it, look at frames, and render. `composition.schema.json` next to it is the authoritative JSON Schema; read it when unsure about a field.
|
|
11
|
+
|
|
12
|
+
## Loop
|
|
13
|
+
|
|
14
|
+
1. For a new video of more than a few seconds, agree beats, look and storyboard first (see Planning a video). Then edit `composition.json`.
|
|
15
|
+
2. `ove validate composition.json` and fix every error it reports.
|
|
16
|
+
3. `ove still composition.json --frame <n> --output still.png` and look at the image. Check the first frame, the end of each animation, and the last frame.
|
|
17
|
+
4. `ove review composition.json --output review.png`: fix every error and warning it lists, then look at the contact sheet it writes (20 frames across the video, labelled with frame numbers) as a whole. Does each scene have one clear focus? Does the video keep moving? Do scenes look like one system?
|
|
18
|
+
5. `ove render composition.json --output out.mp4` once the review is clean and the sheet looks right.
|
|
19
|
+
|
|
20
|
+
`ove review` samples a frame every half second and reports, with frame numbers and layer ids: text cut short with "…", running off the frame, under 2% of the frame's short side, overlapping other text or sitting under the captions (when that lasts a second or more); more than 40 words on screen; text on screen too briefly to read its words (about a quarter of a second a word, plus half a second); more than two headline-size texts competing (a headline set one word per line counts once); caption lines on screen under 0.7 s or faster than about 20 characters a second; sounds cut off mid-phrase by the end of their scene or `endFrame`; nearly equal font sizes or more than 8 steps of size; more than 5 text colours; 4 seconds or more where nothing moves; scenes all the same length; three or more transition styles; and sound quieter than -20 LUFS, louder than -11 LUFS or peaking above -1 dBTP. It exits with an error when it finds errors, `--json` prints the findings as JSON, `--every n` samples every n frames, `--sheet-frames n` sets how many frames the sheet shows. It is a check, not a verdict on taste: a clean review still needs your eye on the sheet.
|
|
21
|
+
|
|
22
|
+
`ove inspect composition.json` prints duration, scene count, variables and assets. A person working alongside you can run `ove dev composition.json` for a live preview that reloads on every save. Its Studio can also edit `composition.json` (layer timing and properties), so re-read the file before changing it rather than relying on an earlier copy. `ove doctor` checks FFmpeg, its encoders and the browser when a render fails for setup reasons. `ove schema` prints the JSON Schema. Every command checks its options: a misspelt or misplaced option stops it with a suggestion, and `--name=value` works as well as `--name value`. Inside the OVE repository itself, run the CLI as `npm run ove -- <command>`; paths are then relative to the repository root.
|
|
23
|
+
|
|
24
|
+
## Time
|
|
25
|
+
|
|
26
|
+
- Everything is in integer frames. Seconds × `fps` = frames (2 s at 30 fps = 60).
|
|
27
|
+
- `durationFrames` is the video length. Each scene needs `startFrame + durationFrames <= durationFrames` of the composition.
|
|
28
|
+
- Layer `startFrame` / `endFrame` (exclusive) are relative to the scene's start. Children of a `group` are relative to the group's start.
|
|
29
|
+
- Keyframe `frame` values are relative to the layer's own `startFrame`, not to the video. A layer starting at 45 with a keyframe at 0 animates from video frame 45.
|
|
30
|
+
- Every frame is computed independently from its number; there is no state carried between frames.
|
|
31
|
+
|
|
32
|
+
## Layout
|
|
33
|
+
|
|
34
|
+
- Origin is the top-left of the canvas; `x` grows right, `y` grows down, units are pixels of `width` × `height`.
|
|
35
|
+
- `rect`, `image` and `video`: `x`, `y` is the top-left corner; set `width` and `height`. `rect.radius` rounds corners.
|
|
36
|
+
- `circle`: `x`, `y` is the centre and `width` is the diameter.
|
|
37
|
+
- `text`: `y` is the top of the first line. `x` is the left edge, centre or right edge depending on `textAlign`. Use `\n` for line breaks; `lineHeight` is a multiple of `fontSize` (default 1.2). Text only wraps when you set `maxWidth`.
|
|
38
|
+
- Fitting text in a space: `maxWidth` wraps at spaces (a word wider than the box is split between letters). `maxLines` caps the line count; past it the text is cut and ends in "…". Add `minFontSize` and the text shrinks 1px at a time, down to that size, until it fits `maxWidth` and `maxLines` without splitting words. That is the right setting for template text like names: `"maxWidth": 900, "maxLines": 2, "minFontSize": 40`. Shrunk text keeps its top edge at `y`.
|
|
39
|
+
- Text style: `fontWeight` (100–900, `normal` or `bold`), `fontStyle` (`normal` or `italic`), `letterSpacing` in pixels, `color`.
|
|
40
|
+
- Rich text: give a text layer `spans` instead of `content` to mix styles in one block: `"spans": [{ "text": "Assistant vs. " }, { "text": "agent", "color": "#22d3ee", "fontWeight": 800 }]`. Each span can set `color` (or a gradient), `fontSize`, `fontWeight`, `fontStyle`, `fontFamily`, `background` (a highlight box) and `underline`; anything unset comes from the layer. Spans wrap together, keep a shared baseline across sizes, and shrink together for `maxLines`/`minFontSize`. Put spaces inside the span texts; `\n` breaks a line.
|
|
41
|
+
- `rotation` (degrees) and `scale` pivot around `x + originX`, `y + originY` (both default 0, the top-left). Set `"originX": "center", "originY": "center"` to spin or grow a rect, image or video around its middle (a circle's centre is already its `x`, `y`). For text, `center` is the middle of `maxWidth` and of one line; use pixel values for an exact pivot.
|
|
42
|
+
- `group`: children's coordinates are relative to the group's `x`, `y`; the group's opacity, scale and rotation apply to all children.
|
|
43
|
+
- Higher `zIndex` draws on top within a scene; equal values keep the order they appear in the file. Scenes draw in the order they are listed, so a later scene covers an earlier one it overlaps.
|
|
44
|
+
- Colours: `#rgb`, `#rrggbb`, `#rrggbbaa`, `rgb(...)`, `rgba(...)` or `transparent`.
|
|
45
|
+
|
|
46
|
+
## Drawing
|
|
47
|
+
|
|
48
|
+
- `path` draws any shape from SVG path data, in pixels relative to its `x`, `y`: `{ "id": "tri", "type": "path", "x": 100, "y": 80, "d": "M0 0 L200 0 L100 150 Z", "fill": "#f59e0b" }`. Lines, arrows, curves (`C`, `Q`, `A`) and polygons are all paths. Leave out `fill` for a line. Give it `width`/`height` if you want `originX: "center"` to work.
|
|
49
|
+
- Outlines on `rect`, `circle`, `path` and `text`: `stroke` (colour or gradient), `strokeWidth` (default 1), `strokeDash` such as `[12, 6]`, `lineCap` (`butt`, `round`, `square`), `lineJoin` (`miter`, `round`, `bevel`; text defaults to `round`). An outline is drawn only when `stroke` is set. For an outline-only rect or circle, set `"fill": "transparent"`. Text outlines sit behind the fill, which suits captions: `"color": "#facc15", "stroke": "#111827", "strokeWidth": 10`.
|
|
50
|
+
- Line drawing: animate a path's `trimEnd` from 0 to 1 to draw its outline on; `trimStart` hides the start. Trims replace any `strokeDash`.
|
|
51
|
+
- Gradients work anywhere a `fill`, text `color` or `stroke` goes: `{ "type": "linear", "from": [0, 0], "to": [400, 0], "stops": [{ "offset": 0, "color": "#22d3ee" }, { "offset": 1, "color": "#e879f9" }] }` or `{ "type": "radial", "center": [100, 100], "radius": 100, "stops": [...] }`. Points are in the layer's own pixels.
|
|
52
|
+
- Effects on any visual layer:
|
|
53
|
+
- `shadow: { "color": "rgba(0,0,0,0.5)", "blur": 16, "offsetX": 0, "offsetY": 8 }`. Shadow offsets ignore the layer's scale and rotation.
|
|
54
|
+
- `blur` in pixels (animatable).
|
|
55
|
+
- `filter` with CSS filter functions, e.g. `"grayscale(1) contrast(1.2)"`.
|
|
56
|
+
- `blendMode`: `multiply`, `screen`, `overlay`, `darken`, `lighten`, `color-dodge`, `color-burn`, `hard-light`, `soft-light`, `difference`, `exclusion`, `hue`, `saturation`, `color` or `luminosity`.
|
|
57
|
+
- On a group, these apply to each child separately, not to the group as one flattened picture.
|
|
58
|
+
- Rounded pictures: `radius` on `image` and `video` clips their corners. A shadow on a rounded image is clipped too, so for a card put a rounded `rect` with the shadow behind it.
|
|
59
|
+
- Shape morphing: animate a path's `d` to turn one shape into another: `{ "property": "d", "keyframes": [{ "frame": 0, "value": "M0 -100 A100 100 0 1 1 -0.1 -100 Z" }, { "frame": 20, "value": "M-100 -100 L100 -100 L100 100 L-100 100 Z", "easing": "move" }] }`. Works best between single closed outlines of similar size (circle, square, star, blob, icon silhouette), centred on the same point; shapes with holes or several pieces morph with stray lines. Use it to carry an object across a beat: the circle that was the logo becomes the card the next scene is about.
|
|
60
|
+
- Masks: a group's `clipPath` (SVG path data in the group's pixels) shows its children only inside that shape, e.g. a circle: `"M100 0 A100 100 0 1 1 99.9 0 Z"`.
|
|
61
|
+
|
|
62
|
+
## Animation
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{ "property": "opacity", "keyframes": [{ "frame": 0, "value": 0 }, { "frame": 20, "value": 1, "easing": "land" }] }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- Animatable properties: `x`, `y`, `opacity`, `rotation`, `scale`, `fontSize`, `width`, `height`, `radius`, `letterSpacing`, `strokeWidth`, `trimStart`, `trimEnd`, `blur`, and the colours `fill` (rect, circle, path), `color` (text) and `stroke`, whose keyframe values are plain colours (an animated colour replaces a gradient): `{ "property": "fill", "keyframes": [{ "frame": 0, "value": "#ef4444" }, { "frame": 30, "value": "#22c55e" }] }`. An animated value replaces the static field.
|
|
69
|
+
- Before the first keyframe the value holds the first keyframe's value; after the last it holds the last.
|
|
70
|
+
- `easing` on a keyframe controls the segment that arrives at that keyframe. Choose by the job the motion does:
|
|
71
|
+
- `land`: entrances. Most of the travel happens early, then a long soft settle, so the element arrives with weight. The default choice for text, cards and panels coming in (14–24 frames).
|
|
72
|
+
- `snap`: almost all of the move in the first few frames, then a tail. For the one hero element or a call to action (10–20 frames).
|
|
73
|
+
- `glide`: a gentler ease-out for supporting elements and fades.
|
|
74
|
+
- `pop`: lands with a small overshoot. Badges and icons only, sparingly.
|
|
75
|
+
- `move`: accelerates and decelerates, for something repositioning within the frame.
|
|
76
|
+
- `exit`: accelerates away. Exits are faster than entrances (8–14 frames).
|
|
77
|
+
- `drift`: close to linear, soft at both ends, for slow continuous motion over seconds (background glows, a gentle zoom on a held title).
|
|
78
|
+
- Standard curves: `linear` (the default; fine for opacity), `easeIn`, `easeOut`, `easeInOut`, `easeInCubic`, `easeOutCubic`, `easeInOutCubic`, `easeInQuart`, `easeOutQuart`, `easeInOutQuart`, `easeInExpo`, `easeOutExpo`, `easeInOutExpo`, `easeOut`, `easeInOut`, `easeInCubic`, `easeOutCubic`, `easeInOutCubic`, `easeInBack` (pulls back first), `easeOutBack` (overshoots, then settles), `spring` (overshoots about 10% and settles), `hold` (stays at the previous value, then jumps), or CSS-style Bézier control points like `[0.4, 0, 0.2, 1]`.
|
|
79
|
+
- Kinetic text: `split` on a text layer animates it piece by piece, each word, letter or line playing the same entrance a few frames after the previous:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{ "id": "headline", "type": "text", "content": "Build the future", "fontSize": 160, "split": { "by": "words", "stagger": 4, "preset": "rise" } }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- `by`: `words` (default), `letters` or `lines`. Rich text (`spans`) splits by words or lines.
|
|
86
|
+
- `stagger`: frames between one piece and the next (default 3; fractions work, e.g. 1.5 for letters).
|
|
87
|
+
- `preset` (default `rise`): `rise` (up into place), `fade`, `pop` (grows in with a small overshoot), `blur` (comes into focus), `slide` (in from the right), `drop` (down into place), `typewriter` (each piece appears at once; use with letters and `stagger` 1–2). `duration` (default 18) is how long each piece takes.
|
|
88
|
+
- `order`: `forward` (default), `reverse`, `center` (from the middle out), `edges` (from both ends in), `random` (shuffled, the same every render).
|
|
89
|
+
- `animations` replaces the preset with your own keyframes for every piece, in `x`, `y` (offsets from where the piece sits), `opacity`, `scale`, `rotation` (around the piece's centre), `blur` and `color`; frames count from the layer's start.
|
|
90
|
+
- The layer's own animations still apply to the whole block, so a group fade or a slow `drift` zoom works on top. Gradients stay continuous across the pieces.
|
|
91
|
+
- Use it for headlines and short statements: words for most headlines, letters for one or two short words, lines for paragraphs. Keep body text and captions whole.
|
|
92
|
+
- `repeat: n` plays the keyframes n more times after the first; add `reverse: true` to play every other cycle backwards (ping-pong).
|
|
93
|
+
|
|
94
|
+
## HTML layers
|
|
95
|
+
|
|
96
|
+
When a layout is easier to say in HTML and CSS than with positioned layers (a card with an icon beside wrapping text, a table, a chart drawn as inline SVG, a UI mock-up, mixed typography), use an `html` layer:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{ "id": "card", "type": "html", "x": 190, "y": 160, "width": 900, "height": 400,
|
|
100
|
+
"html": "<div class='card'><img src='logo'><div><h2>Real <em>HTML</em></h2><p>Flexbox, grid and rich text.</p></div><div class='bar'><i></i></div></div>",
|
|
101
|
+
"css": ".card { display: grid; grid-template-columns: 120px 1fr; gap: 28px; font-family: Inter; color: #f5f7fb } .bar i { display: block; height: 14px; width: calc(var(--progress) * 100%); background: var(--accent) }",
|
|
102
|
+
"vars": { "--accent": "#22d3ee" },
|
|
103
|
+
"animations": [{ "property": "--progress", "keyframes": [{ "frame": 0, "value": 0 }, { "frame": 45, "value": 0.72, "easing": "land" }] }] }
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- The markup is laid out in a `width` × `height` box (overflow hidden) at the layer's `x`, `y`; the layer moves, fades, scales and rotates like any other, and works under the camera, in groups and with transitions.
|
|
107
|
+
- No scripts run: `<script>`, `on…` handlers and `iframe`/`object`/`link` are removed. Nothing loads from the web: refer to pictures by asset id or path (`src="logo"`, `url(./bg.png)`); web URLs are an error. Fonts are Inter and the composition's font assets (`font-family: Inter`, or the asset's family).
|
|
108
|
+
- Motion inside the markup comes from CSS variables. `vars` sets them; animate one with keyframes on `"--name"` (numbers or colours) and use it in the CSS (`width: calc(var(--progress) * 100%)`, `opacity: var(--fade)`). Every frame also gets `--t` (seconds since the layer started) and `--frame`.
|
|
109
|
+
- CSS `@keyframes` work when placed in time with `--t`: `animation: rise 0.6s ease-out both; animation-delay: calc(var(--t) * -1s + 0.3s)` plays `rise` starting 0.3 s into the layer. Animations otherwise stay paused, and transitions are off, so every frame is exact.
|
|
110
|
+
- Each frame where the markup, CSS or variables differ is rasterised again (about 40 ms), so animate the layer itself (x, y, opacity, scale) where you can and keep CSS-variable animation for what changes inside it. `resolution: 2` keeps it sharp when scaled up or under a camera zoom.
|
|
111
|
+
- Prefer ordinary layers for headlines and captions: `ove review` reads their text, but cannot see text inside an html layer.
|
|
112
|
+
|
|
113
|
+
## Lottie, shaders and layout
|
|
114
|
+
|
|
115
|
+
**Lottie animations** (exported from After Effects with Bodymovin, or from LottieFiles) play frame-exactly:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{ "id": "check", "type": "lottie", "src": "check-anim", "x": 860, "y": 400, "width": 200, "height": 200, "startFrame": 30 }
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- `src` is a `"type": "lottie"` asset or a `.json` path. `fit` is `contain` (default), `cover` or `fill`; `trimStart`, `playbackRate` and `loop` work as on video layers, and without `loop` it holds its last frame.
|
|
122
|
+
- After Effects expressions are not supported (they are JavaScript), and pictures must be embedded in the JSON. Use Lottie for icons, illustrations and character moments rather than redrawing them as paths.
|
|
123
|
+
|
|
124
|
+
**Shaders** fill a box with a GLSL fragment shader, for animated gradients, noise, light and textures:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{ "id": "sky", "type": "shader", "width": 1920, "height": 1080, "resolution": 0.5, "vars": { "--tint": "#8b5cf6" },
|
|
128
|
+
"fragment": "void main(){ vec2 uv = gl_FragCoord.xy / resolution; gl_FragColor = vec4(tint.rgb * (0.5 + 0.5 * sin(uv.x * 8.0 + time)), 1.0); }" }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- Provided uniforms: `time` (seconds into the layer), `resolution` (pixels), `frame`, and Shadertoy's `iTime`, `iResolution`, `iFrame`; Shadertoy code written as `mainImage(out vec4 fragColor, in vec2 fragCoord)` works as is. Each var becomes a uniform without its dashes (`--tint` → `tint`): numbers as `float`, colours as `vec4`; animate them with keyframes on `"--tint"`.
|
|
132
|
+
- `resolution: 0.5` renders at half size and scales up, four times faster; fine for soft backgrounds. Renders use a software GPU so pixels match on every machine; a shader that is slow there (many loop iterations per pixel) makes renders slow.
|
|
133
|
+
- A shader that does not compile stops the render with the compiler's message and the layer's id.
|
|
134
|
+
|
|
135
|
+
**Layout**: give a group `layout` and its children are placed one after another, measured as they draw (text included), so cards, lists and rows reflow when text changes:
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{ "id": "row", "type": "group", "x": 120, "y": 120, "width": 1680, "layout": { "direction": "row", "gap": 24, "align": "center", "justify": "space-between" },
|
|
139
|
+
"children": [ { "id": "title", "type": "text", "content": "{{title}}", "fontSize": 64 }, { "id": "badge", "type": "html", "width": 220, "height": 64, "html": "…" } ] }
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
- `direction` `row` or `column`; `gap` between children; `padding` inside the group; `align` across the direction (`start`, `center`, `end`); `justify` along it (`start`, `center`, `end`, `space-between`) within the group's `width` (row) or `height` (column).
|
|
143
|
+
- Children are measured by their width and height (circles by diameter, text by its lines, groups with a layout by their content); give paths and captions a width and height. A child's own `x` and `y` offset it from its place, so entrance animations still work.
|
|
144
|
+
|
|
145
|
+
## Camera and motion blur
|
|
146
|
+
|
|
147
|
+
A scene's `camera` moves the view of the whole scene, like a camera over a set, instead of every layer moving on its own:
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{ "id": "loop", "durationFrames": 300,
|
|
151
|
+
"camera": { "animations": [
|
|
152
|
+
{ "property": "zoom", "keyframes": [{ "frame": 0, "value": 1 }, { "frame": 40, "value": 1.6, "easing": "land" }] },
|
|
153
|
+
{ "property": "x", "keyframes": [{ "frame": 0, "value": 960 }, { "frame": 40, "value": 1300, "easing": "land" }] } ] },
|
|
154
|
+
"layers": [ … , { "use": "cc", "id": "cc", "fixed": true, "with": { "src": "cc-loop" } } ] }
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
- `x`, `y`: the point of the scene shown at the centre of the frame (default: the frame's centre, so a camera that does not move changes nothing). `zoom`: 2 shows half as much at twice the size. `rotation` in degrees. `shake`: a wobble of that many pixels, the same in every render; animate it up to 6–12 for an impact and back to 0 over half a second.
|
|
158
|
+
- All five animate like layer properties. To frame something, point the camera at its centre and zoom so it fills about two thirds of the frame.
|
|
159
|
+
- `"fixed": true` on a top-level scene layer keeps it in place while the camera moves: captions, logos, progress bars, anything that is part of the screen rather than the scene. Place captions with `fixed` whenever the scene has a camera.
|
|
160
|
+
- Good uses: a push-in that lands on what the narration talks about (`land`, 20–40 frames), a slow `drift` zoom of 1 → 1.05 over a held scene so it never freezes, a pull-back that reveals the whole diagram, a small shake when something hits. Avoid constant wandering; the camera should move with a reason, then rest.
|
|
161
|
+
- Things outside the frame become visible when the camera zooms out or pans: build the scene larger than the frame where it needs to be.
|
|
162
|
+
|
|
163
|
+
`"motionBlur": { "samples": 8, "shutter": 180 }` at the top level blurs fast motion (camera moves, things flying in) the way a film camera does: each frame averages that many moments across the shutter (180° = half a frame). Things that do not move stay exactly as sharp. It makes renders slower (about 2.5× with 8–10 samples), so render drafts with `--no-motion-blur`; the live preview and `ove review` show frames without it. Worth it for videos with fast moves; pointless for slow, calm ones.
|
|
164
|
+
|
|
165
|
+
## Matte and colour grade
|
|
166
|
+
|
|
167
|
+
Two properties on any layer (and `grade` on a scene):
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{ "id": "speaker", "type": "video", "src": "clip.mp4", "matte": { "src": "speaker-matte.mp4", "mode": "luma" } }
|
|
171
|
+
{ "id": "look", "type": "video", "src": "clip.mp4", "grade": { "look": "cinematic", "contrast": 0.1, "temperature": -0.1 } }
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
- `matte` shows the layer only where the matte says so. The matte is a video or picture you make with any tool (rembg, Robust Video Matting, an editor): `luma` keeps the layer where the matte is white, `alpha` where it is opaque, `invert` keeps the opposite. A video layer's matte follows its own `trimStart` and `playbackRate`. It becomes a group named `<id>-masked` when the file loads, so give layers unique ids; put blur, scale or a blend mode on a group around the masked layer, not on the layer, to affect the cut-out. `ove track matte.mp4` writes where the speaker is in each frame (for labels that follow).
|
|
175
|
+
- `grade` is exposure (stops), contrast, saturation, temperature, tint, shadows, highlights and fade, each -1 to 1 (exposure -3 to 3), or a `look`: warm, cool, cinematic, teal-orange, noir, vivid, faded. Numbers add to the look. It is static; cross-fade two graded layers to change a grade over time. On a scene, it grades every layer in it except `fixed` ones.
|
|
176
|
+
|
|
177
|
+
## Transitions
|
|
178
|
+
|
|
179
|
+
```json
|
|
180
|
+
{ "id": "two", "startFrame": 75, "durationFrames": 90,
|
|
181
|
+
"transitionIn": { "type": "slide", "direction": "right", "durationFrames": 15, "easing": "easeOutCubic" } }
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
- Scenes take `transitionIn` (over their first `durationFrames`) and `transitionOut` (over their last). Types: `fade`; `slide` (the whole scene moves in from, or out towards, `direction`: `left`, `right`, `top`, `bottom`); `wipe` (revealed or hidden by an edge moving from `direction`); `zoom` (grows from 85% while fading in, shrinks while fading out); `iris` (a circle opening from `x`, `y`, default the centre: put it on the hero so the next scene opens out of it); `push` (comes in from `direction` and pushes the scene underneath out the same way at the same speed, so motion carries across the cut; `transitionIn` only); `dip` (through a solid `color`, default black, which covers everything at the midpoint: use white for a flash, black for a pause between chapters); `blur` (comes into focus while fading in, or goes out of focus). `easing` defaults to `easeInOut`; `move` suits push, `land` suits iris and blur.
|
|
185
|
+
- Every cut should have a reason: `push` when the story moves on in the same direction, `iris` when the next scene is inside or about the hero, `dip` for a chapter break, a plain `fade` otherwise. Keep one or two styles through a video.
|
|
186
|
+
- To go from one scene to the next, overlap them: start the next scene `durationFrames` frames before the previous one ends. A later scene draws on top, so `slide`/`wipe`/`zoom` in on the new scene covers the old one; for a crossfade give the old scene `transitionOut` fade and the new one `transitionIn` fade with the same length.
|
|
187
|
+
- Without an overlap, a transition shows the canvas background behind the scene.
|
|
188
|
+
- Scene length counts the overlap: both scenes must still fit in the composition's `durationFrames`.
|
|
189
|
+
- Let OVE place scenes: leave out a scene's `startFrame` and it starts where the previous scene ends, minus its `transitionIn` length (so the transition plays over the previous scene). Set `"overlap": 0` to start right after instead, as when the previous scene fades out first. `"durationFrames": "auto"` on the composition ends the video with its last scene.
|
|
190
|
+
|
|
191
|
+
## Timing from media (narration-driven videos)
|
|
192
|
+
|
|
193
|
+
Every audio and video asset is measured when a composition mentions `media`, so scenes and cues can follow the voiceover instead of hand-typed frame numbers:
|
|
194
|
+
|
|
195
|
+
- `media['vo-intro'].frames` / `.duration`: the clip's length in frames (at the composition fps) / seconds.
|
|
196
|
+
- `media['vo-intro'].soundEndFrames` / `.soundEnd`: where the last sound ends, ignoring trailing silence.
|
|
197
|
+
- `media['vo-loop'].segments[i]`: the stretches of sound between pauses (`.startFrame`, `.endFrame`, `.start`, `.end`). In speech each segment is a phrase, so `segments[2].startFrame` is when the third phrase starts.
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{ "id": "loop", "durationFrames": "{{18 + media['vo-loop'].soundEndFrames + 38}}",
|
|
201
|
+
"layers": [ { "id": "voice", "type": "audio", "src": "vo-loop", "startFrame": 18 },
|
|
202
|
+
{ "repeat": "{{steps}}", "each": [ { "id": "step-{{index}}", "type": "text", "startFrame": "{{18 + media['vo-loop'].segments[index + 1].startFrame}}", "content": "{{item.name}}" } ] } ] }
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
- Times inside a scene are scene frames, so add the voice layer's own `startFrame` (18 above) to clip positions.
|
|
206
|
+
- Run `ove inspect composition.json` to see the measured `media` (lengths and segments) and the scene timings it produces; use it to pick segment indices.
|
|
207
|
+
|
|
208
|
+
## Captions
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{ "id": "cc", "type": "captions", "src": "cc-intro", "startFrame": 18, "x": 960, "y": 960 }
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
- `src` is a `captions` asset (or a path): an `.srt` or `.vtt` file, or JSON with timed lines (`{ "cues": [{ "text", "start", "end" }] }`) or timed words (`{ "words": [{ "text", "start", "end" }] }`, seconds). Give the layer the same `startFrame` as its voice layer.
|
|
215
|
+
- With word timings the word being said is highlighted (`highlightColor`), words already said use `color`, and words still to come use `upcomingColor`; `mode: "lines"` shows plain lines. Words are grouped into lines at sentence ends, commas and pauses, up to `maxWords`.
|
|
216
|
+
- The line is centred on `x` (with `textAlign: "center"`) with its top at `y`, wrapped to `maxWidth`, on a `background` box with `padding` and `radius`. Style with `fontSize`, `fontFamily`, `fontWeight` and the outline options. `offset` shifts the timing in seconds.
|
|
217
|
+
- Filler words (um, uh, erm, ah, hmm…) and words said twice in a row are left out of captions by default (`dropFillers: false` keeps them), in the video and in exported subtitles.
|
|
218
|
+
- `emphasis: ["agent", "goal"]` draws those words (any case, punctuation ignored) in `emphasisColor` (default `highlightColor`) once they are said: two or three key words per video, not one per line.
|
|
219
|
+
- `ove captions composition.json --output captions.srt` (or `.vtt`) writes every captions layer as a subtitle file for uploads.
|
|
220
|
+
|
|
221
|
+
## Media
|
|
222
|
+
|
|
223
|
+
- Declare files in `assets` with an `id`, then reference that id from an image or video layer's `src`. A `src` may also be a path relative to `composition.json` or an `https://` URL.
|
|
224
|
+
- Images: PNG, JPEG, WebP, GIF and SVG. `objectFit` is `cover` (fill and crop, the default), `contain` (letterbox) or `fill` (stretch). SVG files need `width` and `height` attributes.
|
|
225
|
+
- Video: a `video` layer takes `src`, `width`, `height` and `objectFit` like an image. `trimStart` skips that many frames of the source (counted at the composition fps), `playbackRate` speeds it up (2) or slows it down (0.5), and past the end of the source it holds the last frame unless `loop` is true. The layer's own `startFrame` is when the clip starts playing. Any format FFmpeg can read works (MP4, MOV, WebM, …); the first render of a clip decodes it into a cache, later renders and stills reuse it. A video's own sound plays while the layer is on screen; set `volume` (1 = unchanged) or `muted: true`.
|
|
226
|
+
- Sound effects, voice-over and music go in `audio` layers: `{ "id": "vo", "type": "audio", "src": "voice", "startFrame": 30 }`. They have no position and are never drawn. Timing works like any layer: the sound plays from the layer's `startFrame` until its `endFrame`, its scene or group ends, or the source runs out (then silence, or `loop: true` to repeat). Options: `volume` (0–4, 1 = unchanged), `trimStart` (source frames to skip), `playbackRate` (changes speed, keeps pitch), `fadeInFrames`, `fadeOutFrames`. Overlapping sounds are added together, so lower `volume` on music under voice-over.
|
|
227
|
+
- Loudness: `"export": { "loudness": -14 }` (or `--loudness -14`) makes the finished sound measure that loudness in LUFS: -14 suits most streaming and social sites, -16 spoken explainers and podcasts. OVE mixes the sound once, measures it, and turns the whole mix up or down by one fixed amount, with a limiter keeping peaks under about -1.5 dBFS (so a very loud target can fall a little short rather than distort). Set it on every video with sound; `ove review` reports the loudness it will have.
|
|
228
|
+
- `export.audio` (an audio asset id or relative path) is a soundtrack that loops under the whole video, at `export.audioVolume` (default 1). Use an `audio` layer instead when it should start later, stop, or fade.
|
|
229
|
+
- Built-in sound effects: an audio layer with `"src": "sfx:whoosh"` (also `click`, `tick`, `pop`, `thump`, `ding`, `riser`) plays a synthesised sound, the same in every render, no asset needed. Put them on the frame something happens: `whoosh` on a fast move or push, `thump` when something big lands, `pop` or `tick` as cards and icons appear, `click` on a check mark or button, `ding` on a success, `riser` (1.6 s) ending just before a reveal. Keep them under the voice (`volume` 0.4–0.8) and use them where they mean something, not on every element.
|
|
230
|
+
- Changing volume: `volume` is animatable on audio and video layers (`{ "property": "volume", "keyframes": [...] }`).
|
|
231
|
+
- Ducking: `"duck": { "amount": 0.35, "attackFrames": 6, "releaseFrames": 18 }` on music (an audio layer, or `export.duck` for the soundtrack) lowers it to `amount` of its volume while any other sound plays, and brings it back in the pauses. `by: ["voice-id"]` limits it to certain layers. When the other sounds are assets OVE can measure, ducking follows their phrases rather than their whole time on screen. With ducking the music can sit louder overall (`audioVolume` 1.5 or so) without fighting the voice.
|
|
232
|
+
- Output: `ove render` picks the format from the `--output` extension: `.mp4` (H.264, or H.265 with `--codec h265`), `.webm` (VP9 + Opus), `.mov` (ProRes, for editing software), `.gif` (silent, loops) or a path without extension for a folder of PNG frames. `--format` overrides it, and so does `export.format` in the composition when the output has no recognised extension. `--frames 30-89` renders part of the video (audio is cut to match).
|
|
233
|
+
- Quality: `--crf 18` for higher quality (lower is better and bigger; defaults 20 H.264, 24 H.265, 32 VP9) or `--bitrate 8M` for a set data rate; `--audio-bitrate 256k`. `--scale 0.5` renders a quick half-size draft. The same settings exist as `export.crf`, `export.bitrate`, `export.audioBitrate` and `export.scale`.
|
|
234
|
+
- Transparency: `"export": { "transparent": true }` or `--transparent` leaves the background see-through, for overlays and lower thirds; only webm, mov and png keep it. Otherwise the canvas is filled with the composition's `background`: a colour (default `#101827`) or a gradient, in composition pixels.
|
|
235
|
+
- Fonts: the default `fontFamily` is Inter, which is built in: every weight from 100 to 900, italics, Latin, Greek, Cyrillic and Vietnamese, the same on every machine, with no asset needed. Large sizes automatically use its tighter display shapes. Other scripts (Arabic, CJK, Devanagari…) and emoji fall back to system fonts. `fontFamily` can also name an installed font, a list like `"Inter, Arial, sans-serif"`, or a font file declared as an asset: `{ "id": "brand-bold", "type": "font", "src": "./fonts/Brand-Bold.woff2", "family": "Brand", "weight": 700 }`. Declare one asset per file (weight and style), all with the same `family`, and text picks the closest one by `fontWeight`/`fontStyle`; without an italic file, italics are slanted artificially. WOFF2, WOFF, TTF and OTF work. Check a still: a misspelt family silently falls back to the default font.
|
|
236
|
+
|
|
237
|
+
## Variables (templates)
|
|
238
|
+
|
|
239
|
+
Declare inputs in a top-level `variables` object and use them as `{{name}}` in any string:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
"variables": {
|
|
243
|
+
"customer": { "type": "string", "default": "there", "description": "First name" },
|
|
244
|
+
"accent": { "type": "color", "default": "#4f46e5" },
|
|
245
|
+
"left": { "type": "number", "default": 160 }
|
|
246
|
+
},
|
|
247
|
+
...
|
|
248
|
+
{ "id": "hello", "type": "text", "content": "Welcome, {{customer}}!", "x": "{{left}}" },
|
|
249
|
+
{ "id": "glow", "type": "circle", "fill": "{{accent}}", "width": 600 }
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
- Types: `string`, `number`, `boolean`, `color`, `list`. A variable without a `default` must be given a value.
|
|
253
|
+
- A list holds objects. Declare their fields with `items` so values are checked and get defaults: `"products": { "type": "list", "items": { "name": { "type": "string" }, "price": { "type": "number" }, "discount": { "type": "number", "default": 0 } }, "default": [ ... ] }`. On the command line pass a list as JSON (`--var 'products=[{"name":"Mug","price":12}]'`), or put it in a `--vars` file.
|
|
254
|
+
- `{{ }}` holds an expression, not just a name: `{{160 + index * 320}}`, `{{item.name}}`, `{{items[0].price}}`, `{{products.length}}`, `{{discount > 0 ? "#dc2626" : "#111827"}}`, `{{round(price * (1 - discount / 100), 2)}}`. Operators: `+ - * / %`, `== != < <= > >=`, `&& || !`, `? :`. Functions: `round(x, digits)`, `floor`, `ceil`, `abs`, `min`, `max`, `upper`, `lower`, `string`, `number`. `+` joins text. There are no list literals: lists come from variables or plain JSON arrays.
|
|
255
|
+
- Beat grid: with a top-level `"tempo": { "bpm": 120, "offset": 0.2, "beatsPerBar": 4 }` (offset: seconds to the first beat), `beat(n)` is the frame of beat n (counting from 0), `bar(n)` of the first beat of bar n (`bar(n, b)` beat b in it), and `beats(n)` the length of n beats in frames. Use them so cuts and hits land on the music: `"startFrame": "{{bar(4)}}"`, `"durationFrames": "{{beats(8)}}"`, keyframes at `{{beat(3)}}`. Find the tempo and first beat by listening, or from the track's metadata.
|
|
256
|
+
- A string that is exactly one `{{ }}`, like `"{{left}}"` or `"{{index * 8}}"`, becomes the typed value, so it works in number and boolean fields. Anywhere else the value is inserted as text.
|
|
257
|
+
|
|
258
|
+
### Repeat, if and components
|
|
259
|
+
|
|
260
|
+
These work in `layers`, group `children`, `scenes` and repeat bodies:
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{ "repeat": "{{products}}", "as": "p", "each": [
|
|
264
|
+
{ "id": "card-{{index}}", "type": "rect", "x": "{{40 + index * 300}}", "startFrame": "{{index * 8}}", "width": 270, "height": 360 },
|
|
265
|
+
{ "if": "{{p.discount > 0}}", "id": "badge-{{index}}", "type": "text", "x": "{{60 + index * 300}}", "content": "-{{p.discount}}%" }
|
|
266
|
+
] }
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
- `repeat` takes a list (a `{{ }}` or a JSON array) and outputs the `each` entries once per item. Inside, `as` names the item (default `item`), `index` counts from 0, and `count` is the list length. Use them for positions (`{{x0 + index * gap}}`), staggered timing (`"startFrame": "{{index * 8}}"`), centring (`{{(width - count * cardWidth - (count - 1) * gap) / 2 + index * (cardWidth + gap)}}`) and unique ids (`"card-{{index}}"`; ids are not made unique for you). Nested repeats need different `as` names; the inner `index` hides the outer one.
|
|
270
|
+
- `"if"` on any layer, scene or repeat keeps it only when true. Empty text, empty lists, `0`, `false` and `null` are false.
|
|
271
|
+
- Components are reusable layer sets, declared once at the top level:
|
|
272
|
+
|
|
273
|
+
```json
|
|
274
|
+
"components": { "badge": { "params": { "label": { "type": "string" }, "tone": { "type": "color", "default": "#16a34a" } },
|
|
275
|
+
"layers": [ { "id": "bg", "type": "rect", "width": 120, "height": 40, "radius": 20, "fill": "{{tone}}" },
|
|
276
|
+
{ "id": "text", "type": "text", "x": 16, "y": 8, "content": "{{label}}" } ] } }
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Place one with `{ "use": "badge", "id": "new-badge", "x": 50, "y": 60, "startFrame": 5, "with": { "label": "New" } }`. It becomes a group, so every group field (`x`, `y`, `startFrame`, `animations`, `opacity`, `scale`…) works on the placement. Don't give it a `type`.
|
|
280
|
+
- Inside a component's layers, only its `params` and the composition's own variables are visible. Pass loop values in through `with`, e.g. `"with": { "label": "{{p.name}}" }`. Components can use other components, but not themselves.
|
|
281
|
+
- Any string can use them: text, colours, image/video/audio `src`, the composition `name`.
|
|
282
|
+
- Give values with `--var customer=Priya` (repeatable; `\n` makes a line break) or `--vars values.json`. They work with `validate`, `inspect`, `still` and `render`.
|
|
283
|
+
- One video per row: `ove render composition.json --batch rows.json --output "out/{{customer}}.mp4"`, where `rows.json` is an array of `{ "customer": "Priya", ... }` objects. Every row is checked before anything renders, and two rows may not write the same file.
|
|
284
|
+
- Mistakes are reported all at once, each with its location: unknown names and fields, values of the wrong type, bad expressions, unknown components and missing params.
|
|
285
|
+
- Editors validating against `composition.schema.json` will flag `"{{left}}"` in a number field and `repeat`/`use` entries in layer lists; that is expected. `ove validate` is the real check.
|
|
286
|
+
|
|
287
|
+
## Blocks and themes
|
|
288
|
+
|
|
289
|
+
Ready-made pieces you copy into a video, like a component library. A **block** is a scene (title, big number, quote, bullet list, numbered steps, stat grid, section divider, call to action, phone mockup, image with caption, icon row, split screen) or an overlay (lower third, badge, progress bar, handle). Scenes come with a slow camera push, drifting background shapes, a push transition and soft built-in sound effects, and new compositions get light motion blur; the picture blocks take a screenshot or photo (`--set image=shot.png`, or an asset id). Blocks are sized from the canvas, so each one fits 16:9, 9:16, 1:1 and 4:5, and they read their colours from a **theme** (`midnight`, `paper`, `neon`, `espresso`).
|
|
290
|
+
|
|
291
|
+
```sh
|
|
292
|
+
ove blocks # list blocks and themes
|
|
293
|
+
ove add title-hero reel/composition.json --aspect 9:16 --theme neon --set headline="Fresh roasted, daily"
|
|
294
|
+
ove add stat-grid reel/composition.json # appends a scene
|
|
295
|
+
ove add lower-third reel/composition.json --scene title-hero --at 20 --set name="Sam Lee"
|
|
296
|
+
ove theme espresso reel/composition.json # switch every block's colours at once
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
`ove add` writes plain JSON: a component (named after the block, with underscores) under `components` and a placement whose `with` holds the text, so a block is yours to edit afterwards. Scenes are appended and overlap by their fade; overlays go inside a scene (`--scene`, default the last) from `--at`. Expressions can read `canvas.width`, `canvas.height` and `canvas.fps`, which is how blocks resize themselves. Blocks live in `packages/cli/blocks/*.json` and themes in `packages/cli/themes/*.json`. Themes change colours, corner radius and heading weight; fonts stay Inter unless you add font assets.
|
|
300
|
+
|
|
301
|
+
`ove init --template launch-wide` (also `story-tall`, `explainer-square`, `keynote-wide`) starts from a whole video built from blocks. The AI picks among them and a theme too; see `docs/ai.md`.
|
|
302
|
+
|
|
303
|
+
Before building scenes by hand, check `ove blocks`: a block plus `--set` is faster and consistent. Validate and review as usual afterwards.
|
|
304
|
+
|
|
305
|
+
## Planning a video
|
|
306
|
+
|
|
307
|
+
For anything longer than a few seconds, agree the plan before building, in three cheap steps. Show each to the person and wait for their answer; changing a table costs nothing, changing a rendered video costs minutes.
|
|
308
|
+
|
|
309
|
+
1. **Beats.** A numbered table: for each beat, what the viewer should take away (one idea per beat), the words on screen, the narration if any, and roughly how long. Name the one idea of the whole video in a sentence above the table. Check facts you state.
|
|
310
|
+
2. **Look.** Two or three stills of key beats rendered with `ove still`: type, colours, layout, background. Agree the look on stills, not on a render.
|
|
311
|
+
3. **Storyboard.** `ove review`'s contact sheet of a first rough build (or one still per beat), with how each beat enters and leaves. Then build the motion and render.
|
|
312
|
+
|
|
313
|
+
When the person has already given exact content and a look, skip what is settled; for a quick change to an existing video, just do it and review.
|
|
314
|
+
|
|
315
|
+
## Story and pacing
|
|
316
|
+
|
|
317
|
+
- **Shapes that work for short videos:** a problem, then the thing that solves it, then proof, then what to do (product); a process shown step by step around one hero that stays put while the world changes (explainers); a question, then answers that build to the main one (teasers); a list of three to five items, each its own beat, then a payoff (features). Pick one before writing beats.
|
|
318
|
+
- **Open with the subject in the first second:** a short still or quiet moment is fine, a slow logo is not. The first frame someone sees in a feed should already say what this is.
|
|
319
|
+
- **One idea per beat, and beats of different lengths.** A beat that builds can hold three to six seconds; a turn (the moment the idea changes) should land in a few frames after a short pause. Equal-length beats read as a slideshow; `ove review` flags them.
|
|
320
|
+
- **Stillness before the payoff.** Let the frame and the sound settle for half a second to a second before the most important reveal, then hit it: the loudest sound and the boldest motion belong to the turn, not to the busiest moment.
|
|
321
|
+
- **Every cut needs a reason** you could say out loud: the process moves on (push in the same direction), we go inside the hero (iris on it, or a camera push), time passes (a dip), the same thing changes (the hero holds its place and the world around it changes, or a shape morphs into the next one). A cut with no reason is a plain fade.
|
|
322
|
+
- **Carry something across cuts.** An element that stays in the same place, a colour, a shape that morphs, or motion that continues in the same direction ties beats together; scenes that only replace each other read as slides.
|
|
323
|
+
- **Exits are quicker than entrances**, and scenes can leave while still moving rather than settling first.
|
|
324
|
+
- **Music and narration set the rhythm.** Time beats to narration phrases (`media[...]`) or the music's beat (`tempo`), not to round numbers of seconds.
|
|
325
|
+
|
|
326
|
+
## Endings
|
|
327
|
+
|
|
328
|
+
- End on the idea, not on a credit: the last statement, then the name or logo if there is one, then stop.
|
|
329
|
+
- A logo or wordmark: about a quarter to a third of the frame wide, centred (or very slightly off-centre), one short line under it at most, lots of empty space around it. Build it with a small, quick motion (snap in, or assemble from the shape that came before), hold it two to three seconds, end.
|
|
330
|
+
- Prefer ending on a cut or on a clean hold over a long fade of the whole frame; if it fades, fade the last words, then hold black for a moment before the logo, and do not fade the logo itself.
|
|
331
|
+
- A call to action is one short line, said and shown once.
|
|
332
|
+
|
|
333
|
+
## Design
|
|
334
|
+
|
|
335
|
+
Aim for what a motion designer would hand over, not a slide. Before rendering, look at stills of every scene and check them against this list.
|
|
336
|
+
|
|
337
|
+
- **One type scale, used everywhere.** At 1920×1080: display titles 160–220, scene headings 72–96, card titles 44–60, body 28–36, labels 20–24. Never use body text under 26 px, or labels under 20 px. Pick sizes from the scale instead of inventing new ones per layer.
|
|
338
|
+
- **Weights and tracking.** Headings 700–800 with negative `letterSpacing` of about −3% of the size (−2.5 at 84 px, −8 at 210 px); body 400 at 0; small uppercase labels 600 with +4 to +6 tracking. Set `lineHeight` 1.0–1.1 for big headings, 1.4–1.5 for body.
|
|
339
|
+
- **Two or three text colours.** A near-white for headings (`#f5f7fb`), a muted grey-blue for body and labels (`#97a0b6` on dark), and one accent (two at most). Declare them as `color` variables (`ink`, `muted`, `accent`) and reuse them. Avoid pure `#ffffff` body text on pure black.
|
|
340
|
+
- **A grid and margins.** Keep content inside 120 px margins at 1920 wide (100 at 1080 wide), align left edges to the same x, and use consistent gaps (multiples of 8). Left-aligned layouts read better for content scenes; save centred layouts for titles and endings. Keep the same layout pattern from scene to scene (for example a numbered label, heading and subheading in the same place).
|
|
341
|
+
- **Fill the frame.** Content should span the width. Small cards floating in the top third look unfinished; make cards larger, use fewer of them, or add a supporting visual on the empty side.
|
|
342
|
+
- **Leave a band for captions.** Keep content above about y 880 when captions sit at the bottom, and put captions in the same place in every scene.
|
|
343
|
+
- **Depth without clutter.** Backgrounds: a dark gradient `background`, one or two large soft radial glows (a `circle` with a radial fill that fades to transparent), optionally a faint grid (`rgba(255,255,255,0.035)`) and a vignette (a full-frame `rect` with a radial fill from transparent to dark edges). Cards: `rgba(255,255,255,0.04)` fill, a 1–2 px `rgba(255,255,255,0.1)` stroke, radius 24–32, a large soft shadow. Highlight one thing with the accent, not everything.
|
|
344
|
+
- **Icons, not letters.** Draw simple line icons as `path` layers (stroke 4–5, round caps and joins) inside a rounded tile, rather than a letter in a circle.
|
|
345
|
+
- **Kinetic type.** Animate headlines word by word (`split`) rather than as one block, with the `rise` or `blur` preset and a 3–5 frame stagger; keep one split style through a video.
|
|
346
|
+
- **Motion.** Enter with a short rise (20–30 px on `y`) plus a fade, 14–26 frames, `land` (`snap` for the hero element); stagger related items by 4–8 frames. Use `easeOutBack` or `spring` sparingly, for small badges. Animate groups rather than many separate layers. Keep one transition style through a video (a 15-frame `fade` is a safe default); mixing slide, wipe and zoom looks random. Give the background slow drift (glows moving over the whole scene) so still moments don't feel frozen.
|
|
347
|
+
- **Banding and grain.** Dark gradients can band once encoded. The default encoder settings already spend extra bits on dark areas; for a filmic look add `"grain": 0.03` at the top level (moving film grain, the same in every render), and render it with `--crf 16` or a `--bitrate` of 15M or more, since grain makes files much larger and low quality settings smear it into blocks.
|
|
348
|
+
|
|
349
|
+
## Common mistakes
|
|
350
|
+
|
|
351
|
+
- Keyframes written in video frames for a layer with a non-zero `startFrame`.
|
|
352
|
+
- Text running off the canvas or out of its box: set `maxWidth` (and for template text `maxLines` + `minFontSize`), then check a still with the longest value you expect.
|
|
353
|
+
- Rotating or scaling a shape expecting it to pivot on its centre without setting `originX`/`originY` to `"center"`.
|
|
354
|
+
- Butting scenes end to end and expecting a transition between them: they have to overlap by the transition length.
|
|
355
|
+
- Forgetting `width`/`height` on a `rect`, which then draws nothing.
|
|
356
|
+
- Text smaller than 26 px, or more than three text colours on screen.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ove-ai-compose
|
|
3
|
+
description: Generate or change an Open Video Engine video from a brief with the ove ai commands (compose, refine, history, restore, studio), and keep every AI change reversible. Use when the user wants AI to draft a video, rewrite it, or undo an AI change.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AI compose, refine, history
|
|
7
|
+
|
|
8
|
+
AI runs before rendering; the video is still deterministic. Check the provider with `ove ai doctor`. The full guide is `docs/ai.md` in the OVE repository.
|
|
9
|
+
|
|
10
|
+
## Compose
|
|
11
|
+
|
|
12
|
+
`ove ai compose <dir> --brief "..." --kind reel|product|promo|project [--narration] [--asset id=path.png] [--render]`
|
|
13
|
+
|
|
14
|
+
- By default the model picks a ready-made template of the right shape (and a theme) and writes only its text, which is fast and consistent. It writes a whole composition instead when no template fits, or when you pass `--full`, `--duration`, `--fields`, or non-image `--asset` files.
|
|
15
|
+
- `--asset` pictures are offered to templates with picture slots; the model may use only ids you supply.
|
|
16
|
+
- `--review` adds the renderer's design checks as feedback; use it for finished work, not drafts.
|
|
17
|
+
|
|
18
|
+
## Refine
|
|
19
|
+
|
|
20
|
+
`ove ai refine <composition.json> --instruction "make the title bigger and the accent gold"`. Be specific. New files must be passed with `--asset`; refine keeps what the composition already uses.
|
|
21
|
+
|
|
22
|
+
## History: every AI change is kept
|
|
23
|
+
|
|
24
|
+
- `ove ai history <composition.json>` lists the revisions: what produced each, and what changed (scenes, length, values).
|
|
25
|
+
- `ove ai restore <composition.json> <n>` brings revision n back. The restore is itself a revision, so nothing is lost.
|
|
26
|
+
- Revisions are plain files in `history/composition/` beside the composition. The first AI change keeps the file as it was as revision 1.
|
|
27
|
+
|
|
28
|
+
## After any AI change
|
|
29
|
+
|
|
30
|
+
1. `ove validate`, then `ove review <file> --output review.png` and look at the sheet.
|
|
31
|
+
2. Read the copy: names, numbers and claims must match the brief. Fix wrong text by editing the composition or refining.
|
|
32
|
+
3. If the result is worse, `ove ai restore` rather than refining again.
|
|
33
|
+
|
|
34
|
+
## Studio
|
|
35
|
+
|
|
36
|
+
`ove ai studio` opens a local app (127.0.0.1:3100): projects with thumbnails, a create flow, and an editor with live preview, text editing, Ask AI, looks, history and export. Do not expose the port.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ove-batch-videos
|
|
3
|
+
description: Make many Open Video Engine videos from one template and a list of rows (one per product, customer, city or language). Use when the user wants personalised or localised versions of the same video.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# One template, many videos
|
|
7
|
+
|
|
8
|
+
The main `ove` skill has the full "Variables (templates)" section.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
1. Make the template: a composition whose text, colours and pictures use `{{name}}` variables, each declared under `variables` with a type and a default. Every variable must be used, or every row produces the same video.
|
|
13
|
+
2. Check one variant: `ove validate composition.json --var customer=Priya`, then `ove still composition.json --var customer=Priya --frame 60 --output still.png`.
|
|
14
|
+
3. Write `rows.json`, an array of objects with one key per variable, plus a key for the file name.
|
|
15
|
+
4. Render all: `ove render composition.json --batch rows.json --output "out/{{customer}}.mp4"`. Every row is checked before anything renders, and two rows may not write the same file.
|
|
16
|
+
|
|
17
|
+
## Localising
|
|
18
|
+
|
|
19
|
+
Put each language's text in the rows (`"headline": "..."`), not in the composition. Check every language for text that no longer fits: use `maxWidth`, `maxLines` and `minFontSize` on text layers so long translations shrink instead of overflowing, then run `ove review` on the longest row.
|
|
20
|
+
|
|
21
|
+
## With AI
|
|
22
|
+
|
|
23
|
+
`ove ai batch products.json --brief "a clean product card" --output "out/{{id}}.mp4"` has the model write the template from the brief and the row fields, then renders every row.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ove-code-video
|
|
3
|
+
description: Write an Open Video Engine video as TypeScript, JavaScript or React components (loops, functions, springs, generated scenes, JSX), and draw custom visuals with a code layer. Use when the video needs logic JSON cannot express: generated layouts, data-driven scenes, physics, particles, charts or custom canvas drawing.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Video as code
|
|
7
|
+
|
|
8
|
+
Three tools, from safest to most powerful. The main `ove` skill has every layer and field; this page is how to write them from code.
|
|
9
|
+
|
|
10
|
+
## 1. The builder: code that generates the JSON (safe)
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
import { createVideo, text, rect, group, sfx, sample, spring, riseIn, keyframes, tween, fadeIn } from 'open-video-engine/builder';
|
|
14
|
+
|
|
15
|
+
const v = createVideo({ id: 'launch', width: 1920, height: 1080, fps: 30, background: '#0b1020', motionBlur: true });
|
|
16
|
+
v.export({ loudness: -16 });
|
|
17
|
+
|
|
18
|
+
v.scene('intro', { seconds: 4 }, scene => {
|
|
19
|
+
scene.add(text('Write the video.', { x: 160, y: 330, fontSize: 150, fontWeight: 800, color: '#f5f7fb', maxWidth: 1600, split: { by: 'words', stagger: 5, preset: 'rise' } }));
|
|
20
|
+
scene.add(text('Exact, repeatable.', { x: 160, y: 560, fontSize: 48, color: '#9aa6c4', animations: riseIn(560, 30, 20, 24) }));
|
|
21
|
+
scene.add(rect({ x: 160, y: 700, width: 1, height: 10, fill: '#22d3ee', animations: [sample('width', 20, 70, f => spring({ frame: f - 20, from: 1, to: 520 }))] }));
|
|
22
|
+
scene.add(sfx('whoosh', { startFrame: 4 }));
|
|
23
|
+
});
|
|
24
|
+
export default v;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- `ove build video.mjs` (or `.ts` on Node 22.18+) writes `composition.json` beside the file, checked against the schema. Then `ove validate`, `ove review`, `ove render` as usual.
|
|
28
|
+
- It runs only at build time, so the video stays frame-exact. Use ordinary loops and functions to make scenes: `features.map((f, i) => ...)`, `for` loops, data from a file.
|
|
29
|
+
- `sample(property, from, to, fn)` turns any function of the frame into keyframes (springs, noise, sine waves, physics). `spring`, `interpolate`, `tween`, `keyframes`, `fadeIn`, `riseIn(y, distance, frames, delay)` are helpers. Animated values are absolute, so give `riseIn` the layer's own `y`; for a child of a group with a `layout`, use `0`.
|
|
30
|
+
- Layers without an `id` get one (`text-1`); a repeated explicit id is an error. `v.variable`, `v.component`, `v.asset`, `v.export` set the other parts. `use('component', { values }, { x, y })` places a component.
|
|
31
|
+
- Layer constructors: `text rect circle path image clip audio sfx group html lottie shader captions code use`.
|
|
32
|
+
|
|
33
|
+
## 2. React components (your own files only)
|
|
34
|
+
|
|
35
|
+
For Remotion-style videos: components that are pure functions of the frame.
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { AbsoluteFill, Series, interpolate, spring, useCurrentFrame, useVideoConfig } from 'open-video-engine/react';
|
|
39
|
+
export const config = { id: 'promo', width: 1920, height: 1080, fps: 30, durationInFrames: 270, background: '#0b1020', props: { title: 'Hello' } };
|
|
40
|
+
export const layers = [{ id: 'whoosh', type: 'audio', src: 'sfx:whoosh', startFrame: 4 }]; // optional plain OVE layers beside it
|
|
41
|
+
export default function Promo({ title }) { const frame = useCurrentFrame(); /* ...return JSX... */ }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- `ove build promo.tsx` (needs `react`, `react-dom`, `esbuild` installed) writes `composition.json` and `promo.bundle.js`; render with `--allow-code`. Rebuild after every edit.
|
|
45
|
+
- Available: `useCurrentFrame`, `useVideoConfig` (width, height, fps, durationInFrames), `Sequence` (from, durationInFrames), `Series` with `Series.Sequence`, `AbsoluteFill`, `interpolate`, `spring`, `Easing`, `random(seed)`. Inline styles, `<style>`, SVG and pure React libraries render; `useState`, effects, timers, canvas and iframes do not.
|
|
46
|
+
- Everything must come from the frame and the props. `Math.random()`, `Date.now()` and network calls make frames differ or fail.
|
|
47
|
+
- Pictures: declare each in `config.assets` ({ id, type: 'image', src }) and use `<img src="id">`. Fonts: Inter is built in; add other fonts as font assets.
|
|
48
|
+
- No audio from components: add sound with exported `layers` (`sfx:…`, audio files, captions).
|
|
49
|
+
|
|
50
|
+
## 3. The code layer: your own drawing every frame (trusted files only)
|
|
51
|
+
|
|
52
|
+
For things no layer can draw (particles, generative art, charts from data, custom 2D scenes):
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
code(function draw(ctx, { frame, t, fps, width, height, props, api }) {
|
|
56
|
+
const rand = api.random(11); // repeatable
|
|
57
|
+
for (let i = 0; i < 90; i++) { ctx.globalAlpha = 0.3 + 0.3 * api.noise(i, frame / 40); /* ... */ }
|
|
58
|
+
}, { id: 'field', width: 1920, height: 1080, props: { color: '#22d3ee' } })
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- `draw(ctx, args)` is called once per frame on a cleared canvas of the layer's size. `args.api` has `random(seed)`, `noise(x, y, z)`, `interpolate`, `spring`, `clamp`, `lerp`, `easings`. `Math.random` and `Date` throw: a frame must be a pure function of `frame` and `props`, or renders differ.
|
|
62
|
+
- The function is copied into the composition as text: it must use nothing from the surrounding file (pass data in `props`). A `src` file path works too: a `.js` file defining `function draw(ctx, args)`.
|
|
63
|
+
- It executes JavaScript while rendering, so `ove still|review|render|dev|thumbnail` refuse it unless you pass `--allow-code`. Pass that flag only for files you wrote or have read. The AI commands and the Studio never accept code layers; do not paste code layers from other people.
|
|
64
|
+
- Check with `ove still <file> --frame <n> --allow-code`, and look at frames at the start, middle and end.
|
|
65
|
+
|
|
66
|
+
## Choosing
|
|
67
|
+
|
|
68
|
+
Prefer the builder plus ordinary layers. Use React components when you think in components, props and JSX (charts, dashboards, lists from data); the output is drawn as HTML/SVG, so it suits text, layout and vector graphics, not pixel-level drawing. Reach for a code layer only for visuals nothing else draws, and keep it small and deterministic.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ove-effects
|
|
3
|
+
description: Put motion-graphics effects (glass cards, kinetic titles, icon orbit, dashboards, checklists, timelines, grades, transitions, end cards) on top of footage with the fx-* blocks of Open Video Engine. Use when the user has a video, usually a talking head, and wants it dressed with designed effects.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Effects over footage
|
|
7
|
+
|
|
8
|
+
Effects are blocks named `fx-*`. `ove blocks` lists them under "Effects". They are sized from the canvas, read the theme (`ove theme signal` uses a navy and #2457FF palette), and carry their own motion and sound.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
1. Put the footage in: `ove add footage video/composition.json --aspect 9:16 --theme signal --set src=clip.mp4 --frames 300`.
|
|
13
|
+
2. Overlay effects (they sit inside a scene, on top): `ove add fx-glass-cards video/composition.json --scene footage --at 60 --frames 90`. Change text with `--set name=value`; `ove blocks --json` shows every parameter.
|
|
14
|
+
3. Scene effects replace the picture for a while and bring their own copy of the footage: `ove add fx-stats-split video/composition.json --set footage=clip.mp4 --at 300 --frames 90`. Use `--at` for the start frame, and `--frames` to shorten or lengthen them.
|
|
15
|
+
4. Check: `ove validate`, then `ove review video/composition.json --output review.png`, and look at stills at the middle of each effect. Fix text that overlaps the speaker's face (effects keep to the top, the lower third or the edges, but check).
|
|
16
|
+
5. Render a draft with `--scale 0.5 --no-motion-blur`, then the final.
|
|
17
|
+
|
|
18
|
+
## Which block makes which effect
|
|
19
|
+
|
|
20
|
+
| Effect | Block | Notes |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| 01 Glass cards | `fx-glass-cards` | overlay; frosted look is translucent fill and border, not a real backdrop blur |
|
|
23
|
+
| 02 Duo title | `fx-duo-title` | overlay; in front of the subject, not behind |
|
|
24
|
+
| 03 Icon orbit | `fx-icon-orbit` | overlay; 2D ellipse with size for depth |
|
|
25
|
+
| 04 Stats split | `fx-stats-split` | scene; mark statistics DEMO DATA |
|
|
26
|
+
| 05 Spotlight | `fx-spotlight` | scene; title in front of the subject |
|
|
27
|
+
| 06 Checklist | `fx-checklist` | overlay |
|
|
28
|
+
| 07 Timeline | `fx-timeline` | overlay |
|
|
29
|
+
| 08 Command bridge | `fx-command-bridge` | overlay |
|
|
30
|
+
| 09 Word-synced captions | `fx-captions` | overlay; needs word timings from `ove ai transcribe` |
|
|
31
|
+
| 10 Highlighter | `fx-highlighter` | overlay |
|
|
32
|
+
| 11 Strike and rewrite | `fx-strike-rewrite` | overlay |
|
|
33
|
+
| 12 Glitch title | `fx-glitch-title` | overlay |
|
|
34
|
+
| 13 Extruded title | `fx-extruded-title` | overlay; in front, not behind |
|
|
35
|
+
| 14 Typewriter quote | `fx-typewriter-quote` | overlay; no blinking cursor |
|
|
36
|
+
| 16 Day to night | `fx-day-to-night` | scene |
|
|
37
|
+
| 18 Light leak | `fx-light-leak` | overlay at a scene boundary |
|
|
38
|
+
| 19 Duotone | `fx-duotone` | scene; no film grain |
|
|
39
|
+
| 20 Clone wall | `fx-clone-wall` | scene; copies the whole frame, not a cut-out |
|
|
40
|
+
| 21 Pull-out | `fx-pull-out` | scene |
|
|
41
|
+
| 22 Punch-in | `fx-punch-in` | scene; edit the camera keyframes to the word you want |
|
|
42
|
+
| 26 Notifications | `fx-notifications` | overlay |
|
|
43
|
+
| 27 Chat bubbles | `fx-chat` | overlay |
|
|
44
|
+
| 28 Before/after | `fx-before-after` | scene |
|
|
45
|
+
| 29 Counter hero | `fx-counter-hero` | overlay |
|
|
46
|
+
| 30 Map journey | `fx-map-journey` | scene; stylised map, you give the places and their positions |
|
|
47
|
+
| 31 Step tracker | `fx-step-tracker` | overlay |
|
|
48
|
+
| 32, 33 Blur dissolve, iris wipe | scene transitions `blur`, `iris` | set `transitionIn` on a scene |
|
|
49
|
+
| 34 Split reveal | `fx-split-reveal` | overlay |
|
|
50
|
+
| 35 Gallery | `fx-gallery` | scene |
|
|
51
|
+
| 36 CTA end card | `fx-cta-card` | scene; footage in a rounded window, not a cut-out |
|
|
52
|
+
|
|
53
|
+
## Effects that need a matte
|
|
54
|
+
|
|
55
|
+
15 neon outline (`fx-neon-outline`), 17 colour pop (`fx-color-pop`), 23 background swap (`fx-background-swap`), 24 sticker freeze (`fx-sticker-freeze`) and 25 tracked label (`fx-tracked-label`) need a matte of the speaker: a video or picture, white where the speaker is. OVE does not make it; the user makes it with a matting tool such as rembg or Robust Video Matting, or an editor, and you give it with `--set matte=speaker-matte.mp4` (and `--set footage=clip.mp4`). Without a matte these effects show the footage plain. For the tracked label run `ove track speaker-matte.mp4 --output track.json` and then `ove add fx-tracked-label video/composition.json --scene footage --track track.json`. Do not claim a result without a matte.
|
|
56
|
+
|
|
57
|
+
Anything the handbook puts "behind the subject" (duo title, spotlight title, extruded title) is placed in front by default; with a matte, put the title in a layer under a `matte`d copy of the footage to put it behind.
|
|
58
|
+
|
|
59
|
+
## Colour and extras
|
|
60
|
+
|
|
61
|
+
`fx-grade-look` (--set look=cinematic|warm|cool|teal-orange|noir|vivid|faded), `fx-letterbox`, `fx-shake-hit`, `fx-progress-ring`, `fx-ticker`, `fx-lens-flare`, `fx-rgb-flash`. For your own grade use a layer or scene `grade` property. Fonts: Inter is built in; other fonts (Poppins, Anton) need font assets.
|
|
62
|
+
|
|
63
|
+
## A whole video
|
|
64
|
+
|
|
65
|
+
`npx tsx examples/effects-demo/build.mjs clip.mp4` builds the handbook's 30-second master timeline (900 frames, 1080x1920) from these blocks and validates it.
|