@editframe/create 0.44.0 → 0.45.1

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.
Files changed (98) hide show
  1. package/dist/index.js +16 -28
  2. package/dist/index.js.map +1 -1
  3. package/dist/skills/editframe-brand-video-generator/README.md +155 -0
  4. package/dist/skills/editframe-brand-video-generator/SKILL.md +207 -0
  5. package/dist/skills/editframe-brand-video-generator/references/brand-examples.md +178 -0
  6. package/dist/skills/editframe-brand-video-generator/references/color-psychology.md +227 -0
  7. package/dist/skills/editframe-brand-video-generator/references/composition-patterns.md +383 -0
  8. package/dist/skills/editframe-brand-video-generator/references/editing.md +66 -0
  9. package/dist/skills/editframe-brand-video-generator/references/emotional-arcs.md +496 -0
  10. package/dist/skills/editframe-brand-video-generator/references/genre-selection.md +135 -0
  11. package/dist/skills/editframe-brand-video-generator/references/transition-styles.md +611 -0
  12. package/dist/skills/editframe-brand-video-generator/references/typography-personalities.md +326 -0
  13. package/dist/skills/editframe-brand-video-generator/references/video-archetypes.md +86 -0
  14. package/dist/skills/editframe-brand-video-generator/references/video-fundamentals.md +169 -0
  15. package/dist/skills/editframe-brand-video-generator/references/visual-metaphors.md +50 -0
  16. package/dist/skills/editframe-composition/SKILL.md +169 -0
  17. package/dist/skills/editframe-composition/references/audio.md +483 -0
  18. package/dist/skills/editframe-composition/references/captions.md +844 -0
  19. package/dist/skills/editframe-composition/references/composition-model.md +73 -0
  20. package/dist/skills/editframe-composition/references/configuration.md +403 -0
  21. package/dist/skills/editframe-composition/references/css-parts.md +105 -0
  22. package/dist/skills/editframe-composition/references/css-variables.md +640 -0
  23. package/dist/skills/editframe-composition/references/entry-points.md +810 -0
  24. package/dist/skills/editframe-composition/references/events.md +499 -0
  25. package/dist/skills/editframe-composition/references/getting-started.md +259 -0
  26. package/dist/skills/editframe-composition/references/hooks.md +234 -0
  27. package/dist/skills/editframe-composition/references/image.md +241 -0
  28. package/dist/skills/editframe-composition/references/r3f.md +580 -0
  29. package/dist/skills/editframe-composition/references/render-api.md +484 -0
  30. package/dist/skills/editframe-composition/references/render-strategies.md +119 -0
  31. package/dist/skills/editframe-composition/references/render-to-video.md +1101 -0
  32. package/dist/skills/editframe-composition/references/scripting.md +606 -0
  33. package/dist/skills/editframe-composition/references/sequencing.md +116 -0
  34. package/dist/skills/editframe-composition/references/server-rendering.md +753 -0
  35. package/dist/skills/editframe-composition/references/surface.md +329 -0
  36. package/dist/skills/editframe-composition/references/text.md +627 -0
  37. package/dist/skills/editframe-composition/references/time-model.md +99 -0
  38. package/dist/skills/editframe-composition/references/timegroup-modes.md +102 -0
  39. package/dist/skills/editframe-composition/references/timegroup.md +457 -0
  40. package/dist/skills/editframe-composition/references/timeline-root.md +398 -0
  41. package/dist/skills/editframe-composition/references/transcription.md +47 -0
  42. package/dist/skills/editframe-composition/references/transitions.md +608 -0
  43. package/dist/skills/editframe-composition/references/use-media-info.md +357 -0
  44. package/dist/skills/editframe-composition/references/video.md +506 -0
  45. package/dist/skills/editframe-composition/references/waveform.md +327 -0
  46. package/dist/skills/editframe-editor-gui/SKILL.md +152 -0
  47. package/dist/skills/editframe-editor-gui/references/active-root-temporal.md +657 -0
  48. package/dist/skills/editframe-editor-gui/references/canvas.md +947 -0
  49. package/dist/skills/editframe-editor-gui/references/controls.md +366 -0
  50. package/dist/skills/editframe-editor-gui/references/dial.md +756 -0
  51. package/dist/skills/editframe-editor-gui/references/editor-toolkit.md +587 -0
  52. package/dist/skills/editframe-editor-gui/references/filmstrip.md +460 -0
  53. package/dist/skills/editframe-editor-gui/references/fit-scale.md +772 -0
  54. package/dist/skills/editframe-editor-gui/references/focus-overlay.md +561 -0
  55. package/dist/skills/editframe-editor-gui/references/hierarchy.md +544 -0
  56. package/dist/skills/editframe-editor-gui/references/overlay-item.md +634 -0
  57. package/dist/skills/editframe-editor-gui/references/overlay-layer.md +429 -0
  58. package/dist/skills/editframe-editor-gui/references/pan-zoom.md +568 -0
  59. package/dist/skills/editframe-editor-gui/references/pause.md +397 -0
  60. package/dist/skills/editframe-editor-gui/references/play.md +370 -0
  61. package/dist/skills/editframe-editor-gui/references/preview.md +391 -0
  62. package/dist/skills/editframe-editor-gui/references/resizable-box.md +749 -0
  63. package/dist/skills/editframe-editor-gui/references/scrubber.md +588 -0
  64. package/dist/skills/editframe-editor-gui/references/thumbnail-strip.md +566 -0
  65. package/dist/skills/editframe-editor-gui/references/time-display.md +492 -0
  66. package/dist/skills/editframe-editor-gui/references/timeline-ruler.md +489 -0
  67. package/dist/skills/editframe-editor-gui/references/timeline.md +604 -0
  68. package/dist/skills/editframe-editor-gui/references/toggle-loop.md +618 -0
  69. package/dist/skills/editframe-editor-gui/references/toggle-play.md +526 -0
  70. package/dist/skills/editframe-editor-gui/references/transform-handles.md +924 -0
  71. package/dist/skills/editframe-editor-gui/references/trim-handles.md +725 -0
  72. package/dist/skills/editframe-editor-gui/references/workbench.md +453 -0
  73. package/dist/skills/editframe-motion-design/SKILL.md +101 -0
  74. package/dist/skills/editframe-motion-design/references/0-editframe.md +299 -0
  75. package/dist/skills/editframe-motion-design/references/1-intent.md +201 -0
  76. package/dist/skills/editframe-motion-design/references/2-physics-model.md +405 -0
  77. package/dist/skills/editframe-motion-design/references/3-attention.md +350 -0
  78. package/dist/skills/editframe-motion-design/references/4-process.md +418 -0
  79. package/dist/skills/editframe-vite-plugin/SKILL.md +75 -0
  80. package/dist/skills/editframe-vite-plugin/references/file-api.md +111 -0
  81. package/dist/skills/editframe-vite-plugin/references/getting-started.md +96 -0
  82. package/dist/skills/editframe-vite-plugin/references/jit-transcoding.md +91 -0
  83. package/dist/skills/editframe-vite-plugin/references/local-assets.md +75 -0
  84. package/dist/skills/editframe-vite-plugin/references/visual-testing.md +136 -0
  85. package/dist/skills/editframe-webhooks/SKILL.md +126 -0
  86. package/dist/skills/editframe-webhooks/references/events.md +382 -0
  87. package/dist/skills/editframe-webhooks/references/getting-started.md +232 -0
  88. package/dist/skills/editframe-webhooks/references/security.md +418 -0
  89. package/dist/skills/editframe-webhooks/references/testing.md +409 -0
  90. package/dist/skills/editframe-webhooks/references/troubleshooting.md +457 -0
  91. package/dist/templates/html/AGENTS.md +13 -0
  92. package/dist/templates/react/AGENTS.md +13 -0
  93. package/dist/utils.js +15 -16
  94. package/dist/utils.js.map +1 -1
  95. package/package.json +1 -1
  96. package/tsdown.config.ts +4 -0
  97. package/dist/detectAgent.js +0 -89
  98. package/dist/detectAgent.js.map +0 -1
