Live preview with the DOM renderer
@videoflow/renderer-dom paints a running composition into a DOM element you own. It's what the playground and the React video editor use internally. Seekable, no recompilation on each edit, and completely isolated in a shadow root.
Minimal example
import DomRenderer from '@videoflow/renderer-dom';
const host = document.getElementById('player');
const r = new DomRenderer(host);
await r.loadVideo(videoJSON);
await r.play(); // auto-loops
// Or scrub precisely:
await r.seek(42); // frame 42
await r.renderFrame(0); // paint a single frame
r.stop();
r.destroy();Methods
| Method | Effect |
|---|---|
new DomRenderer(host) | Attach to a DOM element. Creates a shadow root internally. |
.loadVideo(json) | Load or swap in a VideoJSON. Resolves when media is ready. |
.play() | Start a rAF loop. Auto-loops by default. |
.stop() | Pause at the current frame. |
.seek(frame) | Jump to a specific frame (integer). |
.renderFrame(frame) | Paint a single frame without starting a play loop. |
.destroy() | Tear down. Call on unmount. |
Properties & events
| Name | Notes |
|---|---|
onFrame: (frame) => void | Fires on every painted frame during playback. Use for a scrub bar. |
currentFrame | Read-only. Last painted frame. |
duration | Composition duration in seconds. |
fps | From the loaded video. |
Granular updates
Calling loadVideo() reloads everything. For typing-speed edits, use the incremental patch API — it skips decode for unchanged layers:
r.updateVideo({ duration: 4 }); // top-level patch
r.addLayer(newLayer); // append a layer
r.removeLayer(layerId); // drop one
r.updateLayer(layerId, { scale: 1.2 }); // patch props on an existing layer
r.reorderLayers([id3, id1, id2]); // reorderrenderFrame(n) to rasterise thumbnails, and read the canvas contents — no MP4 encode needed.