npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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 inputVideo element must already be playing with available metadata (non-zero videoWidth/videoHeight) before constructing the VirtualBackground, 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 unchanged

Hosting 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.tflite

To 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/. run npm 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:

  1. Get the camera track and apply the virtual background to it.
  2. Take the processed MediaStreamTrack from the returned HTMLVideoElement.
  3. 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();