@depths/waves 0.1.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 +22 -0
- package/README.md +435 -0
- package/dist/chunk-TGAL5RQN.mjs +404 -0
- package/dist/chunk-WGQITADJ.mjs +284 -0
- package/dist/cli.d.mts +3 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +1107 -0
- package/dist/cli.mjs +440 -0
- package/dist/index.d.mts +89 -0
- package/dist/index.d.ts +89 -0
- package/dist/index.js +728 -0
- package/dist/index.mjs +40 -0
- package/dist/registry-hVIyqwS6.d.mts +355 -0
- package/dist/registry-hVIyqwS6.d.ts +355 -0
- package/dist/remotion/index.d.mts +11 -0
- package/dist/remotion/index.d.ts +11 -0
- package/dist/remotion/index.js +468 -0
- package/dist/remotion/index.mjs +58 -0
- package/package.json +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Depths AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
# @depths/waves
|
|
2
|
+
|
|
3
|
+
`@depths/waves` is a TypeScript-first library for rendering videos from a JSON “intermediate representation” (IR).
|
|
4
|
+
|
|
5
|
+
The intended workflow is:
|
|
6
|
+
|
|
7
|
+
1. An LLM (or a human) produces JSON that conforms to a schema (`VideoIRSchema`).
|
|
8
|
+
2. The IR is validated (schema + semantics).
|
|
9
|
+
3. The IR is rendered to an output video file (MP4 by default) using Remotion.
|
|
10
|
+
|
|
11
|
+
This project is designed around explicitness:
|
|
12
|
+
|
|
13
|
+
- You only render components that have been explicitly registered in a component registry.
|
|
14
|
+
- Component props are validated with Zod before rendering.
|
|
15
|
+
- Scene timing is validated for common authoring mistakes (gaps, overlaps, duration mismatches, children exceeding parent bounds).
|
|
16
|
+
|
|
17
|
+
## What you get (v0.1.0)
|
|
18
|
+
|
|
19
|
+
- A stable IR format (currently `version: "1.0"`) for videos composed of scenes and components.
|
|
20
|
+
- A component registry (`ComponentRegistry`) that maps `type` strings to React components + Zod props schemas + metadata.
|
|
21
|
+
- A validator (`IRValidator`) with schema + semantic checks.
|
|
22
|
+
- A rendering engine (`WavesEngine`) that bundles a Remotion project on the fly and renders to a media file.
|
|
23
|
+
- Built-in primitives: `Scene`, `Text`, `Audio`.
|
|
24
|
+
- Utilities to generate JSON Schemas for LLM prompting from registered component props.
|
|
25
|
+
|
|
26
|
+
## Why this exists
|
|
27
|
+
|
|
28
|
+
Many “LLM → video” pipelines fail for one of these reasons:
|
|
29
|
+
|
|
30
|
+
- The LLM output is not reliably validated and fails at render time.
|
|
31
|
+
- The rendering system has hidden magic (implicit component inference, brittle conventions).
|
|
32
|
+
- The model lacks a precise schema + component catalog to target.
|
|
33
|
+
|
|
34
|
+
Waves solves this by:
|
|
35
|
+
|
|
36
|
+
- Giving the model a JSON schema to follow.
|
|
37
|
+
- Enforcing strict component registration and props validation.
|
|
38
|
+
- Providing deterministic, testable semantic rules around timing.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm i @depths/waves
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Peer dependencies (must be installed by the consumer):
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm i react remotion
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Rendering prerequisites:
|
|
53
|
+
|
|
54
|
+
- `ffmpeg` must be installed and available on the PATH.
|
|
55
|
+
- A Chromium browser must be available to `@remotion/renderer` (Remotion’s renderer runs headless Chromium).
|
|
56
|
+
|
|
57
|
+
## CLI / Agent workflow
|
|
58
|
+
|
|
59
|
+
Waves ships a `waves` CLI so a locally running AI agent (or any terminal user) can:
|
|
60
|
+
|
|
61
|
+
1) fetch a system prompt + JSON Schemas (the authoring contract)
|
|
62
|
+
2) write a starter IR file
|
|
63
|
+
3) validate the IR
|
|
64
|
+
4) render an MP4 from the IR
|
|
65
|
+
|
|
66
|
+
Install locally (recommended):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm i -D @depths/waves
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Run the CLI via the local bin:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx waves --help
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Or run without installing (one-off):
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npx -y -p @depths/waves waves --help
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 1) Get the agent prompt + schemas
|
|
85
|
+
|
|
86
|
+
Machine-readable payload (recommended for agents):
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npx waves prompt --format json --out ./waves-prompt.json
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Human-readable system prompt:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx waves prompt --format text --out ./waves-system-prompt.txt
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Schemas only:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npx waves schema --kind all --pretty --out ./waves-schemas.json
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
If you register custom components at module import time, include them with repeatable `--register`:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npx waves prompt --format json --register ./src/register-waves-components.ts
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 2) Write IR JSON
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npx waves write-ir --template basic --pretty --out ./video.ir.json
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 3) Validate IR JSON
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npx waves validate --in ./video.ir.json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Structured validation result:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
npx waves validate --in ./video.ir.json --format json
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 4) Render MP4
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
npx waves render --in ./video.ir.json --out ./output.mp4 --codec h264 --crf 28 --concurrency 1
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
If your IR references `"/assets/..."` paths, pass `--publicDir` and ensure the files exist at `${publicDir}/assets/...`:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npx waves render --in ./video.ir.json --out ./output.mp4 --publicDir ./public
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Exit codes
|
|
141
|
+
|
|
142
|
+
- `0`: success
|
|
143
|
+
- `1`: usage error (invalid flags/command)
|
|
144
|
+
- `2`: validation failure (invalid JSON or IR validation errors)
|
|
145
|
+
- `3`: render failure
|
|
146
|
+
- `4`: I/O error (missing/unreadable/unwritable files)
|
|
147
|
+
- `5`: internal error (bug)
|
|
148
|
+
|
|
149
|
+
## Quickstart (one function)
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { renderVideo } from '@depths/waves';
|
|
153
|
+
|
|
154
|
+
await renderVideo(
|
|
155
|
+
{
|
|
156
|
+
version: '1.0',
|
|
157
|
+
video: { id: 'my-video', width: 1920, height: 1080, fps: 30, durationInFrames: 90 },
|
|
158
|
+
scenes: [
|
|
159
|
+
{
|
|
160
|
+
id: 'scene-1',
|
|
161
|
+
type: 'Scene',
|
|
162
|
+
timing: { from: 0, durationInFrames: 90 },
|
|
163
|
+
props: { background: { type: 'color', value: '#000000' } },
|
|
164
|
+
children: [
|
|
165
|
+
{
|
|
166
|
+
id: 'title',
|
|
167
|
+
type: 'Text',
|
|
168
|
+
timing: { from: 0, durationInFrames: 90 },
|
|
169
|
+
props: { content: 'Hello Waves', fontSize: 72, animation: 'fade' }
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
},
|
|
175
|
+
{ outputPath: './output.mp4' }
|
|
176
|
+
);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Core concepts
|
|
180
|
+
|
|
181
|
+
### 1) IR (Intermediate Representation)
|
|
182
|
+
|
|
183
|
+
The IR is plain JSON. At minimum, it includes:
|
|
184
|
+
|
|
185
|
+
- `version`: currently `"1.0"`
|
|
186
|
+
- `video`: dimensions, fps, total duration
|
|
187
|
+
- `scenes`: an array of `Scene` components
|
|
188
|
+
|
|
189
|
+
All timing is expressed in frames.
|
|
190
|
+
|
|
191
|
+
### 2) Timing model
|
|
192
|
+
|
|
193
|
+
- Every component has a `timing` object: `{ from, durationInFrames }`
|
|
194
|
+
- Scenes are expected to be sequential:
|
|
195
|
+
- Scene 0 starts at frame 0
|
|
196
|
+
- Scene N starts exactly where Scene N-1 ends
|
|
197
|
+
- Sum of scene durations must equal `video.durationInFrames`
|
|
198
|
+
- Child components (inside a `Scene`) must fit within the scene duration.
|
|
199
|
+
|
|
200
|
+
### 3) Component registry
|
|
201
|
+
|
|
202
|
+
Waves never renders arbitrary `type` strings. A component `type` must be registered:
|
|
203
|
+
|
|
204
|
+
- `type`: a string identifier, e.g. `"Text"`
|
|
205
|
+
- `component`: a React component to render
|
|
206
|
+
- `propsSchema`: a Zod schema used to validate and coerce defaults
|
|
207
|
+
- `metadata`: descriptions and examples useful for LLM prompting
|
|
208
|
+
|
|
209
|
+
### 4) Rendering engine
|
|
210
|
+
|
|
211
|
+
Rendering is done through Remotion:
|
|
212
|
+
|
|
213
|
+
1. Validate IR
|
|
214
|
+
2. Generate a temporary Remotion entry point
|
|
215
|
+
3. Bundle using `@remotion/bundler`
|
|
216
|
+
4. Select the composition using `@remotion/renderer`
|
|
217
|
+
5. Render the media to `outputPath`
|
|
218
|
+
|
|
219
|
+
Important limitation (v0.1.0):
|
|
220
|
+
|
|
221
|
+
- `WavesEngine` currently requires using `globalRegistry` for rendering, because the generated Remotion bundle needs to register the same components at bundle-time.
|
|
222
|
+
|
|
223
|
+
## API reference
|
|
224
|
+
|
|
225
|
+
### `VideoIRSchema`
|
|
226
|
+
|
|
227
|
+
Use this to validate IR generated by an LLM:
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
import { VideoIRSchema } from '@depths/waves';
|
|
231
|
+
|
|
232
|
+
const parsed = VideoIRSchema.safeParse(maybeJson);
|
|
233
|
+
if (!parsed.success) {
|
|
234
|
+
// parsed.error.issues
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### `IRValidator`
|
|
239
|
+
|
|
240
|
+
Runs both schema validation and semantic checks:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
import { IRValidator } from '@depths/waves';
|
|
244
|
+
|
|
245
|
+
const validator = new IRValidator();
|
|
246
|
+
const result = validator.validate(maybeJson);
|
|
247
|
+
if (!result.success) {
|
|
248
|
+
// result.errors: { path: string[]; message: string; code: string }[]
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Semantic checks include:
|
|
253
|
+
|
|
254
|
+
- Scene duration sum matches `video.durationInFrames` (`DURATION_MISMATCH`)
|
|
255
|
+
- Scene `from` frames are sequential (`TIMING_GAP_OR_OVERLAP`)
|
|
256
|
+
- Child components do not exceed their parent duration (`COMPONENT_EXCEEDS_PARENT`)
|
|
257
|
+
|
|
258
|
+
### `ComponentRegistry` / `globalRegistry`
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
import { ComponentRegistry, globalRegistry } from '@depths/waves';
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Typically you will use `globalRegistry` so that the rendering bundle can register and render the same types.
|
|
265
|
+
|
|
266
|
+
### `registerBuiltInComponents()`
|
|
267
|
+
|
|
268
|
+
Registers built-in primitives (`Scene`, `Text`, `Audio`) into `globalRegistry`.
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
import { registerBuiltInComponents } from '@depths/waves';
|
|
272
|
+
|
|
273
|
+
registerBuiltInComponents();
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
This function is idempotent: calling it multiple times does not double-register types.
|
|
277
|
+
|
|
278
|
+
### `renderVideo()`
|
|
279
|
+
|
|
280
|
+
Convenience wrapper:
|
|
281
|
+
|
|
282
|
+
- Registers built-ins
|
|
283
|
+
- Constructs `WavesEngine(globalRegistry, new IRValidator())`
|
|
284
|
+
- Calls `engine.render()`
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
import { renderVideo } from '@depths/waves';
|
|
288
|
+
|
|
289
|
+
await renderVideo(ir, { outputPath: './out.mp4' });
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### `WavesEngine`
|
|
293
|
+
|
|
294
|
+
Lower-level control:
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
import { WavesEngine, globalRegistry, IRValidator, registerBuiltInComponents } from '@depths/waves';
|
|
298
|
+
|
|
299
|
+
registerBuiltInComponents();
|
|
300
|
+
const engine = new WavesEngine(globalRegistry, new IRValidator());
|
|
301
|
+
await engine.render(ir, {
|
|
302
|
+
outputPath: './out.mp4',
|
|
303
|
+
codec: 'h264',
|
|
304
|
+
crf: 18,
|
|
305
|
+
concurrency: 4
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Options:
|
|
310
|
+
|
|
311
|
+
- `outputPath` (required): output file path
|
|
312
|
+
- `codec`: one of `'h264' | 'h265' | 'vp8' | 'vp9'` (default: `h264`)
|
|
313
|
+
- `crf`: quality parameter (lower is higher quality)
|
|
314
|
+
- `concurrency`: number or percentage string supported by Remotion renderer
|
|
315
|
+
- `publicDir`: optional directory for Remotion `staticFile()` assets
|
|
316
|
+
|
|
317
|
+
Advanced / internal:
|
|
318
|
+
|
|
319
|
+
- `rootDir`: temp directory root (defaults to `process.cwd()`)
|
|
320
|
+
- `registrationModules`: module specifiers to import into the Remotion bundle before rendering (useful if you register custom components at module import time)
|
|
321
|
+
|
|
322
|
+
## Asset handling
|
|
323
|
+
|
|
324
|
+
Waves supports two kinds of asset references in the IR:
|
|
325
|
+
|
|
326
|
+
1. Remote URLs: `https://...` or `http://...`
|
|
327
|
+
2. Absolute “public” paths: `/assets/foo.png`
|
|
328
|
+
|
|
329
|
+
For absolute paths:
|
|
330
|
+
|
|
331
|
+
- `Scene`/`Audio` resolve them using Remotion’s `staticFile()` which expects a path relative to the `publicDir`.
|
|
332
|
+
- Example: `src: "/assets/a.png"` becomes `staticFile("assets/a.png")`.
|
|
333
|
+
|
|
334
|
+
If you reference `"/assets/..."`, pass `publicDir` to `renderVideo()` / `engine.render()` and make sure the file exists at:
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
${publicDir}/assets/...
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Built-in components (v0.1.0)
|
|
341
|
+
|
|
342
|
+
### `Scene`
|
|
343
|
+
|
|
344
|
+
Container component for a segment of the video.
|
|
345
|
+
|
|
346
|
+
Props:
|
|
347
|
+
|
|
348
|
+
- `background` (required):
|
|
349
|
+
- `{ type: "color", value: "#RRGGBB" }`
|
|
350
|
+
- `{ type: "image", value: "/assets/bg.png" | "https://..." }`
|
|
351
|
+
- `{ type: "video", value: "/assets/bg.mp4" | "https://..." }`
|
|
352
|
+
|
|
353
|
+
Children:
|
|
354
|
+
|
|
355
|
+
- Any registered components; typically `Text` and `Audio`.
|
|
356
|
+
|
|
357
|
+
### `Text`
|
|
358
|
+
|
|
359
|
+
Animated text overlay.
|
|
360
|
+
|
|
361
|
+
Props:
|
|
362
|
+
|
|
363
|
+
- `content` (required)
|
|
364
|
+
- `fontSize` (default `48`)
|
|
365
|
+
- `color` (default `#FFFFFF`)
|
|
366
|
+
- `position` (default `"center"`) — `"top" | "center" | "bottom" | "left" | "right"`
|
|
367
|
+
- `animation` (default `"fade"`) — `"none" | "fade" | "slide" | "zoom"`
|
|
368
|
+
|
|
369
|
+
### `Audio`
|
|
370
|
+
|
|
371
|
+
Audio playback. Supports remote URLs or `staticFile()` assets.
|
|
372
|
+
|
|
373
|
+
Props:
|
|
374
|
+
|
|
375
|
+
- `src` (required)
|
|
376
|
+
- `volume` (default `1`)
|
|
377
|
+
- `startFrom` (default `0`) — trims from the beginning in frames
|
|
378
|
+
- `fadeIn` (default `0`) — fade-in duration in frames
|
|
379
|
+
- `fadeOut` (default `0`) — fade-out duration in frames
|
|
380
|
+
|
|
381
|
+
Notes:
|
|
382
|
+
|
|
383
|
+
- `fadeIn`/`fadeOut` are implemented via a `volume(frame)` curve.
|
|
384
|
+
- The engine passes an internal prop `__wavesDurationInFrames` so `fadeOut` can compute its end.
|
|
385
|
+
|
|
386
|
+
## LLM integration
|
|
387
|
+
|
|
388
|
+
### Generating a prompt schema for the model
|
|
389
|
+
|
|
390
|
+
```ts
|
|
391
|
+
import { globalRegistry, registerBuiltInComponents } from '@depths/waves';
|
|
392
|
+
|
|
393
|
+
registerBuiltInComponents();
|
|
394
|
+
const componentCatalog = globalRegistry.getJSONSchemaForLLM();
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
This returns an object keyed by component type, including:
|
|
398
|
+
|
|
399
|
+
- A JSON Schema describing the component’s props
|
|
400
|
+
- The component’s metadata (description, examples, etc.)
|
|
401
|
+
|
|
402
|
+
### Typical prompt rules (recommended)
|
|
403
|
+
|
|
404
|
+
When you prompt a model, include rules like:
|
|
405
|
+
|
|
406
|
+
- All timing is in frames (`fps` defaults to 30)
|
|
407
|
+
- Total scene durations must equal `video.durationInFrames`
|
|
408
|
+
- Scene start frames must be sequential with no gaps/overlaps
|
|
409
|
+
- Asset paths are either full URLs or absolute paths like `/assets/foo.png`
|
|
410
|
+
|
|
411
|
+
## Troubleshooting
|
|
412
|
+
|
|
413
|
+
### Rendering fails with browser/Chromium errors
|
|
414
|
+
|
|
415
|
+
Remotion renders using headless Chromium. Ensure a compatible Chromium is available to `@remotion/renderer`.
|
|
416
|
+
|
|
417
|
+
### Rendering fails with ffmpeg errors
|
|
418
|
+
|
|
419
|
+
Install `ffmpeg` and ensure it is on the PATH.
|
|
420
|
+
|
|
421
|
+
### Unknown component type
|
|
422
|
+
|
|
423
|
+
If the engine throws “Unknown component type”:
|
|
424
|
+
|
|
425
|
+
- Ensure you called `registerBuiltInComponents()`
|
|
426
|
+
- If it’s a custom component, ensure it is registered into `globalRegistry`
|
|
427
|
+
|
|
428
|
+
### Props validation errors
|
|
429
|
+
|
|
430
|
+
Props are validated using the registered Zod schema. Defaults are applied by Zod on parse.
|
|
431
|
+
|
|
432
|
+
## License notes
|
|
433
|
+
|
|
434
|
+
- This package is MIT licensed (see `LICENSE`).
|
|
435
|
+
- Remotion has separate licensing terms; ensure you comply with Remotion’s license for your use case.
|