Skip to main content

Compositor API

The Compositor is a useful helper that lets you combine multiple video sources (camera, screen share, canvas, videos) into a single stream for AI processing.
Check out the with-compositor demo to see it in action!

Basic Usage

import { createCompositor } from "@daydreamlive/browser";

// Create a compositor with 512x512 output
const compositor = createCompositor({
  width: 512,
  height: 512,
});

// Add a camera source
const cameraStream = await navigator.mediaDevices.getUserMedia({ video: true });
compositor.addSource("camera", cameraStream, {
  x: 0,
  y: 0,
  width: 512,
  height: 512,
});

// Get the composited stream
const outputStream = compositor.getStream();

// Use with broadcast
const broadcast = createBroadcast({
  whipUrl,
  stream: outputStream,
});
await broadcast.connect();

Configuration

const compositor = createCompositor({
  width: 512, // Output width
  height: 512, // Output height
  frameRate: 30, // Target frame rate
  background: "#000", // Background color (CSS color or 'transparent')
});

Adding Sources

Video Source (Camera, Screen Share)

const cameraStream = await navigator.mediaDevices.getUserMedia({ video: true });

compositor.addSource("camera", cameraStream, {
  x: 0, // X position
  y: 0, // Y position
  width: 256, // Width
  height: 256, // Height
  zIndex: 1, // Layer order (higher = on top)
});

Image Source

const image = new Image();
image.src = "logo.png";
await image.decode();

compositor.addSource("logo", image, {
  x: 400,
  y: 10,
  width: 100,
  height: 100,
  zIndex: 10,
});

Canvas Source

const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");

// Draw something
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 100, 100);

compositor.addSource("canvas", canvas, {
  x: 0,
  y: 0,
  width: 512,
  height: 512,
  zIndex: 0,
});

Video Element Source

const video = document.querySelector("video#background");

compositor.addSource("video", video, {
  x: 0,
  y: 0,
  width: 512,
  height: 512,
  zIndex: 0,
});

Updating Sources

// Update position/size
compositor.updateSource("camera", {
  x: 100,
  y: 100,
  width: 300,
  height: 300,
});

// Change z-index
compositor.updateSource("logo", { zIndex: 5 });

Removing Sources

compositor.removeSource("camera");

Picture-in-Picture Example

Create a PiP layout with screen share and camera overlay:
import { createCompositor, createBroadcast } from "@daydreamlive/browser";

async function startPiP(whipUrl: string) {
  // Create compositor
  const compositor = createCompositor({
    width: 512,
    height: 512,
  });

  // Add screen share as background
  const screenStream = await navigator.mediaDevices.getDisplayMedia({
    video: { width: 1920, height: 1080 },
  });
  compositor.addSource("screen", screenStream, {
    x: 0,
    y: 0,
    width: 512,
    height: 512,
    zIndex: 0,
  });

  // Add camera as small overlay in corner
  const cameraStream = await navigator.mediaDevices.getUserMedia({
    video: { width: 320, height: 240 },
  });
  compositor.addSource("camera", cameraStream, {
    x: 352, // Bottom-right corner
    y: 352,
    width: 150,
    height: 150,
    zIndex: 1,
  });

  // Broadcast the composited stream
  const broadcast = createBroadcast({
    whipUrl,
    stream: compositor.getStream(),
  });
  await broadcast.connect();

  return { compositor, broadcast };
}

Dynamic Canvas Drawing

Combine live video with dynamic graphics:
import { createCompositor, createBroadcast } from "@daydreamlive/browser";

async function startWithOverlay(whipUrl: string) {
  const compositor = createCompositor({ width: 512, height: 512 });

  // Add camera
  const camera = await navigator.mediaDevices.getUserMedia({ video: true });
  compositor.addSource("camera", camera, {
    x: 0,
    y: 0,
    width: 512,
    height: 512,
    zIndex: 0,
  });

  // Create overlay canvas
  const overlay = document.createElement("canvas");
  overlay.width = 512;
  overlay.height = 512;
  const ctx = overlay.getContext("2d");

  compositor.addSource("overlay", overlay, {
    x: 0,
    y: 0,
    width: 512,
    height: 512,
    zIndex: 1,
  });

  // Animate the overlay
  function animate() {
    ctx.clearRect(0, 0, 512, 512);

    // Draw timestamp
    ctx.font = "24px sans-serif";
    ctx.fillStyle = "white";
    ctx.fillText(new Date().toLocaleTimeString(), 10, 30);

    requestAnimationFrame(animate);
  }
  animate();

  // Broadcast
  const broadcast = createBroadcast({
    whipUrl,
    stream: compositor.getStream(),
  });
  await broadcast.connect();

  return { compositor, broadcast };
}

Cleanup

// Stop the compositor and release resources
compositor.stop();

Next Steps