API reference
EffectsPipeline
The single public class. Hand it a MediaStream, get a MediaStream back with the effect applied. The pipeline owns its own worker, weight loading, autotune, frame transport selection, audio passthrough, and adaptive preset swaps — designed so you don’t have to wire any of that yourself.
Construction returns synchronously: pipeline.stream is wired immediately and emits the unprocessed input until the model is ready (~1–3s on cold start, depending on hardware). Use await pipeline.ready if you want to wait for the effect to actually be live before consuming the output.
const pipeline = new EffectsPipeline(inputStream, options?)
Constructor parameters
inputStream
Type: MediaStream · Required
Any video stream — webcam from getUserMedia(), screen capture from getDisplayMedia(), or a captured stream from a <video> or <canvas> element via .captureStream(). Audio tracks are passed through unchanged by default; pass audio: 'drop' to strip them, or audio: 'denoise' to clean them (see Audio denoise).
options
Type: PipelineOptions · Optional
Shape:
interface PipelineOptions {
background?: BackgroundInput // default: 'blur'
preset?: PresetName | ManualPreset // default: 'auto'
weightsBaseUrl?: string // default: cdn.longpipe.dev/.../v/0.0.4/
audio?: 'passthrough' | 'drop' | 'denoise' | { denoise: DenoiseOptions } // default: 'passthrough'
enabled?: boolean // default: true
outputResolution?: { w: number, h: number } // default: input track size
adaptive?: boolean // default: true
onReady?: () => void
onError?: (err: PipelineError) => void
}
Each property in detail:
Blur
To add a blur, you can either set background to 'blur' to use default blur settings,
{
background: 'blur'
}or you can specify an object
{
background: {
blur: {
strength?: number //0 to 1
}
}
}Image
You can set background to a url, an ImageBitmap or an <img> element
{
background: string | HTMLImageElement | ImageBitmap
}Video
You can also set a video background by specifying a video url, or a <video> element, which will play in a loop with each frame being set as the background as the video plays in a loop.
{
background: string | HTMLVideoElement
}Swappable at runtime via pipeline.setBackground(...) — no flicker, no re-init. See Backgrounds for the full input surface.
Model size selection. 'auto' runs an init-time microbenchmark and picks the largest preset whose per-frame model cost fits the frame budget (~half a 30fps frame) on the user’s hardware; while in 'auto' mode the adaptive controller (see below) can swap up or down at runtime. Explicit choices — 'fast', 'balanced', 'quality' — are always respected and never auto-overridden. Pass a ManualPreset object to pin a specific model + dtype + resolution combination. See Presets & autotune.
Where the SDK fetches model weight files. The default points at a public, versioned CDN (https://cdn.longpipe.dev/models/v/0.0.4/) — fine for prototyping. Override to self-host: offline use, locked-down corporate networks, or to avoid the public CDN dependency. The SDK fetches files of the form model_${preset}.bin. See Self-hosting weights.
Controls the output stream’s audio. 'passthrough' forwards the input’s audio tracks unmodified — useful for video-call scenarios where audio is part of the same stream. 'drop' strips audio, useful when you only care about the video (e.g., screen recording, frame export, server-side processing). 'denoise' runs the input microphone through the real-time speech denoiser — a separate AudioWorklet pipeline, independent of the video work — and outputs the cleaned track; pass { denoise: { model, postFilterBeta, gruLeak, enabled } } to configure it. See Audio denoise.
Whether the effect is active. false makes the pipeline emit the raw input stream verbatim — no model load, no inference, no compositing. Toggle at runtime via pipeline.setEnabled(true | false) — cheap to re-enable since the worker stays alive.
Dimensions of the output canvas. By default matches the input video track’s intrinsic size — preserves aspect ratio and avoids pointless rescale. Falls back to 1280×720 if the track hasn’t reported its size yet. Set explicitly to force a specific output dimension regardless of input.
Whether the SDK auto-adjusts the preset at runtime when conditions change. Only applies when preset: 'auto' — explicit preset choices are always respected. When enabled, downgrades to a smaller preset if framerate drops below target, and upgrades when modelMs shows consistent headroom (WebGPU only — WebGL upgrades are too expensive to swap live).
Fires once after the first composited frame lands. Equivalent to await pipeline.ready resolving — pick whichever pattern fits your code. Fires exactly once per pipeline lifetime.
Fires for async / runtime errors after the constructor returns: weight 404, GPU context loss (webglcontextlost or device.lost), worker crashes, adaptive preset swap failures, runtime background-resolution errors. Synchronous construction errors (no input video track, transport setup throws) propagate out of new EffectsPipeline(...) directly — wrap construction in try/catch if you want one handler for both.
Properties
pipeline.stream: MediaStream— the output stream. Available synchronously; emits passthrough video until the effect is live.pipeline.ready: Promise<void>— resolves when the worker emits its first composited frame. Optional to await.
Methods
pipeline.setBackground(bg: BackgroundInput): Promise<void>— swap the background at runtime. Same input shape as the option. No flicker, no re-init.pipeline.setEnabled(enabled: boolean): void— toggle the effect.falseputs the pipeline in passthrough mode.pipeline.setDenoise(input: boolean | DenoiseOptions): void— toggle or reconfigure audio denoise at runtime.false= passthrough; an object updates the DFN params live. Only effective whenaudio: 'denoise'was set at construction. See Audio denoise.pipeline.getAudioStats(): AudioStats | null— current denoise telemetry (model, per-hop p50/p95 ms, buffered latency, sample rate).nullwhen denoise isn’t active.pipeline.setPreset(preset: PresetName | ManualPreset): void— manually swap presets. Disables the adaptive controller.pipeline.getStats(): Promise<PipelineStats>— runtime counters (model time, fps, current preset, etc). Single postMessage round-trip.pipeline.attachPreview(canvas: HTMLCanvasElement): void— attach a second canvas for previewing candidate effects. See Preview.pipeline.setPreview({ background, fps? }): Promise<void>— render a candidate effect to the preview canvas. See Preview.pipeline.clearPreview(): void— stop previewing.pipeline.destroy(): void— terminates the worker and releases GPU resources. The pipeline is not reusable after destroy.
Preview
Show a user what a candidate effect would look like — e.g. while they browse a background menu — without changing the effect on the outgoing stream. The main output keeps its currently-applied background; a second canvas renders the candidate. The network and matte are computed once and shared between the two, so previewing adds only a second composite (not a second inference).
// 1. Attach a canvas you own. Called ONCE — control is transferred to the
// worker, so the element can't be drawn to by the page afterward. The
// worker sizes the backing to the output resolution; CSS-scale it for display.
pipeline.attachPreview(document.querySelector('#preview'))
// 2. Preview a candidate. `background` is the identical wide surface as the
// main effect (keyword, URL, ImageBitmap, { color }, { blur }, …). 'none'
// previews the no-effect (raw) option. fps throttles the preview (default 15;
// the main output stays the priority).
await pipeline.setPreview({ background: 'https://example.com/beach.jpg', fps: 15 })
// 3. Stop previewing (the canvas freezes on its last frame — hide it in your UI).
pipeline.clearPreview()
To apply a previewed candidate to the outgoing stream, just call pipeline.setBackground(sameInput) — there’s no separate “commit” call.
The preview canvas is local display only; it is not a MediaStream and is not transmitted. On WebGPU it composites directly to your canvas; on WebGL2 it’s presented via a per-frame snapshot of the render canvas (a little more expensive, which is why preview is throttled).
Error handling
onError and pipeline.ready rejection both fire for async errors after the constructor returns:
- Init failures — weight fetch 404,
normalizeBackgroundURL fetch fail, worker init exception. - GPU context loss — WebGL
webglcontextlostevent, WebGPUdevice.lostpromise. - Worker uncaught failures — pipe broken, command threw.
- Adaptive swap failures — marked
recoverable: trueon the error; pipeline keeps running on the prior preset. - Runtime background errors — e.g.,
setBackground()called with an invalid URL.
onError does not fire for synchronous constructor errors — those propagate out of new EffectsPipeline(...) directly. Wrap in try/catch if you want a single handler for both.