VideoFlowcodeGitHubTry itCoreRenderersReact Video EditorPlaygroundExamplesDocscodeGitHubTry it
Getting started
InstallationQuick startCore conceptsYour first video
Builder
Builder APITime formatsParallel & wait
Layers
TextImageVideoAudioCaptionsShapeGroups
Animation
Animate & keyframesEasing functionsTransitionsEffects
Renderers
Browser rendererServer rendererDOM preview
React Video Editor
QuickstartThemingUploadsCustom panelsHooks & commandsKeyboard shortcuts
API reference
Overview@videoflow/core@videoflow/renderer-browser@videoflow/renderer-server@videoflow/renderer-dom@videoflow/react-video-editor
API reference

@videoflow/renderer-dom

Live, scrubbable player that paints VideoJSON into a DOM host.

Classes

class

DomRenderer

Methods

constructor
(host: HTMLElement): DomRenderer
Parameters
NameTypeDescription
hostHTMLElement
Returns
DomRenderer
addLayer
(layerJSON: LayerJSON, index?: number): Promise<void>

Insert a new layer into the video at the given index (defaults to end). The new layer is constructed, initialized, and mounted into the existing `$canvas` — other layers' DOM elements are left untouched.

Parameters
NameTypeDescription
layerJSONLayerJSONThe layer JSON to add. Must include a unique `id`.
index optionalnumberThe insertion index in `this.layers`. Defaults to appending.
Returns
Promise<void>
compositeLayerInto
(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, layer: RuntimeBaseLayer, frame?: number): Promise<void>

Rasterize a single layer (cached when possible), pipe it through the WebGL effect compositor if it declares effects, and `drawImage` the result onto `ctx`. Used by `RuntimeGroupLayer.renderFrame` to flatten each child onto the group's canvas.

Parameters
NameTypeDescription
ctxCanvasRenderingContext2D | OffscreenCanvasRenderingContext2D
layerRuntimeBaseLayer
frame optionalnumber
Returns
Promise<void>
destroy
(clearShadow: boolean): void

Tear down the renderer: stop playback (also exits smooth-playback mode on every video layer), destroy every runtime layer (releases media elements, decoders, and `requestVideoFrameCallback` subscriptions), clear the per-layer effect canvases, dispose the rasterizer's per-layer surfaces and the WebGL effect compositor's GL context, and finally empty the shadow DOM. Pending queued mutations registered before destroy resolve to no-ops via the `destroyed` guard.

Parameters
NameTypeDescription
clearShadowbooleanWhether to wipe the shadow DOM. Default `true`. Pass `false` if the host element is being removed anyway and you want to keep the last rendered frame visible until removal.
getPropertyDefinition
(layerType: string): Record<string, PropertyDefinition> | undefined

Return the full propertiesDefinition for a layer type.

Parameters
NameTypeDescription
layerTypestring
Returns
Record<string, PropertyDefinition> | undefined
getVirtualLayerHost
(): Node

Where group layers should park their hidden child host. We use the shadow root so renderer CSS (which is shadow-scoped) still applies to the group's children — without this, descendants would lose their `--vw` / `--project-*` context and render at the wrong scale.

Returns
Node
loadFont
(fontName: string): Promise<void>

Load a Google Font and make it available to the document (and shadow DOM).

Parameters
NameTypeDescription
fontNamestring
Returns
Promise<void>
loadVideo
(videoJSON: VideoJSON): Promise<void>

Load a compiled VideoJSON into the renderer. Sets up the shadow DOM, creates runtime layers, initialises media, and renders frame 0.

Parameters
NameTypeDescription
videoJSONVideoJSONCompiled VideoJSON from VideoFlow.compile().
Returns
Promise<void>
play
(options: { fpsCallback?: (fps: number) => void }): Promise<void>

Start real-time playback from the current frame with audio sync. Renders audio to WAV, creates an `Audio` element, and advances frames inside a `requestAnimationFrame` loop while nudging the audio `playbackRate` to keep video and audio in sync. Each layer is switched into its smooth-playback path via RuntimeBaseLayer.enterSmoothPlayback at the start of the loop — for video layers this trades the per-frame `currentTime` seek for a native `<video>.play()` plus drift correction, eliminating the seek cost that otherwise dominates live-preview frame budget. The path is reverted on `stop()` / `seek()` so scrubbing stays frame-accurate.

Parameters
NameTypeDescription
options{ fpsCallback?: (fps: number) => void }Optional callbacks: - `fpsCallback(fps)` — fired every animation frame with the current measured render FPS, useful for a HUD/diagnostic display. To track frame changes, set the public onFrame property.
Returns
Promise<void>
removeLayer
(id: string): Promise<void>

Remove a layer from the video. Destroys the layer (releasing its media ref) and detaches its DOM element. Other layers are untouched.

Parameters
NameTypeDescription
idstring
Returns
Promise<void>
renderAudio
(): Promise<AudioBuffer | null>

Render the full audio track as an AudioBuffer.

Returns
Promise<AudioBuffer | null>
renderFrame
(frame: number, force: boolean): Promise<void>

Render a specific frame to the DOM. Skips if already at that frame (unless forced), queues if a render is already in progress.

Parameters
NameTypeDescription
framenumberFrame number to render.
forcebooleanRender even if already at this frame.
Returns
Promise<void>
reorderLayers
(orderedIds: string[]): Promise<void>

