openvidu-virtual-background
v1.0.0
Published
Browser library to apply virtual backgrounds (blur or image) to an HTMLVideoElement. The WASM backends and TFLite segmentation models are loaded lazily from a configurable URL (a CDN by default), keeping the bundle small. No OpenVidu server or token is re
Readme
Virtual Background
NPM browser library that allows applying virtual backgrounds to an HTMLVideoElement. Returns another HTMLVideoElement with the applied VB.
No OpenVidu server and no authentication token are required to use it. The heavy assets (the WebAssembly backends and the TFLite segmentation models, ~6 MB) are not inlined into the bundle — they are downloaded lazily the first time a virtual background is applied. This keeps the JavaScript bundle tiny (~50 KB). By default the assets are fetched from a public CDN; see Hosting the model assets to self-host them.
let video = document.getElementsByTagName('video')[0];
let VB = new VirtualBackground.VirtualBackground({
id: 'virtual-background-id',
inputVideo: video,
inputResolution: '160x96',
outputFramerate: 24,
});
let videoFiltered1 = await VB.backgroundImage({ url: 'https://link-to-image.jpg' });
let videoFiltered2 = await VB.backgroundBlur();The
inputVideoelement must already be playing with available metadata (non-zerovideoWidth/videoHeight) before constructing theVirtualBackground, because its intrinsic dimensions are used to size the output canvas.
Switching effects seamlessly
backgroundBlur(), backgroundImage({ url }) and removeBackground() all return the same
output HTMLVideoElement once the pipeline has been set up. After the first call, switching between
effects only swaps the internal rendering stage while keeping the same output canvas — and therefore
the same underlying MediaStreamTrack. This means you can switch effects on the fly without having
to re-attach or re-publish the resulting track.
const output = await VB.backgroundBlur(); // first call performs the full setup
const track = output.srcObject.getVideoTracks()[0];
await VB.backgroundImage({ url: 'https://link-to-image.jpg' }); // seamless, `track` is unchanged
await VB.removeBackground(); // disables the effect (raw frame), `track` is still unchangedHosting the model assets
On the first call to backgroundBlur() / backgroundImage() the library downloads the WebAssembly
backend and the TFLite model from VirtualBackground.assetsBaseUrl, which defaults to a public CDN
(jsDelivr, pinned to the installed version):
https://cdn.jsdelivr.net/npm/openvidu-virtual-background@<version>/assets/The expected layout under that base URL is:
assets/tflite/tflite-simd.wasm
assets/tflite/tflite.wasm
assets/models/segm_full_v679.tflite
assets/models/segm_lite_v681.tfliteTo self-host (offline use, avoiding the CDN, or pinning a specific origin), copy the assets/ folder
shipped with the npm package to your web server and point the library at it before applying a
background. The host must send permissive CORS headers.
VirtualBackground.VirtualBackground.assetsBaseUrl = 'https://my-server.com/vb-assets/';Build
- At path
openvidu-virtual-background/.runnpm run clean:force && npm run bundle
This generates the bundles in the bundle/ folder: openvidu-virtual-background.js (IIFE global),
.esm.js (ESM) and .umd.js (UMD). The model assets are downloaded at runtime, not embedded, so the
bundles are small and can be served from anywhere; they do not need to be hosted by an OpenVidu server.
Using it with LiveKit
Because the processed output is a regular MediaStreamTrack, it can be published to a LiveKit Room
without any custom track processor. The flow is:
- Get the camera track and apply the virtual background to it.
- Take the processed
MediaStreamTrackfrom the returnedHTMLVideoElement. - Publish it with
room.localParticipant.publishTrack(processedTrack, { source: Track.Source.Camera }).
import { Room, Track } from 'livekit-client';
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const inputVideo = document.createElement('video');
inputVideo.srcObject = stream;
inputVideo.muted = true;
await inputVideo.play(); // ensure metadata/dimensions are available
const vb = new VirtualBackground.VirtualBackground({
id: 'livekit-cam',
inputVideo,
inputResolution: '160x96',
outputFramerate: 30,
});
const processedVideo = await vb.backgroundBlur();
const processedTrack = processedVideo.srcObject.getVideoTracks()[0];
const room = new Room();
await room.connect(LIVEKIT_URL, TOKEN);
await room.localParticipant.publishTrack(processedTrack, { source: Track.Source.Camera });
// Switch effects without republishing:
await vb.backgroundImage({ url: 'https://link-to-image.jpg' });
await vb.removeBackground();