SnapDOMGitHub★ 8K
Labs Experiment

Live video overlays from plain HTML

A scoreboard, a lower third, a stream chat: HTML and CSS composited over live video in the browser, in Chrome, Firefox and Safari. No OBS, no server.

TL;DR

The stream plugin keeps the overlay as a canvas that repaints only the boxes that changed; streamTexture uploads those rectangles to WebGL, where a shader composites them over the video and gives the look: clean, shine, CRT, hologram, glitch, or liquid glass refracting the footage under each panel. The output is a canvas. No browser flag.

Try it

Sports TV runs a broadcast package over a simulated match; press Goal or wait for one. Streamer uses your camera; Share screen puts your game behind the overlay, you in a circle. Change the look, drag the pieces, record a clip, or press Edit for the HTML and CSS.

starting

How it works

  1. The HTML is a real DOM element

    Your markup goes into a 960 by 540 stage on this page, with your CSS nested under it.

  2. stream() turns it into a canvas that repaints only what changed

    The first frame is a full SnapDOM capture. After that, a change is traced to the nearest box that can repaint on its own, and only that subtree is captured. A patched frame matches a full capture pixel for pixel.

  3. streamTexture() uploads the dirty rectangles

    On WebGL2 only the changed rectangles reach the GPU, usually a few percent of the texture per frame.

  4. The shader composites the video and the overlay

    Most looks read only the overlay texture: a light sweep, scanlines and glow, a hologram tint, glitch slices on a goal. Liquid glass also reads the layout: each frame, every .glass panel's box and border-radius become a rounded-rectangle distance field, joined by a smooth minimum with the drop under the pointer, so nearby shapes melt together. A spring makes each shape trail its box, so moving pieces stretch and wobble. Inside, the footage is sampled through a lens: pulled inward at the rim, frosted, colour-split, darkened over bright backgrounds so white text stays legible. The streamed HTML goes on top.

  5. The program output is a canvas

    It records to MP4 or WebM with recordCanvas(), or goes out as a WebRTC track with captureStream(), overlay included.

The code

The SnapDOM part is short. The rest is a full-screen fragment shader.

import { stream, streamTexture } from '@zumer/snapdom-plugins/stream'

const gl = canvas.getContext('webgl2')
const overlay = streamTexture(gl)

stream(stage, {
  embedFonts: false,
  onFrame: (frame) => overlay.update(frame), // uploads frame.rects only
})

// every animation frame: upload the video, pass each .glass box to the shader, draw
for (const el of stage.querySelectorAll('.glass')) {
  const r = el.getBoundingClientRect() // relative to the stage
  rects.push(r.left - base.left, r.top - base.top, r.width, r.height)
}

Limits

  • The glass shape is the box and its border-radius, one radius per box; other shapes need their own distance function in the shader.
  • The texture is pixels: links and buttons in it are not clickable.
  • A box over a translucent background (rgba() with alpha below 1) cannot repaint alone and forces a full repaint. Keep glass boxes free of background colour.
  • Footage from another origin needs CORS.