Reorder layers to match the given id sequence. The runtime layers and the backing `videoJSON.layers` array are reordered, and the layers' `$element`s are re-appended to `$canvas` in the new order. For layers without a `track`, DOM order drives paint order. Layers with a `track` carry an explicit z-index (`track + 1`) written in `applyProperties`, so reordering them in the array without changing their `track` only changes DOM order, not visual stacking. No media is touched.

Parameters
NameTypeDescription
orderedIdsstring[]The new layer order. Must contain exactly the set of currently-loaded layer ids, in any order. Extra/missing ids throw.
Returns
Promise<void>
seek
(frame: number): Promise<void>

Seek to a frame. If playback is active, stops it (which exits smooth mode), renders the target frame deterministically through the seek path, then restarts playback (re-entering smooth mode). When playback is not active, the frame is decoded by the seek path directly — pixel-deterministic, the same path used by export.

Parameters
NameTypeDescription
framenumber
Returns
Promise<void>
stop
(): void

Stop playback. Bumps the play-token (so any loop suspended in `await renderAudio()` / `requestAnimationFrame` exits without running its cleanup), tears down the `Audio` element, and reverts every layer's smooth-playback hook so the next `renderFrame` (typically from `seek()` or `currentTime =`) decodes the exact requested timestamp instead of whatever the smooth decoder happened to be presenting.

updateLayer
(id: string, patch: { animations?: Animation[]; effects?: any[]; properties?: Record<string, any>; settings?: Partial<LayerSettingsJSON>; transitionIn?: any; transitionOut?: any }): Promise<void>

Apply a property / settings / animations patch to a single layer. - `settings` is shallow-merged into `layer.json.settings`. - `properties` replaces `layer.json.properties` wholesale so keys removed by the editor (e.g. via a reset-to-default) are actually cleared. Callers should pass the full post-mutation properties object. - `animations` replaces the array wholesale (same rationale — callers hold the diffing logic because per-keyframe reconciliation is cheap to do in editor state). If `settings.source` changed, the layer's media is re-initialized via `layer.initialize()` — callers should debounce rapid source swaps. If a text layer's `fontFamily` is among the patched properties, the font is loaded into the shadow DOM before the frame is re-rendered.

Parameters
NameTypeDescription
idstringThe layer id to patch.
patch{ animations?: Animation[]; effects?: any[]; properties?: Record<string, any>; settings?: Partial<LayerSettingsJSON>; transitionIn?: any; transitionOut?: any }A partial patch. Any subset of settings/properties/animations.
Returns
Promise<void>
updateVideo
(patch: { backgroundColor?: string; duration?: number; height?: number; name?: string; width?: number }): Promise<void>

Patch top-level video properties (width, height, backgroundColor, name, duration). `fps` changes are not supported here — they invalidate frame numbers across the pipeline and require a full `loadVideo()`. `duration` is safe to update incrementally: it's only used as the loop bound in `play()` and the divisor in the `totalFrames` getter; layer frame numbers depend on `fps`, not `duration`. An in-flight `play()` call captures `durationSec` at start, so the new bound takes effect on the next `play()` invocation.

Parameters
NameTypeDescription
patch{ backgroundColor?: string; duration?: number; height?: number; name?: string; width?: number }
Returns
Promise<void>
registerEffect
(name: string, glsl: string, params: Record<string, EffectParamDefinition>): void

Register a GLSL effect. Shares its registry with `BrowserRenderer`.

Parameters
NameTypeDescription
namestring
glslstring
paramsRecord<string, EffectParamDefinition>
registerTransition
(name: string, fn: TransitionFn): void

Register a transition preset. The registry is shared with `BrowserRenderer` so a transition registered on either renderer is available in both live preview and export.

Parameters
NameTypeDescription
namestring
fnTransitionFn

Properties

NameTypeDescription
currentFramenumberCurrent frame rendered.
layerByIdMap<string, RuntimeBaseLayer>Fast id → runtime layer lookup, kept in sync with `layers`.
layersRuntimeBaseLayer[]Runtime layer instances.
onFrame(frame: number) => void | nullOptional callback fired whenever a new frame is rendered. Set this externally to keep a UI (seek bar, time label, …) in sync with playback.
playingbooleanWhether playback is active.
currentTime
duration
fps
totalFrames

Type aliases

type

DomRendererCallback

type DomRendererCallback = (event: string, data: any) => void

On this page

ClassesType aliases
VideoFlow

Open-source toolkit for composing videos from code.

Product

CoreRenderersReact Video EditorPlayground

Learn

DocsAPI referenceExamplesvs. Remotionvs. FFmpeg

Project

GitHubLicenseContactTermsPrivacy

From the blog

All posts →Beyond the Shell: Why Your Video Pipeline Should Be a TypeScript Library, Not an FFmpeg ScriptComponent-Driven Video: Mastering Layer Groups and CompositionHow to Build an In-App Video Editor with React and VideoFlowMastering GLSL Video Effects: Building Cinematic Pipelines with VideoFlowBuilding a YouTube Shorts Factory with VideoFlow and TypeScriptThe Three-Renderer Rule: Why Your Video Pipeline Needs a Single Source of TruthZero-Server Video Automation: Rendering MP4s in the Browser with WebCodecsProgrammatic Video Animation: A Deep Dive into VideoFlow Keyframes
© 2026 VideoFlow. Apache-2.0 core.