@scenerok/cli 2.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/README.md +173 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +94 -0
- package/dist/commands/cache.d.ts +3 -0
- package/dist/commands/cache.d.ts.map +1 -0
- package/dist/commands/cache.js +65 -0
- package/dist/commands/lint-video.d.ts +21 -0
- package/dist/commands/lint-video.d.ts.map +1 -0
- package/dist/commands/lint-video.js +122 -0
- package/dist/commands/project.d.ts +3 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/project.js +173 -0
- package/dist/commands/render.d.ts +4 -0
- package/dist/commands/render.d.ts.map +1 -0
- package/dist/commands/render.js +242 -0
- package/dist/commands/skills.d.ts +3 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +208 -0
- package/dist/commands/validate.d.ts +3 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +254 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/lib/api.d.ts +268 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/api.js +160 -0
- package/dist/lib/config.d.ts +13 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +53 -0
- package/dist/lib/format-api-error.d.ts +2 -0
- package/dist/lib/format-api-error.d.ts.map +1 -0
- package/dist/lib/format-api-error.js +24 -0
- package/dist/lib/language.d.ts +3 -0
- package/dist/lib/language.d.ts.map +1 -0
- package/dist/lib/language.js +9 -0
- package/dist/lib/logger.d.ts +8 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +51 -0
- package/dist/lib/project.d.ts +29 -0
- package/dist/lib/project.d.ts.map +1 -0
- package/dist/lib/project.js +156 -0
- package/dist/lib/sdk-scene-modules.d.ts +7 -0
- package/dist/lib/sdk-scene-modules.d.ts.map +1 -0
- package/dist/lib/sdk-scene-modules.js +35 -0
- package/dist/utils/skillsManifest.d.ts +42 -0
- package/dist/utils/skillsManifest.d.ts.map +1 -0
- package/dist/utils/skillsManifest.js +122 -0
- package/examples/system/3-tips-fitness.vid +45 -0
- package/examples/system/ecom-product-launch.vid +31 -0
- package/examples/system/glitch-title.vid +17 -0
- package/examples/system/meme-remix.vid +15 -0
- package/examples/system/minimal-text-reel.vid +11 -0
- package/examples/system/product-launch.vid +18 -0
- package/examples/system/rokmilk-chocolate-promo.vid +25 -0
- package/examples/system/testimonial-cut.vid +16 -0
- package/examples/system/ugc-testimonial-voiceover.vid +33 -0
- package/examples/system/voice-visual-promo.vid +21 -0
- package/package.json +51 -0
- package/skills/remote/ai-models.md +65 -0
- package/skills/remote/animations-catalog.md +89 -0
- package/skills/remote/animations-picker.md +77 -0
- package/skills/remote/brand-setup.md +69 -0
- package/skills/remote/catalog-index.json +1810 -0
- package/skills/remote/environments.md +73 -0
- package/skills/remote/intent-router.md +81 -0
- package/skills/remote/media-stdlib.md +66 -0
- package/skills/remote/music.md +69 -0
- package/skills/remote/sdk-guide.md +201 -0
- package/skills/remote/shaders-filters.md +97 -0
- package/skills/remote/text-styles.md +108 -0
- package/skills/remote/transitions-catalog.md +55 -0
- package/skills/remote/tts.md +82 -0
- package/skills/remote/vidscript-guide.md +574 -0
- package/skills/remote/vidscript-sample.md +28 -0
- package/skills/remote/vidscript-strict.md +251 -0
- package/skills/remote/workflow.md +72 -0
- package/skills/shared/SKILL.md +157 -0
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
# VidScript Language Reference (v2)
|
|
2
|
+
|
|
3
|
+
VidScript is a declarative DSL for composing short-form videos. Write a script, render an MP4.
|
|
4
|
+
|
|
5
|
+
## Agent quick start (read before composing)
|
|
6
|
+
|
|
7
|
+
1. **Import every package function** — use `import xai from "@scenerok/xai"`, `import cf from "@scenerok/cloudflare"`, `import eleven from "@elevenlabs/music"`, and `import motion from "@scenerok/basic-animations"` before calling `xai.*`, `cf.*`, `eleven.*`, or `motion.*`.
|
|
8
|
+
2. **Validate early** — `scenerok validate file.vid` (errors show as `Line N:C — message`).
|
|
9
|
+
3. **Music package syntax** — `import eleven from "@elevenlabs/music"`, then call `eleven.music(...)`, `eleven.generateMusic(...)`, or `eleven.composeMusic(...)`.
|
|
10
|
+
4. **Copy working examples** — see `examples/system/` in this skill folder (e.g. `minimal-text-reel.vid`, `ecom-product-launch.vid`, `rokmilk-chocolate-promo.vid`).
|
|
11
|
+
5. **Use URL assets selectively** — if the user gives a website or product URL and browser screenshot tools are available, you can capture above-fold screenshots, product images, app screenshots, and logos for VidScript `input` assets. Do not treat screenshots as mandatory or as the only visual source.
|
|
12
|
+
6. **Probe asset type and dimensions** — before placing real assets, identify image vs video with `file`; inspect width, height, and duration with `ffprobe`, `sips`, or `identify`; then scale from the measured aspect ratio and set explicit `width`, `height`, `x`, and `y`.
|
|
13
|
+
7. **Use still images directly** — `input` supports videos and images. Place still screenshots, logos, and product photos with `video image_input`; the renderer treats images as static clips for the block duration.
|
|
14
|
+
8. **Use the full media plugin toolkit** — choose still generation, text-to-video, image-to-video, reference-to-video, video extension, TTS, and music deliberately. Do not default to only `xai.imagine`.
|
|
15
|
+
9. **Include audio when useful** — use `xai.tts` / `xai.textToSpeech` for narration and `eleven.music` / `eleven.generateMusic` / `eleven.composeMusic` for music beds.
|
|
16
|
+
10. **Generated media has no text** — prompts for AI-generated images/videos must explicitly ask for no text, no words, no letters, no captions, no logos, no watermarks, and no readable UI copy. Use VidScript `text` primitives for all final copy.
|
|
17
|
+
11. **Channel-aware `[-]` timing** — audio and visual auto blocks have separate playheads. A music bed or TTS sequence does not delay the next `[-] = video ...` block.
|
|
18
|
+
12. **Never guess asset durations** — assign plugin results with `let` and reference the measured duration: `let vo = xai.tts(...)` then `[0 .. vo.duration] = audio vo`. Guessed windows like `[0s .. 3s] = audio xai.tts(...)` clip or overlap when the real audio runs longer.
|
|
19
|
+
13. **Never guess text coordinates** — place text with `area:` (`title`, `stage`, `lower-third`, `caption` built in) instead of hand-picked `x`/`y`. See Grid Areas below.
|
|
20
|
+
14. **Deep-validate before rendering** — `scenerok validate script.vid --generate-assets --report-json` runs the real compile (billed, assets cached for the render) and returns measured text geometry, collisions, and audio overlaps to fix before rendering.
|
|
21
|
+
15. **Strict rules** — see `vidscript-strict.md` for invalid patterns (bare `xai.imagine`, bare `cf.video(...)`, bare `eleven.music(...)` without import, trailing params after direct plugin calls, nested quotes in prompts).
|
|
22
|
+
|
|
23
|
+
### Common validation failures
|
|
24
|
+
|
|
25
|
+
| Symptom | Fix |
|
|
26
|
+
|---------|-----|
|
|
27
|
+
| `Unknown function 'xai.imagine'` | Add `import xai from "@scenerok/xai"` at top |
|
|
28
|
+
| `Unknown function 'cf.video'` | Add `import cf from "@scenerok/cloudflare"` at top |
|
|
29
|
+
| `Unknown function 'fadeIn'` | Use `import motion from "@scenerok/basic-animations"` and call `motion.fadeIn(...)` |
|
|
30
|
+
| `Expected ... but "," found` | Do not put `, volume:` after a direct plugin call. Use `let bed = eleven.music(...)`, then `audio bed, volume: ...` |
|
|
31
|
+
| `Unknown function 'eleven.generateMusic'` | Add `import eleven from "@elevenlabs/music"` before calling `eleven.generateMusic(...)` |
|
|
32
|
+
| Prompt needs quoted words | Escape quotes inside strings, e.g. `"Package labeled \"AER-01\""` |
|
|
33
|
+
|
|
34
|
+
## Program Structure
|
|
35
|
+
|
|
36
|
+
A VidScript program is a sequence of statements separated by newlines.
|
|
37
|
+
|
|
38
|
+
Strings support common backslash escapes. Use `\n` inside a `text` string for an intentional rendered line break, and use `\"` or `\'` for quote characters inside the matching string delimiter.
|
|
39
|
+
|
|
40
|
+
```vidscript
|
|
41
|
+
[0s .. 3s] = text "RUN LIGHTER\nDAILY", line_height: 1.05
|
|
42
|
+
let prompt = "Package labeled \"AER-01\", no text, no watermark"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```vidscript
|
|
46
|
+
# Single-line comment
|
|
47
|
+
/* Multi-line
|
|
48
|
+
comment */
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Statements
|
|
52
|
+
|
|
53
|
+
### Input Declaration
|
|
54
|
+
|
|
55
|
+
```vidscript
|
|
56
|
+
input hero = "https://cdn.example.com/hero.mp4"
|
|
57
|
+
input logo = "./assets/logo.png"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Supports HTTP(S) URLs, `/uploads/` paths, and local paths for video/image assets.
|
|
61
|
+
|
|
62
|
+
For ads based on a supplied URL, browser tools can capture real website visuals and save product images or logos when useful. Use screenshots sparingly: favor above-fold, cropped, or focused captures that remain legible at video size. Avoid full-page or long scrolling screenshots as backgrounds because their text will usually become unreadable.
|
|
63
|
+
|
|
64
|
+
Website captures are optional grounding assets, not a ban on generated video. A strong ad may combine product/logo/app screenshots with generated lifestyle clips, motion backgrounds, transitions, and VidScript text primitives. Use generative clips when they improve storytelling, show context, or make the ad feel more polished.
|
|
65
|
+
|
|
66
|
+
After declaring or downloading assets, probe file type and dimensions before placing them:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
file asset.png
|
|
70
|
+
ffprobe -v error -select_streams v:0 -show_entries stream=width,height,duration -of csv=p=0 asset.mp4
|
|
71
|
+
sips -g pixelWidth -g pixelHeight asset.png
|
|
72
|
+
identify asset.webp
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Use real videos as motion footage and still images as static clips. The `video` primitive accepts both; the renderer detects image vs video:
|
|
76
|
+
|
|
77
|
+
```vidscript
|
|
78
|
+
input screenshot = "assets/screenshots/homepage.png"
|
|
79
|
+
[0s .. 4.5s] = video screenshot, width: 1080, height: 1920, x: 0, y: 0, animate: motion.fadeIn(0.35s)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For URL-based ads, still assets can also drive generated motion: use `xai.imageToVideo(screenshot, "...")` for one strong source image, or `xai.referenceToVideo([product, logo, person], "...")` to guide a new scene with extracted references.
|
|
83
|
+
|
|
84
|
+
Use the measured width and height to preserve aspect ratio. For a 1080x1920 vertical ad, a common centered fit is:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
scaledWidth = min(assetWidth * scale, 900)
|
|
88
|
+
scaledHeight = scaledWidth / (assetWidth / assetHeight)
|
|
89
|
+
x = (1080 - scaledWidth) / 2
|
|
90
|
+
y = chosen safe-area top offset
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Do not guess asset size. Tiny logos, wide screenshots, and tall app screenshots need different scale and placement.
|
|
94
|
+
|
|
95
|
+
### Output Declaration
|
|
96
|
+
|
|
97
|
+
```vidscript
|
|
98
|
+
output to "video.mp4", resolution: "1080x1920", fps: 30
|
|
99
|
+
output to "reel.mp4", resolution: "720x1280", format: "mp4", codec: "h264", bitrate: "5M", background: "#0D0D0D"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
| Option | Type | Default | Description |
|
|
103
|
+
|--------|------|---------|-------------|
|
|
104
|
+
| `resolution` | string | `"1080x1920"` | Width×Height or single number |
|
|
105
|
+
| `fps` | number | `30` | Frames per second |
|
|
106
|
+
| `format` | string | `"mp4"` | Container format |
|
|
107
|
+
| `codec` | string | `"h264"` | Video codec |
|
|
108
|
+
| `bitrate` | string | `"5M"` | Encoding bitrate |
|
|
109
|
+
| `background` | string | `"#000000"` | Background color hex |
|
|
110
|
+
|
|
111
|
+
### Variable Assignment
|
|
112
|
+
|
|
113
|
+
```vidscript
|
|
114
|
+
let brandColor = "#6366F1"
|
|
115
|
+
let fadeTime = 0.5s
|
|
116
|
+
let titleSize = 72
|
|
117
|
+
let clipName = "hero"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Variables can hold strings, numbers, time literals, and object expressions. Variables are evaluated at compile time.
|
|
121
|
+
|
|
122
|
+
### Package Imports
|
|
123
|
+
|
|
124
|
+
Only default package imports are supported for callable packages. Call functions through the imported alias.
|
|
125
|
+
|
|
126
|
+
```vidscript
|
|
127
|
+
import xai from "@scenerok/xai"
|
|
128
|
+
import cf from "@scenerok/cloudflare"
|
|
129
|
+
import eleven from "@elevenlabs/music"
|
|
130
|
+
import motion from "@scenerok/basic-animations"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Named imports (`import { fn } ...`) and namespace imports (`import * as ...`) are not supported.
|
|
134
|
+
|
|
135
|
+
## Time Blocks
|
|
136
|
+
|
|
137
|
+
Time blocks are the core of VidScript. They define when instructions execute on the timeline.
|
|
138
|
+
|
|
139
|
+
### Dynamic Playhead (recommended)
|
|
140
|
+
|
|
141
|
+
```vidscript
|
|
142
|
+
[-] = hero # auto-append: starts at visual cursor
|
|
143
|
+
[- 3s] = text "Title", size: 72 # auto-start, last 3 seconds
|
|
144
|
+
[- 2.5s] = filter "glow", intensity: 0.8
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
VidScript keeps separate auto-playhead cursors for visual and audio content. Visual blocks (`video`, `text`, filters, shaders, visual plugin calls, and bare visual inputs) advance the visual cursor. Audio blocks (`audio`, `xai.tts`, `eleven.music`, and other audio plugin calls) advance the audio cursor. `[- duration]` advances the relevant cursor by the explicit duration.
|
|
148
|
+
|
|
149
|
+
This means a long audio bed or voiceover sequence does not push a following auto video block to the end of the audio:
|
|
150
|
+
|
|
151
|
+
```vidscript
|
|
152
|
+
import xai from "@scenerok/xai"
|
|
153
|
+
import eleven from "@elevenlabs/music"
|
|
154
|
+
|
|
155
|
+
input product = "https://cdn.example.com/product.png"
|
|
156
|
+
let bed = eleven.music("Warm music bed", duration: 15, instrumental: true)
|
|
157
|
+
[0s .. 15s] = audio bed, volume: 0.35
|
|
158
|
+
[-] = video xai.imageToVideo(product, "Slow premium camera move", aspect_ratio: "9:16", duration: 6)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The generated video above starts at the current visual cursor, usually `0s`, even though the audio cursor already reaches `15s`. Mixed blocks containing both audio and visual instructions advance both cursors. The `prev` keyword resolves against the cursor for the block being compiled; for mixed timing it uses the current combined timeline position.
|
|
162
|
+
|
|
163
|
+
### Explicit Range
|
|
164
|
+
|
|
165
|
+
```vidscript
|
|
166
|
+
[0s .. 5s] = hero # absolute range
|
|
167
|
+
[0.5s .. 3s] = text "Hello" # explicit times
|
|
168
|
+
[prev + 1s .. prev + 4s] = filter "glow" # expression-based
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Use `..` (not `-`) as the range separator to avoid ambiguity with subtraction.
|
|
172
|
+
|
|
173
|
+
### Time Formats
|
|
174
|
+
|
|
175
|
+
```vidscript
|
|
176
|
+
5s # 5 seconds
|
|
177
|
+
300ms # 300 milliseconds
|
|
178
|
+
frame 90 # 90 frames at 30fps = 3 seconds
|
|
179
|
+
0:30 # 30 seconds
|
|
180
|
+
1:30:00 # 1 hour 30 minutes
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### The `prev` Keyword
|
|
184
|
+
|
|
185
|
+
`prev` refers to the current channel-aware playhead position for the block being compiled. In a visual block it follows the visual cursor; in an audio block it follows the audio cursor.
|
|
186
|
+
|
|
187
|
+
```vidscript
|
|
188
|
+
[- 2s] = text "Hello" # plays from cursor to cursor+2s
|
|
189
|
+
[prev + 1s .. prev + 3s] = filter "glow" # 1s after text ends, for 2s
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Symbolic Durations (`asset.duration`)
|
|
193
|
+
|
|
194
|
+
Generated assets (TTS, music, video) have durations you cannot know while writing the script. Assign the plugin result with `let` and reference its **measured** duration in any time expression — the compiler probes the generated file, so the value is real, not an estimate:
|
|
195
|
+
|
|
196
|
+
```vidscript
|
|
197
|
+
import xai from "@scenerok/xai"
|
|
198
|
+
|
|
199
|
+
let vo1 = xai.tts("Welcome to the show", voice: "eve")
|
|
200
|
+
let vo2 = xai.tts("Let's dive in", voice: "eve")
|
|
201
|
+
|
|
202
|
+
[0 .. vo1.duration] = audio vo1
|
|
203
|
+
[vo1.duration + 0.3 .. vo1.duration + 0.3 + vo2.duration] = audio vo2
|
|
204
|
+
[0 .. vo1.duration] = text "Welcome", area: "title" # visual spans the narration exactly
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
- Bare numbers are seconds; `2s` / `300ms` literals and `+ - * /` arithmetic work anywhere a time appears.
|
|
208
|
+
- Exported timelines expose `.duration` too: `use scene1 at 0s .. 0s with {}` then `[scene1.duration .. scene1.duration + 4] = ...`. A `use` also advances the playhead past its content, so a following `[-]` block never overlaps it.
|
|
209
|
+
- This replaces guessing: `[0s .. 5s] = audio xai.tts(...)` clips or overlaps when the real audio is longer. For simple back-to-back narration, consecutive `[-] = audio ...` blocks also never overlap.
|
|
210
|
+
|
|
211
|
+
## Include TypeScript / JavaScript scenes
|
|
212
|
+
|
|
213
|
+
Import a `@scenerok/sdk` scene (`.ts` / `.js`) into VidScript with the same default-import form as plugins, then `use` it like a timeline:
|
|
214
|
+
|
|
215
|
+
```vidscript
|
|
216
|
+
import intro from "./intro.ts"
|
|
217
|
+
import bump from "./bump.js"
|
|
218
|
+
|
|
219
|
+
use intro at 0s .. 0s with {}
|
|
220
|
+
use bump at intro.duration .. intro.duration with {}
|
|
221
|
+
[intro.duration + bump.duration .. intro.duration + bump.duration + 2s] = text "Thanks", area: "caption"
|
|
222
|
+
|
|
223
|
+
output to "reel.mp4", resolution: "1080x1920"
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Rules:
|
|
227
|
+
- The SDK file must export `default function main()` (or `module.exports = main` / `exports.main`) that returns `t.toIR()` or an `IRTimeline`.
|
|
228
|
+
- `with { ... }` params are not supported on SDK scenes (no timeline parameters).
|
|
229
|
+
- Relative paths only (`./` / `../`). The CLI uploads companion scene files automatically when you `scenerok validate` / `scenerok render` a `.vid`.
|
|
230
|
+
- Prefer this when a complex loop/branch scene should sit inside a mostly-VidScript reel. For a fully TypeScript project, author the entry as `.ts` instead.
|
|
231
|
+
|
|
232
|
+
## Instructions (inside time blocks)
|
|
233
|
+
|
|
234
|
+
Multiple instructions in the same time block go on separate lines:
|
|
235
|
+
|
|
236
|
+
```vidscript
|
|
237
|
+
[0s .. 5s] = hero
|
|
238
|
+
hero.Trim(start: 0s, end: 5s)
|
|
239
|
+
hero.Speed(factor: 1.5)
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Clip Reference
|
|
243
|
+
|
|
244
|
+
```vidscript
|
|
245
|
+
[-] = hero # just play the input clip
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Text Overlay
|
|
249
|
+
|
|
250
|
+
```vidscript
|
|
251
|
+
[- 3s] = text "Hello World", font: "Bebas Neue", x: "50%", y: "45%", align: center, color: "#FFFFFF", size: 72, stroke: "#000000", stroke_width: 3, letter_spacing: 4, shadow_color: "#FF0055", shadow_blur: 8
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
| Param | Type | Description |
|
|
255
|
+
|-------|------|-------------|
|
|
256
|
+
| `style` | string | `title` (64px), `subtitle` (54px), `caption` (36px), or `default` (48px) |
|
|
257
|
+
| `area` | string | Named grid region (preferred over `x`/`y`). Built-in: `title`, `stage`, `lower-third`, `caption`. Centers text in the cell and caps `max_width`/`max_height` to it. See Grid Areas. |
|
|
258
|
+
| `position` | string | `center`, `top`, `bottom`, `top-left`, `top-right`, `bottom-left`, `bottom-right` |
|
|
259
|
+
| `x` | number \| string | Horizontal position. Number = px from left. `"50%"` = percent of width. Overrides `position` and `area`. |
|
|
260
|
+
| `y` | number \| string | Vertical position. Number = px from top. `"50%"` = percent of height. Overrides `position` and `area`. |
|
|
261
|
+
| `max_width` | number \| string | Wrap/shrink boundary, px or `"90%"` |
|
|
262
|
+
| `max_height` | number \| string | Vertical fit boundary, px or `"20%"` |
|
|
263
|
+
| `fit` | string | `wrap-shrink` (default), `wrap`, `shrink`, `none` |
|
|
264
|
+
| `overflow` | string | `contain` (default, clamps to safe area), `crop`, `allow` |
|
|
265
|
+
| `color` | string | Hex color |
|
|
266
|
+
| `size` | number \| string | Font size in output canvas pixels. `size: 72` is shorthand for `size: "72px"`. |
|
|
267
|
+
| `font` | string | Font family. System fonts or Google Fonts: `"Inter"`, `"Bebas Neue"`, `"Space Grotesk"`, `"Outfit"`, etc. |
|
|
268
|
+
| `align` | string | `left`, `center`, `right`. Default: `center` |
|
|
269
|
+
| `line_height` | number | Line height multiplier for multi-line text. Default: 1.2 |
|
|
270
|
+
| `letter_spacing` | number | Extra letter spacing in pixels. Default: 0 |
|
|
271
|
+
| `rotation` | number | Rotation in degrees. Positive = clockwise. Default: 0 |
|
|
272
|
+
| `opacity` | number | Opacity 0–1. Default: 1 |
|
|
273
|
+
| `stroke` | string | Outline color |
|
|
274
|
+
| `stroke_width` | number | Outline thickness |
|
|
275
|
+
| `shadow_color` | string | Drop shadow color |
|
|
276
|
+
| `shadow_blur` | number | Shadow blur radius |
|
|
277
|
+
| `shadow_offset_x` | number | Shadow horizontal offset |
|
|
278
|
+
| `shadow_offset_y` | number | Shadow vertical offset |
|
|
279
|
+
| `animation` | string | Parsed but not yet rendered |
|
|
280
|
+
|
|
281
|
+
Params are optional and comma-separated. Params can be on the same line as the content.
|
|
282
|
+
|
|
283
|
+
### Grid Areas
|
|
284
|
+
|
|
285
|
+
Place text in named regions instead of guessing coordinates. Without any declaration, a built-in grid provides `title`, `stage`, `lower-third`, and `caption`:
|
|
286
|
+
|
|
287
|
+
```vidscript
|
|
288
|
+
[0 .. 3] = text "BIG SALE", area: "title"
|
|
289
|
+
[0 .. 3] = text "50% off this week", area: "lower-third"
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Declare a custom grid with CSS Grid semantics — `rows`/`cols` are track lists (`%` and `fr` units only), `areas` is grid-template-areas (one string per row, `.` for empty cells, repeated names span):
|
|
293
|
+
|
|
294
|
+
```vidscript
|
|
295
|
+
grid rows: "20% 1fr 30%", cols: "1fr 1fr", areas: ["title title", "left right", "caption caption"]
|
|
296
|
+
[0 .. 3] = text "Left column", area: "left"
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
The area centers the text in its cell and sets `max_width`/`max_height` to the cell size, so long copy wraps and shrinks to fit the region. Explicit `x`/`y`/`max_width`/`max_height` override the area. Everything resolves to percentages, so grids work at any output resolution.
|
|
300
|
+
|
|
301
|
+
**TypeScript SDK equivalent** (`sdk-guide.md`):
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
t.grid({
|
|
305
|
+
rows: '20% 1fr 30%',
|
|
306
|
+
cols: '1fr 1fr',
|
|
307
|
+
areas: ['title title', 'left right', 'caption caption'],
|
|
308
|
+
})
|
|
309
|
+
t[0][s(3)] = text('Left column', { area: 'left' })
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Video Methods
|
|
313
|
+
|
|
314
|
+
```vidscript
|
|
315
|
+
hero.Trim(start: 0s, end: 5s) # trim clip (positional: hero.Trim(0s, 5s))
|
|
316
|
+
hero.Resize(width: 720, height: 1280) # resize (positional: hero.Resize(720, 1280))
|
|
317
|
+
hero.Speed(factor: 1.5) # playback rate
|
|
318
|
+
hero.Loop(count: 3) # loop count
|
|
319
|
+
hero.Opacity(value: 0.5, duration: 2s) # opacity with duration
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Arguments can be positional or named using `:`.
|
|
323
|
+
|
|
324
|
+
### Compositing
|
|
325
|
+
|
|
326
|
+
```vidscript
|
|
327
|
+
hero.Overlay(logo, x: 50, y: 100, opacity: 0.8)
|
|
328
|
+
hero.Composite(overlay, x: 0, y: 0, opacity: 0.5, mode: "screen")
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
| Param | Type | Description |
|
|
332
|
+
|-------|------|-------------|
|
|
333
|
+
| `x` | number | Horizontal offset |
|
|
334
|
+
| `y` | number | Vertical offset |
|
|
335
|
+
| `opacity` | number | 0–1 transparency |
|
|
336
|
+
| `mode` | string | Blend mode for Composite |
|
|
337
|
+
|
|
338
|
+
### Filters
|
|
339
|
+
|
|
340
|
+
```vidscript
|
|
341
|
+
[- 2s] = filter "glow", intensity: 0.8
|
|
342
|
+
[0s .. 5s] = filter "vignette", intensity: 0.3
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Built-in filters: `monochrome`, `sepia`, `blur`, `chromatic`, `glitch`, `vignette`, `contrast`, `saturation`, `brightness`.
|
|
346
|
+
|
|
347
|
+
### Shaders
|
|
348
|
+
|
|
349
|
+
Author fragment GLSL in a separate file, import it, then apply with `shader` (not `filter`):
|
|
350
|
+
|
|
351
|
+
```vidscript
|
|
352
|
+
import shader glow from "./shaders/glow.glsl"
|
|
353
|
+
# or: import shader glow from "shader-pack/shaders/glow.glsl"
|
|
354
|
+
|
|
355
|
+
[0s .. 5s] = shader glow, intensity: 0.8
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Fragment must sample `tDiffuse`, write `gl_FragColor`, and declare uniforms matching param names. See `shaders-filters.md` → Custom shaders.
|
|
359
|
+
### Audio
|
|
360
|
+
|
|
361
|
+
```vidscript
|
|
362
|
+
audio music, volume: 0.5, fade_in: 1s, fade_out: 2s
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Audio sources can be MP3 inputs, audio plugin results, or the embedded audio stream from an input video. Video inputs are visually silent unless you explicitly add `audio clip`. Plugin-generated audio (TTS, music, and probed native audio on generated video clips) is EBU R128 loudness-normalized during render to about -16 LUFS; `volume` is a relative gain applied after that. Ordinary imported audio/video is not auto-normalized.
|
|
366
|
+
|
|
367
|
+
| Param | Type | Description |
|
|
368
|
+
|-------|------|-------------|
|
|
369
|
+
| `volume` | number | Relative gain (0–1+). For generated audio, applied after loudness normalization. |
|
|
370
|
+
| `fade_in` | number | Fade-in duration (seconds) |
|
|
371
|
+
| `fade_out` | number | Fade-out duration (seconds) |
|
|
372
|
+
|
|
373
|
+
### Plugin Calls
|
|
374
|
+
|
|
375
|
+
```vidscript
|
|
376
|
+
import xai from "@scenerok/xai"
|
|
377
|
+
import cf from "@scenerok/cloudflare"
|
|
378
|
+
import eleven from "@elevenlabs/music"
|
|
379
|
+
import motion from "@scenerok/basic-animations"
|
|
380
|
+
|
|
381
|
+
[- 4s] = video xai.image("Premium product still on seamless studio background, no text, no words, no letters, no captions, no logos, no watermark, no readable UI copy", aspect_ratio: "9:16", resolution: "2k")
|
|
382
|
+
[-] = video xai.imagine("Cinematic product video, premium lighting, no text, no words, no letters, no captions, no logos, no watermark, no readable UI copy", aspect_ratio: "9:16", resolution: "720p", duration: 5)
|
|
383
|
+
[-] = video xai.imageToVideo("https://cdn.example.com/product.png", "Slow premium camera move around the product, no text, no words, no letters, no captions, no logos, no watermark, no readable UI copy", aspect_ratio: "9:16", duration: 6)
|
|
384
|
+
[-] = video xai.referenceToVideo(["https://cdn.example.com/product.png", "https://cdn.example.com/person.png"], "Lifestyle ad shot featuring the referenced product and person, no text, no words, no letters, no captions, no logos, no watermark, no readable UI copy", aspect_ratio: "9:16", duration: 8)
|
|
385
|
+
[-] = video xai.extendVideo("https://cdn.example.com/clip.mp4", "Camera pulls back to reveal the full premium setup, no text, no words, no letters, no captions, no logos, no watermark, no readable UI copy", duration: 5)
|
|
386
|
+
[-] = audio xai.tts("Welcome to SceneRok", voice: "eve")
|
|
387
|
+
|
|
388
|
+
# Cloudflare AI Gateway package.
|
|
389
|
+
[-] = video cf.video("High-energy fashion product shot, no text, no words, no letters, no captions, no logos, no watermark, no readable UI copy", model: "pixverse/v6", aspect_ratio: "9:16", duration: 5, generate_audio: false)
|
|
390
|
+
[-] = video cf.imageToVideo("https://cdn.example.com/product.png", "Animate the product with realistic parallax, no text, no words, no letters, no captions, no logos, no watermark, no readable UI copy", model: "runwayml/gen-4.5", aspect_ratio: "9:16", duration: 5)
|
|
391
|
+
[- 4s] = video cf.image("Clean editorial product still, no text, no words, no letters, no captions, no logos, no watermark, no readable UI copy", model: "black-forest-labs/flux-2-pro-preview", aspect_ratio: "9:16")
|
|
392
|
+
|
|
393
|
+
# ElevenLabs music package functions require an import alias.
|
|
394
|
+
[0s .. 15s] = audio eleven.music("Warm premium launch bed", duration: 15, instrumental: true)
|
|
395
|
+
|
|
396
|
+
# If you need volume or fades, store the generated audio first.
|
|
397
|
+
let bed = eleven.music("Warm premium launch bed", duration: 15, instrumental: true)
|
|
398
|
+
[0s .. 15s] = audio bed, volume: 0.35, fade_out: 2s
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
`@scenerok/xai`, `@scenerok/cloudflare`, and `@elevenlabs/music` require explicit default imports. ElevenLabs music validates as `eleven.music(...)`, `eleven.generateMusic(...)`, or `eleven.composeMusic(...)` after `import eleven from "@elevenlabs/music"`. Plugin calls run at compile time; direct media plugin instructions cannot have trailing audio params, so use a `let` binding when you need `volume`, `fade_in`, or `fade_out`.
|
|
402
|
+
|
|
403
|
+
### Media Plugin API Reference
|
|
404
|
+
|
|
405
|
+
| Package | Function | Use when |
|
|
406
|
+
|---------|----------|----------|
|
|
407
|
+
| `@scenerok/xai` | `xai.image(prompt, ...)`, `xai.generateImage(prompt, ...)` | Generate a still image clip. Use `resolution: "1k"` or `"2k"` and `duration` for how long the still stays on the timeline. |
|
|
408
|
+
| `@scenerok/xai` | `xai.imagine(prompt, ...)`, `xai.genVideo(prompt, ...)` | Generate video from text. Use video resolutions such as `"480p"` or `"720p"`. |
|
|
409
|
+
| `@scenerok/xai` | `xai.imageToVideo(image, prompt, ...)` | Animate one source image, screenshot, logo, product photo, data URI, file id, or `{ url }` object. |
|
|
410
|
+
| `@scenerok/xai` | `xai.referenceToVideo([images...], prompt, ...)` | Guide a generated scene with 1-7 reference images. Requires a prompt; keep duration <= 10s. |
|
|
411
|
+
| `@scenerok/xai` | `xai.extendVideo(video, prompt, ...)` | Continue an existing video clip by up to 10s. |
|
|
412
|
+
| `@scenerok/xai` | `xai.tts(text, ...)`, `xai.textToSpeech(text, ...)` | Generate spoken narration. Voices: `eve`, `ara`, `rex`, `sal`, `leo`. |
|
|
413
|
+
| `@scenerok/cloudflare` | `cf.image(prompt, ...)`, `cf.generateImage(prompt, ...)` | Generate stills through Cloudflare AI Gateway image models such as `black-forest-labs/flux-2-pro-preview`, `openai/gpt-image-2`, `xai/grok-imagine-image-quality`, or `recraft/recraftv4-pro`. |
|
|
414
|
+
| `@scenerok/cloudflare` | `cf.video(prompt, ...)`, `cf.imagine(prompt, ...)`, `cf.genVideo(prompt, ...)`, `cf.generateVideo(prompt, ...)` | Generate text-to-video through models such as `pixverse/v6`, `vidu/q3-turbo`, `runwayml/gen-4.5`, `minimax/hailuo-2.3`, `bytedance/seedance-2.0`, `google/veo-3.1`, `google/veo-3.1-fast`, or `google/veo-3-fast`. |
|
|
415
|
+
| `@scenerok/cloudflare` | `cf.imageToVideo(image, prompt, ...)`, `cf.referenceToVideo([images...], prompt, ...)`, `cf.extendVideo(video, prompt, ...)` | Use Cloudflare-backed image-guided, reference-guided, or extension modes when the selected model supports them. |
|
|
416
|
+
| `@scenerok/cloudflare` | `cf.listModels()`, `cf.models()` | Return model metadata as data; useful for exploration, not for timeline media. |
|
|
417
|
+
| `@elevenlabs/music` | `eleven.music(prompt, ...)`, `eleven.generateMusic(prompt, ...)`, `eleven.composeMusic(prompt, ...)` | Generate background music. Use a `let` binding if you need `volume`, `fade_in`, or `fade_out`. |
|
|
418
|
+
|
|
419
|
+
xAI TTS voices: `eve` for upbeat demos/announcements, `ara` for warm conversational narration, `rex` for business/tutorial content, `sal` for balanced general delivery, and `leo` for authoritative instruction.
|
|
420
|
+
|
|
421
|
+
Do not call `eleven.tts(...)`; the registered ElevenLabs package is `@elevenlabs/music` for music beds only. Use `xai.tts(...)` or `xai.textToSpeech(...)` for voiceover.
|
|
422
|
+
|
|
423
|
+
Cloudflare video calls accept these common signatures:
|
|
424
|
+
|
|
425
|
+
| Function | Signature |
|
|
426
|
+
|----------|-----------|
|
|
427
|
+
| `cf.video` / `cf.generateVideo` / `cf.imagine` / `cf.genVideo` | `(prompt, model?, aspect_ratio?, duration?, resolution?/quality?, generate_audio?, negative_prompt?, seed?, input?)` |
|
|
428
|
+
| `cf.imageToVideo` | `(image, prompt, model?, aspect_ratio?, duration?, resolution?/quality?, generate_audio?, negative_prompt?, seed?, input?)` |
|
|
429
|
+
| `cf.referenceToVideo` | `([images...], prompt, model?, aspect_ratio?, duration?, resolution?/quality?, generate_audio?, input?)` |
|
|
430
|
+
| `cf.extendVideo` | `(video, prompt, model?, duration?, aspect_ratio?, resolution?/quality?, input?)` |
|
|
431
|
+
|
|
432
|
+
Use `aspect_ratio: "9:16"` for vertical output, `"16:9"` for wide, `"1:1"` for square, or model-supported values such as `"4:3"`, `"3:4"`, and `"21:9"`. For Runway models, keep writing friendly `aspect_ratio` values; SceneRok translates them to Cloudflare's required `ratio` values such as `"720:1280"` for vertical Gen-4.5. Prefer explicit `model:` when choosing Cloudflare so the intended provider is clear.
|
|
433
|
+
|
|
434
|
+
**Invalid (will not validate):**
|
|
435
|
+
|
|
436
|
+
```vidscript
|
|
437
|
+
[-] = video xai.imagine("...") # missing xAI import
|
|
438
|
+
[0s .. 5s] = video cf.video("...") # missing Cloudflare import
|
|
439
|
+
[0s .. 15s] = audio eleven.generateMusic("...") # missing `import eleven from "@elevenlabs/music"`
|
|
440
|
+
[0s .. 5s] = audio eleven.tts("...") # ElevenLabs TTS is not registered; use xai.tts
|
|
441
|
+
[0s .. 15s] = audio eleven.music("..."), volume: 0.5 # trailing params after direct plugin call
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
## Animations, Effects & Rendering (v2)
|
|
445
|
+
|
|
446
|
+
VidScript v2 uses a **minimal-grammar, plugin-first** architecture. The compiler lowers VidScript into `IRTimeline` — the single contract shared by browser preview and final render.
|
|
447
|
+
|
|
448
|
+
User-facing extension uses two parameters on any text or video surface:
|
|
449
|
+
|
|
450
|
+
- `animate:` — motion over time (plugin calls or object descriptors)
|
|
451
|
+
- `effects:` — post-processing filters with optional animated params
|
|
452
|
+
|
|
453
|
+
### animate: parameter
|
|
454
|
+
|
|
455
|
+
```vidscript
|
|
456
|
+
import motion from "@scenerok/basic-animations"
|
|
457
|
+
|
|
458
|
+
[0s .. 3s] = text "Hello", x: "50%", y: "50%", animate: motion.fadeIn(0.8s)
|
|
459
|
+
|
|
460
|
+
[-] = video hero, animate: [motion.fadeIn(0.5s), motion.slideY(-40, 0, 1.2s)]
|
|
461
|
+
|
|
462
|
+
# Static asset with measured 4:3-ish aspect ratio, centered in a vertical 1080x1920 frame.
|
|
463
|
+
[0s .. 3.4s] = video app_screen, width: 760, height: 615, x: 160, y: 300, animate: [motion.riseIn(0.6s, 70), motion.float(2.4s, 8)]
|
|
464
|
+
[3.4s .. 4s] = video app_screen, width: 760, height: 615, x: 160, y: 300, animate: motion.fadeOut(0.6s)
|
|
465
|
+
|
|
466
|
+
[0s .. 2s] = text "Title", animate: {
|
|
467
|
+
type: "fade",
|
|
468
|
+
from: { opacity: 0 },
|
|
469
|
+
to: { opacity: 1 },
|
|
470
|
+
start: 0,
|
|
471
|
+
end: 1.5,
|
|
472
|
+
easing: "easeOutQuad"
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Compiler lowers `animate:` into `IRMotionTrack[]` (property-path keyframes). Legacy `IRAnimation` descriptors are adapted at runtime for backward compatibility.
|
|
477
|
+
|
|
478
|
+
### effects: parameter
|
|
479
|
+
|
|
480
|
+
```vidscript
|
|
481
|
+
[0s .. 5s] = filter "vignette", intensity: 0.4
|
|
482
|
+
|
|
483
|
+
[0s .. 5s] = filter "glitch", intensity: 0.5, animate: motion.fadeIn(1s)
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
Built-in filters: `monochrome`, `sepia`, `blur`, `chromatic`, `glitch`, `vignette`, `contrast`, `saturation`, `brightness`. Filters are optional finishing tools; use them only when they improve the composition.
|
|
487
|
+
|
|
488
|
+
### Animation functions (`@scenerok/basic-animations`)
|
|
489
|
+
|
|
490
|
+
| Function | Description |
|
|
491
|
+
|----------|-------------|
|
|
492
|
+
| `motion.fadeIn(duration?)` | Opacity 0 → 1 |
|
|
493
|
+
| `motion.fadeOut(duration?)` | Opacity 1 → 0 |
|
|
494
|
+
| `motion.slideX(from, to, dur?)` | Horizontal slide |
|
|
495
|
+
| `motion.slideY(from, to, dur?)` | Vertical slide |
|
|
496
|
+
| `motion.popIn(duration?)` | Scale + fade entrance |
|
|
497
|
+
| `motion.riseIn(duration?, dist?)` | Upward fade entrance |
|
|
498
|
+
| `motion.swingIn(duration?)` | Rotating slide/fade |
|
|
499
|
+
| `motion.glitchIn(duration?)` | Jitter + flash entrance |
|
|
500
|
+
| `motion.float(duration?, amp?)` | Gentle vertical bob |
|
|
501
|
+
| `motion.typewriter(duration?)` | Character reveal |
|
|
502
|
+
|
|
503
|
+
### Render architecture (contributors)
|
|
504
|
+
|
|
505
|
+
Implemented registry-based rendering. See `plan-v1/35-render-subsystem-architecture.md`.
|
|
506
|
+
|
|
507
|
+
| Registry | Path | Add via |
|
|
508
|
+
|----------|------|---------|
|
|
509
|
+
| Motion | `src/lib/motion/registry.ts` | `registerEasing`, `registerMotionBehavior` |
|
|
510
|
+
| Surface | `src/render/surfaces/registry.ts` | `registerSurfaceRenderer` |
|
|
511
|
+
| Effect | `src/render/effects/registry.ts` | `registerEffect` or `src/shaders/library.ts` |
|
|
512
|
+
| Engine | `src/render/engine/index.ts` | `createRenderEngine` backends |
|
|
513
|
+
|
|
514
|
+
For SceneRok platform engineering, load the **`scenerok-development`** skill (`.agents/skills/scenerok-development/`).
|
|
515
|
+
|
|
516
|
+
## Expressions
|
|
517
|
+
|
|
518
|
+
Used in time specs, `let` values, and function arguments:
|
|
519
|
+
|
|
520
|
+
```vidscript
|
|
521
|
+
let x = 5s # Time literals
|
|
522
|
+
let y = x * 2 # Arithmetic
|
|
523
|
+
let prompt = "Scene: " + title_text # String concatenation
|
|
524
|
+
let z = prev + 0.5s # Playhead reference
|
|
525
|
+
let a = { primary: "#FF5733" } # Objects
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
Operators: `+`, `-`, `*`, `/` with standard precedence.
|
|
529
|
+
|
|
530
|
+
## Template Placeholders
|
|
531
|
+
|
|
532
|
+
```vidscript
|
|
533
|
+
[- 3s] = text "{{headline | Default Text}}", color: "{{color | #FFFFFF}}"
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
Placeholders are filled before parsing via `fillPlaceholders()`.
|
|
537
|
+
|
|
538
|
+
## CLI Commands
|
|
539
|
+
|
|
540
|
+
```bash
|
|
541
|
+
# Authenticate
|
|
542
|
+
scenerok auth login
|
|
543
|
+
|
|
544
|
+
# Validate a script
|
|
545
|
+
scenerok validate my-video.vid
|
|
546
|
+
|
|
547
|
+
# Render a video
|
|
548
|
+
scenerok project upload my-video.vid --assets ./assets --render --watch --download ./renders
|
|
549
|
+
scenerok project render <project-id> --watch --download ./renders
|
|
550
|
+
scenerok render my-video.vid --project-id <project-id> --watch
|
|
551
|
+
scenerok cache pull
|
|
552
|
+
|
|
553
|
+
# Check render status
|
|
554
|
+
scenerok status <render-id>
|
|
555
|
+
|
|
556
|
+
# Install agent skills
|
|
557
|
+
scenerok skills install <platform>
|
|
558
|
+
|
|
559
|
+
# Set API secrets for plugins
|
|
560
|
+
scenerok secrets set ELEVENLABS_API_KEY=your-key
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
## Best Practices
|
|
564
|
+
|
|
565
|
+
1. **Use dynamic timeblocks** — `[-]` auto-advances the relevant audio or visual cursor, reducing calculation errors
|
|
566
|
+
2. **Use `prev` for offsets** — `[prev + 0.5s .. prev + 2s]` for gaps after previous content in the same channel
|
|
567
|
+
3. **1080×1920 for vertical** (TikTok, Reels, Shorts), **1920×1080 for horizontal** (YouTube)
|
|
568
|
+
4. **Hook viewers in the first 3 seconds** — place the most compelling content early
|
|
569
|
+
5. **High-contrast text** — use `stroke` and `stroke_width` on text overlays over video
|
|
570
|
+
6. **Test with `scenerok validate` first** — catch syntax errors before spending render credits
|
|
571
|
+
7. **Keep text content short** — text disappears when the time block ends
|
|
572
|
+
8. **One instruction per line** — each instruction inside a timeblock goes on its own line
|
|
573
|
+
9. **Text-only auto blocks** — `[-] = text ...` lasts 2 seconds by default; use `[- 3s]` for custom timing
|
|
574
|
+
10. **Named arguments are clearer** — `hero.Trim(start: 0s, end: 5s)` over `hero.Trim(0s, 5s)`
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# VidScript v2 Sample — Product promo with generated video
|
|
2
|
+
|
|
3
|
+
Copy this shape for promos that use xAI-generated clips. Run `scenerok validate` before `scenerok render`.
|
|
4
|
+
|
|
5
|
+
```vidscript
|
|
6
|
+
import xai from "@scenerok/xai"
|
|
7
|
+
import motion from "@scenerok/basic-animations"
|
|
8
|
+
|
|
9
|
+
input product_name = "{{product_name | RokMilk}}"
|
|
10
|
+
input hook_text = "{{hook_text | Chocolate Milkshake}}"
|
|
11
|
+
input cta_text = "{{cta_text | visit your nearest grocery store}}"
|
|
12
|
+
|
|
13
|
+
[0s .. 2.5s] = text product_name, font: "Bebas Neue", size: 80, color: "#FFD700", x: "50%", y: "20%", align: center, stroke: "#3E1C00", stroke_width: 4, animate: motion.fadeIn(0.6s)
|
|
14
|
+
|
|
15
|
+
[2s .. 4.5s] = text hook_text, font: "Outfit", size: 52, color: "#FFFFFF", x: "50%", y: "45%", align: center, stroke: "#3E1C00", stroke_width: 3, animate: motion.riseIn(0.8s)
|
|
16
|
+
|
|
17
|
+
[-] = video xai.imagine(
|
|
18
|
+
"Premium lifestyle product shot of " + product_name + " chocolate milk in 250ml carton, cinematic lighting, 9:16 vertical, no text, no words, no letters, no captions, no logos, no watermark, no readable UI copy",
|
|
19
|
+
aspect_ratio: "9:16",
|
|
20
|
+
duration: 6
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
[8s .. 11s] = text cta_text, font: "Bebas Neue", size: 48, color: "#FFD700", x: "50%", y: "80%", align: center, stroke: "#3E1C00", stroke_width: 4, animate: motion.fadeIn(0.8s)
|
|
24
|
+
|
|
25
|
+
output to "product-promo.mp4", resolution: "1080x1920", fps: 30
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
For clips you already have (no xAI), start from `examples/system/product-launch.vid` instead.
|