Skip to content

@ilovevideoeditor/renderer-dom — API reference

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

Classes

DomRenderer

Properties

  • currentFrame: number — Current frame rendered.
  • layerById: Map<string, RuntimeBaseLayer> — Fast id → runtime layer lookup, kept in sync with layers.
  • layers: RuntimeBaseLayer[] — Runtime layer instances.
  • onFrame: (frame: number) => void | null — Optional callback fired whenever a new frame is rendered. Set this externally to keep a UI (seek bar, time label, …) in sync with playback.
  • playing: boolean — Whether playback is active.
  • currentTime
  • duration
  • fps
  • totalFrames

Methods

  • constructor(host: HTMLElement): 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.

  • 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.
  • 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.
  • getPropertyDefinition(layerType: string): Record<string, PropertyDefinition> | undefined — Return the full propertiesDefinition for a layer type.
  • 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.
  • loadFont(fontName: string): Promise<void> — Load a Google Font and make it available to the document (and shadow DOM).
  • 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.

  • 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.

  • 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.

  • renderAudio(): Promise<AudioBuffer | null> — Render the full audio track as an AudioBuffer.
  • 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.

  • 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' $elements 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.

  • 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.

  • 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.

  • 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.

  • registerEffect(name: string, glsl: string, params: Record<string, EffectParamDefinition>): void — Register a GLSL effect. Shares its registry with BrowserRenderer.
  • 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.

Type aliases

DomRendererCallback

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

Released under the MIT License.