@@ -0,0 +1,484 @@
1
+ ---
2
+ title: Render API
3
+ description: Export compositions to video in the browser or via CLI with full control over resolution, bitrate, and encoding options.
4
+ type: reference
5
+ nav:
6
+ parent: "Rendering"
7
+ priority: 11
8
+ api:
9
+ methods:
10
+ - name: renderToVideo()
11
+ signature: renderToVideo(options?)
12
+ description: Export timegroup to MP4 video with WebCodecs
13
+ returns: Promise<Uint8Array | undefined>
14
+ - name: createRenderClone()
15
+ signature: createRenderClone()
16
+ description: Create off-DOM clone for rendering without affecting preview
17
+ returns: Promise<RenderCloneResult>
18
+ - name: getRenderData()
19
+ signature: getRenderData<T>()
20
+ description: Access custom data passed from CLI at render time
21
+ returns: T | undefined
22
+ ---
23
+
24
+ # Render API
25
+
26
+ Export compositions to MP4 video using browser-based WebCodecs or server-side rendering via CLI.
27
+
28
+ ## Browser Export with renderToVideo()
29
+
30
+ Export videos directly in the browser using the WebCodecs API. Best for client-side applications and interactive exports.
31
+
32
+ ### Basic Usage
33
+
34
+ ```html live
35
+ <ef-timegroup mode="fixed" duration="3s" class="w-[720px] h-[480px] bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center">
36
+ <ef-text duration="3s" class="text-white text-6xl font-bold">Hello Video!</ef-text>
37
+ </ef-timegroup>
38
+
39
+ <button id="exportBtn" class="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
40
+ Export Video
41
+ </button>
42
+
43
+ <div id="progress" class="mt-4 hidden">
44
+ <div class="text-sm text-gray-300 mb-2">
45
+ <span id="progressText">Rendering...</span>
46
+ </div>
47
+ <div class="w-full bg-gray-700 rounded-full h-2">
48
+ <div id="progressBar" class="bg-blue-600 h-2 rounded-full transition-all" style="width: 0%"></div>
49
+ </div>
50
+ <canvas id="preview" class="mt-4 border border-gray-600" style="width: 360px; height: 240px;"></canvas>
51
+ </div>
52
+
53
+ <script type="module">
54
+ const timegroup = document.querySelector('ef-timegroup');
55
+ const exportBtn = document.getElementById('exportBtn');
56
+ const progressDiv = document.getElementById('progress');
57
+ const progressBar = document.getElementById('progressBar');
58
+ const progressText = document.getElementById('progressText');
59
+ const previewCanvas = document.getElementById('preview');
60
+
61
+ exportBtn.addEventListener('click', async () => {
62
+ exportBtn.disabled = true;
63
+ progressDiv.classList.remove('hidden');
64
+
65
+ try {
66
+ await timegroup.renderToVideo({
67
+ fps: 30,
68
+ codec: 'avc',
69
+ filename: 'my-video.mp4',
70
+ onProgress: (progress) => {
71
+ const percent = Math.round(progress.progress * 100);
72
+ progressBar.style.width = `${percent}%`;
73
+ progressText.textContent = `Rendering frame ${progress.currentFrame}/${progress.totalFrames} (${percent}%)`;
74
+
75
+ // Show live preview of current frame
76
+ if (progress.framePreviewCanvas) {
77
+ const ctx = previewCanvas.getContext('2d');
78
+ previewCanvas.width = progress.framePreviewCanvas.width;
79
+ previewCanvas.height = progress.framePreviewCanvas.height;
80
+ ctx.drawImage(progress.framePreviewCanvas, 0, 0);
81
+ }
82
+ }
83
+ });
84
+
85
+ progressText.textContent = 'Export complete! Video downloaded.';
86
+ exportBtn.disabled = false;
87
+ } catch (error) {
88
+ progressText.textContent = `Export failed: ${error.message}`;
89
+ exportBtn.disabled = false;
90
+ }
91
+ });
92
+ </script>
93
+ ```
94
+
95
+ ### RenderToVideoOptions
96
+
97
+ All options for `renderToVideo()`:
98
+
99
+ | Option | Type | Default | Description |
100
+ |--------|------|---------|-------------|
101
+ | `fps` | `number` | `30` | Frame rate (frames per second) |
102
+ | `codec` | `string` | `"avc"` | Video codec: `"avc"`, `"hevc"`, `"vp9"`, `"av1"`, `"vp8"` |
103
+ | `bitrate` | `number` | `5_000_000` | Video bitrate in bits per second |
104
+ | `filename` | `string` | `"video.mp4"` | Download filename |
105
+ | `scale` | `number` | `1` | Rendering scale multiplier (2 = 2x resolution) |
106
+ | `keyFrameInterval` | `number` | `150` | Frames between keyframes |
107
+ | `fromMs` | `number` | `0` | Start time in milliseconds |
108
+ | `toMs` | `number` | `duration` | End time in milliseconds |
109
+ | `onProgress` | `function` | - | Progress callback (see below) |
110
+ | `streaming` | `boolean` | `false` | Stream output using File System Access API |
111
+ | `signal` | `AbortSignal` | - | AbortSignal to cancel render |
112
+ | `includeAudio` | `boolean` | `true` | Include audio tracks in output |
113
+ | `audioBitrate` | `number` | `128_000` | Audio bitrate in bits per second |
114
+ | `contentReadyMode` | `string` | `"immediate"` | `"immediate"` or `"blocking"` for video readiness |
115
+ | `blockingTimeoutMs` | `number` | `5000` | Timeout for blocking video loads |
116
+ | `returnBuffer` | `boolean` | `false` | Return Uint8Array instead of downloading |
117
+ | `preferredAudioCodecs` | `array` | `["opus", "aac"]` | Preferred audio codecs in order |
118
+ | `benchmarkMode` | `boolean` | `false` | Skip encoding for performance testing |
119
+ | `customWritableStream` | `WritableStream` | - | Custom output stream for programmatic control |
120
+ | `progressPreviewInterval` | `number` | `10` | Frames between preview updates |
121
+ | `canvasMode` | `string` | `"foreignObject"` | `"native"` or `"foreignObject"` rendering |
122
+
123
+ ### Progress Callback
124
+
125
+ The `onProgress` callback receives a `RenderProgress` object with detailed information:
126
+
127
+ ```typescript
128
+ interface RenderProgress {
129
+ progress: number; // 0.0 to 1.0
130
+ currentFrame: number; // Current frame index
131
+ totalFrames: number; // Total frames to render
132
+ renderedMs: number; // Milliseconds rendered so far
133
+ totalDurationMs: number; // Total video duration
134
+ elapsedMs: number; // Real time elapsed
135
+ estimatedRemainingMs: number; // Estimated time remaining
136
+ speedMultiplier: number; // Render speed (2.0 = 2x real-time)
137
+ framePreviewCanvas?: HTMLCanvasElement; // Preview of current frame
138
+ }
139
+ ```
140
+
141
+ ### Codec Support Matrix
142
+
143
+ Browser support varies by codec. Check availability before rendering:
144
+
145
+ | Codec | Chrome | Safari | Firefox | Notes |
146
+ |-------|--------|--------|---------|-------|
147
+ | `avc` (H.264) | ✅ | ✅ | ✅ | Best compatibility, widely supported |
148
+ | `hevc` (H.265) | ⚠️ | ✅ | ❌ | macOS/iOS only, better compression |
149
+ | `vp9` | ✅ | ❌ | ✅ | Open codec, good compression |
150
+ | `av1` | ✅ | ⚠️ | ✅ | Modern, best compression, slower encoding |
151
+ | `vp8` | ✅ | ❌ | ✅ | Legacy WebM codec |
152
+
153
+ **Recommendation:** Use `avc` for maximum compatibility or `av1` for best quality/size.
154
+
155
+ ### Audio Inclusion
156
+
157
+ Audio from `ef-video` and `ef-audio` elements is automatically mixed and included:
158
+
159
+ ```typescript
160
+ await timegroup.renderToVideo({
161
+ includeAudio: true, // Include audio tracks
162
+ audioBitrate: 192_000, // Higher quality audio (192 kbps)
163
+ preferredAudioCodecs: ['opus', 'aac'] // Codec preference order
164
+ });
165
+ ```
166
+
167
+ Audio codecs available: `opus` (best quality), `aac` (most compatible), `mp3` (legacy).
168
+
169
+ ### Streaming Output
170
+
171
+ Stream large videos to disk without loading entire file into memory:
172
+
173
+ ```typescript
174
+ await timegroup.renderToVideo({
175
+ streaming: true, // Uses File System Access API
176
+ filename: 'large-video.mp4' // User picks save location
177
+ });
178
+ ```
179
+
180
+ Requires browser support for File System Access API (Chrome 86+, Edge 86+).
181
+
182
+ ### Aborting Renders
183
+
184
+ Cancel long-running exports with AbortController:
185
+
186
+ ```typescript
187
+ const controller = new AbortController();
188
+
189
+ // Start render
190
+ const renderPromise = timegroup.renderToVideo({
191
+ signal: controller.signal,
192
+ onProgress: (progress) => {
193
+ console.log(`${Math.round(progress.progress * 100)}%`);
194
+ }
195
+ });
196
+
197
+ // Cancel after 5 seconds
198
+ setTimeout(() => controller.abort(), 5000);
199
+
200
+ try {
201
+ await renderPromise;
202
+ } catch (error) {
203
+ if (error.name === 'RenderCancelledError') {
204
+ console.log('Render was cancelled');
205
+ }
206
+ }
207
+ ```
208
+
209
+ ### Partial Exports
210
+
211
+ Export specific time ranges without modifying the composition:
212
+
213
+ ```typescript
214
+ // Export only seconds 5-15
215
+ await timegroup.renderToVideo({
216
+ fromMs: 5000,
217
+ toMs: 15000,
218
+ filename: 'clip.mp4'
219
+ });
220
+ ```
221
+
222
+ ### High-Resolution Export
223
+
224
+ Render at higher resolutions using the `scale` option:
225
+
226
+ ```typescript
227
+ // Render at 2x resolution (1440x960 from 720x480 composition)
228
+ await timegroup.renderToVideo({
229
+ scale: 2,
230
+ bitrate: 10_000_000 // Increase bitrate for higher resolution
231
+ });
232
+ ```
233
+
234
+ ### Programmatic Buffer Access
235
+
236
+ Get video data as Uint8Array instead of downloading:
237
+
238
+ ```typescript
239
+ const videoBuffer = await timegroup.renderToVideo({
240
+ returnBuffer: true,
241
+ filename: 'video.mp4'
242
+ });
243
+
244
+ // Upload to server
245
+ const formData = new FormData();
246
+ formData.append('video', new Blob([videoBuffer], { type: 'video/mp4' }));
247
+ await fetch('/api/upload', { method: 'POST', body: formData });
248
+ ```
249
+
250
+ ## Off-DOM Rendering with createRenderClone()
251
+
252
+ Create an independent clone of a timegroup for off-screen rendering. This enables rendering without affecting the user's preview position and allows concurrent renders.
253
+
254
+ ### Why Use Render Clones?
255
+
256
+ - **Non-disruptive:** Render in background without affecting preview playback
257
+ - **Concurrent:** Run multiple renders simultaneously with different clones
258
+ - **Isolated state:** Each clone has independent time position and state
259
+ - **JavaScript re-execution:** Initializer functions run on each clone
260
+
261
+ ### Basic Usage
262
+
263
+ ```typescript
264
+ // Create a render clone
265
+ const { clone, container, cleanup } = await timegroup.createRenderClone();
266
+
267
+ try {
268
+ // Clone is fully functional and independent
269
+ await clone.seekForRender(5000); // Seek to 5 seconds
270
+
271
+ // Render single frame to canvas
272
+ const canvas = await renderToImageNative(clone, 1920, 1080);
273
+
274
+ // Use canvas data...
275
+ const dataUrl = canvas.toDataURL('image/png');
276
+
277
+ } finally {
278
+ // Always clean up when done
279
+ cleanup();
280
+ }
281
+ ```
282
+
283
+ ### Automatic Clone Management
284
+
285
+ `renderToVideo()` automatically manages clones internally. You typically don't need to use `createRenderClone()` directly unless you're building custom rendering logic.
286
+
287
+ ```typescript
288
+ // This internally creates and manages a render clone
289
+ await timegroup.renderToVideo({ fps: 30 });
290
+ ```
291
+
292
+ ### Clone Factory Pattern
293
+
294
+ For compositions with JavaScript behavior, provide an initializer function that runs on both the prime timeline and all clones:
295
+
296
+ ```typescript
297
+ <ef-timegroup id="myComp" mode="sequence"></ef-timegroup>
298
+
299
+ <script type="module">
300
+ const timegroup = document.getElementById('myComp');
301
+
302
+ // This function runs on the original AND on all render clones
303
+ timegroup.initializer = (tg) => {
304
+ // Set up reactive state, register callbacks, etc.
305
+ tg.addEventListener('frame-task', (e) => {
306
+ // Update canvas, modify DOM, etc.
307
+ console.log('Frame:', e.detail.currentTimeMs);
308
+ });
309
+ };
310
+ </script>
311
+ ```
312
+
313
+ The initializer:
314
+ - **Must be synchronous** (no async/await, no Promise return)
315
+ - **Must complete quickly** (&lt;10ms warning, &lt;100ms error)
316
+ - Runs once per instance (original + each clone)
317
+ - Enables JavaScript-driven animations in renders
318
+
319
+ ## CLI Rendering
320
+
321
+ For server-side rendering, use the Editframe CLI. See the `editframe-cli` skill for full documentation.
322
+
323
+ ### Quick Render
324
+
325
+ ```bash
326
+ npx editframe render -o output.mp4
327
+ ```
328
+
329
+ ### Custom Render Data
330
+
331
+ Pass dynamic data into compositions at render time:
332
+
333
+ ```bash
334
+ npx editframe render --data '{"userName":"John","theme":"dark"}' -o video.mp4
335
+ ```
336
+
337
+ Read the data in your composition with `getRenderData()`:
338
+
339
+ ```typescript
340
+ import { getRenderData } from "@editframe/elements";
341
+
342
+ interface MyRenderData {
343
+ userName: string;
344
+ theme: "light" | "dark";
345
+ }
346
+
347
+ const data = getRenderData<MyRenderData>();
348
+ if (data) {
349
+ console.log(data.userName); // "John"
350
+ console.log(data.theme); // "dark"
351
+ }
352
+ ```
353
+
354
+ ### When to Use CLI vs Browser
355
+
356
+ **Use CLI rendering when:**
357
+ - Running on a server or CI/CD pipeline
358
+ - Need consistent encoding across platforms
359
+ - Processing videos in batch
360
+ - Require specific encoder settings not available in browsers
361
+
362
+ **Use browser rendering when:**
363
+ - Building interactive client-side applications
364
+ - Want instant preview and export without server
365
+ - Need real-time progress feedback
366
+ - Exporting user-generated content
367
+
368
+ ## Advanced: Custom Writable Streams
369
+
370
+ For fine-grained control over output, provide a custom WritableStream:
371
+
372
+ ```typescript
373
+ class VideoUploadStream extends WritableStream<Uint8Array> {
374
+ constructor() {
375
+ super({
376
+ async write(chunk) {
377
+ // Stream chunks directly to server
378
+ await fetch('/api/upload/chunk', {
379
+ method: 'POST',
380
+ body: chunk
381
+ });
382
+ },
383
+ async close() {
384
+ // Finalize upload
385
+ await fetch('/api/upload/complete', { method: 'POST' });
386
+ }
387
+ });
388
+ }
389
+ }
390
+
391
+ await timegroup.renderToVideo({
392
+ customWritableStream: new VideoUploadStream(),
393
+ returnBuffer: false
394
+ });
395
+ ```
396
+
397
+ ## Performance Tips
398
+
399
+ 1. **Use appropriate codecs:** `avc` encodes fastest, `av1` encodes slowest but smallest
400
+ 2. **Reduce resolution:** Lower resolution renders much faster
401
+ 3. **Limit audio bitrate:** High audio bitrates don't improve quality much
402
+ 4. **Use contentReadyMode: "immediate":** Skip waiting for videos to fully load
403
+ 5. **Disable progress previews:** Set high `progressPreviewInterval` or omit callback
404
+ 6. **Test codec support:** Not all codecs are hardware-accelerated on all devices
405
+
406
+ ## Browser Requirements
407
+
408
+ - **WebCodecs API** (Chrome 94+, Edge 94+, Safari 16.4+)
409
+ - **File System Access API** for streaming (Chrome 86+, Edge 86+)
410
+ - Requires HTTPS or localhost (secure context)
411
+
412
+ Check support:
413
+
414
+ ```typescript
415
+ const hasWebCodecs = 'VideoEncoder' in window && 'VideoDecoder' in window;
416
+ const hasFileSystemAccess = 'showSaveFilePicker' in window;
417
+ ```
418
+
419
+ ## Error Handling
420
+
421
+ ```typescript
422
+ try {
423
+ await timegroup.renderToVideo({ fps: 60, codec: 'av1' });
424
+ } catch (error) {
425
+ if (error.name === 'RenderCancelledError') {
426
+ console.log('User cancelled export');
427
+ } else if (error.name === 'NoSupportedAudioCodecError') {
428
+ console.log('No compatible audio codec available');
429
+ // Retry without audio
430
+ await timegroup.renderToVideo({ includeAudio: false });
431
+ } else {
432
+ console.error('Render failed:', error);
433
+ }
434
+ }
435
+ ```
436
+
437
+ ## Examples
438
+
439
+ ### Export with Custom Settings
440
+
441
+ ```typescript
442
+ await timegroup.renderToVideo({
443
+ fps: 60, // Smooth 60fps
444
+ codec: 'avc', // H.264 for compatibility
445
+ bitrate: 8_000_000, // 8 Mbps
446
+ scale: 1.5, // 1.5x resolution
447
+ includeAudio: true,
448
+ audioBitrate: 256_000, // High quality audio
449
+ filename: 'high-quality.mp4'
450
+ });
451
+ ```
452
+
453
+ ### Progress Bar with Time Estimates
454
+
455
+ ```typescript
456
+ await timegroup.renderToVideo({
457
+ onProgress: ({ progress, elapsedMs, estimatedRemainingMs, speedMultiplier }) => {
458
+ const percent = Math.round(progress * 100);
459
+ const elapsed = Math.round(elapsedMs / 1000);
460
+ const remaining = Math.round(estimatedRemainingMs / 1000);
461
+
462
+ console.log(
463
+ `${percent}% complete | ` +
464
+ `Elapsed: ${elapsed}s | ` +
465
+ `Remaining: ${remaining}s | ` +
466
+ `Speed: ${speedMultiplier.toFixed(1)}x`
467
+ );
468
+ }
469
+ });
470
+ ```
471
+
472
+ ### Export Multiple Clips
473
+
474
+ ```typescript
475
+ const clips = [
476
+ { fromMs: 0, toMs: 5000, filename: 'intro.mp4' },
477
+ { fromMs: 5000, toMs: 15000, filename: 'main.mp4' },
478
+ { fromMs: 15000, toMs: 20000, filename: 'outro.mp4' },
479
+ ];
480
+
481
+ for (const clip of clips) {
482
+ await timegroup.renderToVideo(clip);
483
+ }
484
+ ```
@@ -0,0 +1,119 @@
1
+ ---
2
+ title: Render Strategies
3
+ description: Three approaches to rendering Editframe compositions — CLI, browser-side, and cloud — with tradeoffs and when to use each.
4
+ type: explanation
5
+ nav:
6
+ parent: "Concepts"
7
+ priority: 13
8
+ related: ["render-api", "render-to-video"]
9
+ ---
10
+
11
+ # Render Strategies
12
+
13
+ Editframe compositions can be rendered through three paths. Each targets a different environment and set of constraints. The composition itself is identical across all three — only the rendering infrastructure differs.
14
+
15
+ ## CLI Render (Playwright)
16
+
17
+ The CLI spawns a local Vite dev server and a Playwright browser instance. The browser loads the composition, and frames are captured server-side using Playwright's screenshot API.
18
+
19
+ ```bash
20
+ npx editframe render -o output.mp4
21
+ ```
22
+
23
+ The CLI handles encoding, audio muxing, and output assembly. Dynamic data can be injected at render time:
24
+
25
+ ```bash
26
+ npx editframe render --data '{"title":"Q4 Report","theme":"dark"}' -o output.mp4
27
+ ```
28
+
29
+ Access the data inside the composition with `getRenderData()`:
30
+
31
+ ```html
32
+ <script type="module">
33
+ import { getRenderData } from "@editframe/elements";
34
+
35
+ const data = getRenderData();
36
+ if (data) {
37
+ document.querySelector('ef-text').textContent = data.title;
38
+ }
39
+ </script>
40
+ ```
41
+
42
+ The CLI path is suited for local development, automation scripts, and CI/CD pipelines where no browser UI is needed.
43
+
44
+ ## Browser Render (WebCodecs)
45
+
46
+ The browser path uses the WebCodecs API for video encoding and FFmpeg.wasm for muxing. The entire render runs client-side — no server is required.
47
+
48
+ ```html
49
+ <script type="module">
50
+ const timegroup = document.querySelector('ef-timegroup');
51
+
52
+ await timegroup.renderToVideo({
53
+ fps: 30,
54
+ codec: 'avc',
55
+ filename: 'export.mp4',
56
+ onProgress: (progress) => {
57
+ console.log(`${Math.round(progress.progress * 100)}%`);
58
+ }
59
+ });
60
+ </script>
61
+ ```
62
+
63
+ Browser support: Chrome 94+, Edge 94+, Safari 16.4+. Check availability at runtime:
64
+
65
+ ```html
66
+ <script type="module">
67
+ const supported = 'VideoEncoder' in window && 'VideoDecoder' in window;
68
+ </script>
69
+ ```
70
+
71
+ The browser path is suited for user-facing export features, interactive applications, and cases where you want to avoid running a backend.
72
+
73
+ ## Cloud Render (API)
74
+
75
+ The cloud path uploads the composition bundle to a server, which processes it with Playwright in a controlled environment. The server handles encoding, asset resolution, and output delivery.
76
+
77
+ ```bash
78
+ # POST composition bundle to render API
79
+ curl -X POST https://api.editframe.com/v1/renders \
80
+ -H "Authorization: Bearer $API_KEY" \
81
+ -F "bundle=@composition.tgz"
82
+ ```
83
+
84
+ Results are delivered via webhook or polling. The cloud path is suited for production workflows, scalable rendering, and environments where no browser is available.
85
+
86
+ ## Comparison
87
+
88
+ | Aspect | CLI (Playwright) | Browser (WebCodecs) | Cloud (API) |
89
+ |--------|-------------------|---------------------|-------------|
90
+ | Runs in | Local machine | User's browser | Remote server |
91
+ | Encoding | Server-side FFmpeg | WebCodecs + FFmpeg.wasm | Server-side FFmpeg |
92
+ | Audio muxing | Automatic | Automatic | Automatic |
93
+ | Dynamic data | `--data` flag | JavaScript state | Request payload |
94
+ | Progress feedback | Terminal output | `onProgress` callback | Webhook / polling |
95
+ | Browser required | Playwright (headless) | User's browser | None (server-side) |
96
+ | Codec control | Full FFmpeg options | Browser-supported codecs | Full FFmpeg options |
97
+ | Concurrent renders | Limited by local CPU | One per tab | Scales with infrastructure |
98
+
99
+ ## Architecture Differences
100
+
101
+ All three paths share the same composition format. The divergence is in how frames are captured and encoded.
102
+
103
+ **CLI and cloud** both use Playwright to load the composition in a headless browser. Playwright captures each frame as a screenshot, and an FFmpeg process encodes the image sequence into the output container. Audio is decoded and muxed separately.
104
+
105
+ **Browser render** creates an off-DOM clone of the composition, seeks it frame-by-frame, and rasterizes each frame to a canvas. The canvas frames are fed to a `VideoEncoder` instance (WebCodecs API), and the encoded chunks are muxed with FFmpeg.wasm into the final MP4.
106
+
107
+ ## Performance Characteristics
108
+
109
+ **CLI render** speed depends on the local machine. Frame capture through Playwright adds per-frame overhead, but encoding benefits from native FFmpeg and hardware acceleration where available.
110
+
111
+ **Browser render** speed depends on the user's device and browser. WebCodecs can leverage hardware encoding (GPU-accelerated H.264/H.265 on supported hardware), but the JavaScript overhead of canvas rasterization is the typical bottleneck.
112
+
113
+ **Cloud render** offloads all computation to the server. Latency comes from uploading the bundle and waiting for the job to complete, but the render itself benefits from dedicated infrastructure.
114
+
115
+ ## See Also
116
+
117
+ - [render-api.md](references/render-api.md) — full `renderToVideo()` options reference
118
+ - [render-to-video.md](references/render-to-video.md) — step-by-step browser export tutorial
119
+ - [server-rendering.md](references/server-rendering.md) — SSR-safe imports for server environments