No downloads, instant streaming from any browser
Everything you can do with browser streaming
Stream your webcam with configurable resolution and audio
Share your entire screen, window, or browser tab
Overlay webcam on screen share for presenter mode
Stream games, animations, or custom graphics
Replace or blur your background in real-time
Combine multiple video sources into one stream
Stream your webcam in 5 lines of code
import { WaveClient } from '@wave/browser-sdk';
const wave = new WaveClient({
apiKey: 'wave_pub_xxxxx' // Public key, safe for browser
});
// Request webcam access
const mediaStream = await navigator.mediaDevices.getUserMedia({
video: { width: 1920, height: 1080, frameRate: 30 },
audio: { echoCancellation: true, noiseSuppression: true }
});
// Create and start stream
const stream = await wave.streams.createFromMediaStream(mediaStream, {
title: 'My Webcam Stream',
protocol: 'webrtc',
visibility: 'public' // or 'private', 'unlisted'
});
console.log('Streaming at:', stream.url);
console.log('Share this link with viewers!');
// Display preview locally
const video = document.getElementById('preview');
video.srcObject = mediaStream;
video.play();Share your entire screen, a window, or a browser tab
// Share entire screen with system audio (Chrome only)
const mediaStream = await navigator.mediaDevices.getDisplayMedia({
video: {
displaySurface: 'monitor', // Entire screen
cursor: 'always', // Show cursor
frameRate: { ideal: 60 } // High FPS for smooth motion
},
audio: {
systemAudio: 'include' // Capture system audio (Chrome)
},
selfBrowserSurface: 'exclude', // Don't show this browser
surfaceSwitching: 'include' // Allow switching during share
});
// Create stream
const stream = await wave.streams.createFromMediaStream(mediaStream, {
title: 'Screen Share',
protocol: 'webrtc'
});
// Handle user stopping share via browser UI
mediaStream.getVideoTracks()[0].onended = () => {
console.log('User stopped sharing');
wave.streams.stop(stream.id);
};Overlay your webcam on screen share for professional presentations
import { WaveClient, CompositeStream } from '@wave/browser-sdk';
const wave = new WaveClient({ apiKey: 'wave_pub_xxxxx' });
// Get both sources
const screenStream = await navigator.mediaDevices.getDisplayMedia({
video: { displaySurface: 'monitor' },
audio: { systemAudio: 'include' }
});
const webcamStream = await navigator.mediaDevices.getUserMedia({
video: { width: 320, height: 240 }, // Smaller for overlay
audio: true
});
// Composite them together
const composite = new CompositeStream({
width: 1920,
height: 1080,
frameRate: 30
});
// Add screen as background
composite.addSource({
stream: screenStream,
layer: 0,
position: { x: 0, y: 0 },
size: { width: 1920, height: 1080 }
});
// Add webcam as overlay (bottom-right corner)
composite.addSource({
stream: webcamStream,
layer: 1,
position: { x: 1580, y: 820 }, // Bottom-right
size: { width: 320, height: 240 },
borderRadius: 8,
border: { width: 3, color: 'oklch(0.55 0.2 260)' } // Primary blue
});
// Mix audio from both
composite.setAudioMix({
screen: 0.7, // System audio at 70%
webcam: 1.0 // Mic at 100%
});
// Create stream from composite
const stream = await wave.streams.createFromMediaStream(
composite.getOutputStream(),
{ title: 'Presenter Mode' }
);
// Allow dragging the webcam overlay
composite.enableOverlayDrag(1); // Enable drag for layer 1Stream HTML5 games, WebGL content, or custom animations
// Stream a canvas element (games, animations, WebGL)
const canvas = document.getElementById('gameCanvas') as HTMLCanvasElement;
// Capture canvas at 60 FPS
const canvasStream = canvas.captureStream(60);
// Add audio if needed
const audioContext = new AudioContext();
const destination = audioContext.createMediaStreamDestination();
// ... connect your audio nodes to destination
canvasStream.addTrack(destination.stream.getAudioTracks()[0]);
// Create WAVE stream
const stream = await wave.streams.createFromMediaStream(canvasStream, {
title: 'Game Stream',
protocol: 'webrtc',
metadata: {
type: 'gaming',
engine: 'three.js'
}
});
// For WebGL with preserveDrawingBuffer
const gl = canvas.getContext('webgl', {
preserveDrawingBuffer: true // Required for canvas capture
});
// Performance optimization for high FPS
// Use requestVideoFrameCallback when available
if ('requestVideoFrameCallback' in HTMLVideoElement.prototype) {
// More efficient frame timing
}preserveDrawingBuffer: true is set when creating the context. Use requestVideoFrameCallback for better frame timing when available.Replace or blur your background using ML-powered segmentation
import { WaveClient, BackgroundProcessor } from '@wave/browser-sdk';
const wave = new WaveClient({ apiKey: 'wave_pub_xxxxx' });
// Get webcam
const webcamStream = await navigator.mediaDevices.getUserMedia({
video: { width: 1920, height: 1080 }
});
// Initialize background processor (uses TensorFlow.js BodyPix model)
const bgProcessor = new BackgroundProcessor({
modelUrl: 'https://cdn.wave.inc/models/bodypix',
mode: 'replace' // 'blur' | 'replace' | 'none'
});
await bgProcessor.initialize();
// Option 1: Blur background
bgProcessor.setMode('blur', { strength: 15 });
// Option 2: Replace with image
bgProcessor.setMode('replace', {
imageUrl: 'https://example.com/office-background.jpg'
});
// Option 3: Replace with video
bgProcessor.setMode('replace', {
videoUrl: 'https://example.com/animated-background.mp4'
});
// Process the stream
const processedStream = bgProcessor.process(webcamStream);
// Create WAVE stream with processed video
const stream = await wave.streams.createFromMediaStream(processedStream, {
title: 'Stream with Virtual Background'
});
// Performance stats
bgProcessor.on('performance', (stats) => {
console.log('Processing FPS:', stats.fps);
console.log('Latency:', stats.latency + 'ms');
});
// Toggle background on/off
function toggleBackground() {
const currentMode = bgProcessor.getMode();
bgProcessor.setMode(currentMode === 'none' ? 'blur' : 'none');
}Gaussian blur, adjustable strength 1-20
Static image, auto-scaled to fit
Looping video, synchronized playback
Feature support across browsers and platforms
| Browser | Webcam | Screen | System Audio | PiP | Canvas | Notes |
|---|---|---|---|---|---|---|
| Chrome 90+ | Full support, recommended | |||||
| Edge 90+ | Same as Chrome (Chromium) | |||||
| Firefox 88+ | ✗ | No system audio capture | ||||
| Safari 15+ | ✗ | Limited screen share options | ||||
| Safari iOS 15+ | ✗ | ✗ | ✗ | No screen share on iOS | ||
| Chrome Android | ✗ | Screen share requires Android 10+ |
Configure video and audio capture quality
{ width: 3840, height: 2160, frameRate: 30 }{ width: 1920, height: 1080, frameRate: 30 }{ width: 1280, height: 720, frameRate: 30 }{ width: 854, height: 480, frameRate: 30 }{ frameRate: { ideal: 60, max: 60 } }{ frameRate: { ideal: 30, max: 30 } }{ frameRate: { ideal: 24, max: 24 } }{ frameRate: { ideal: 15, max: 15 } }{ echoCancellation: true }{ noiseSuppression: true }{ autoGainControl: true }{ sampleRate: 48000, channelCount: 2 }Real-time stream health metrics
// Monitor connection quality in real-time
wave.on('connectionQuality', (quality) => {
// Quality level
console.log('Level:', quality.level); // 'excellent' | 'good' | 'fair' | 'poor'
// Metrics
console.log('Bitrate:', quality.bitrate, 'kbps');
console.log('Packet loss:', quality.packetLoss, '%');
console.log('Round-trip time:', quality.rtt, 'ms');
console.log('Jitter:', quality.jitter, 'ms');
console.log('Frames dropped:', quality.framesDropped);
// Auto-adjust quality based on connection
if (quality.level === 'poor') {
// Reduce resolution/bitrate
wave.streams.setQuality('low');
} else if (quality.level === 'excellent') {
// Increase to max quality
wave.streams.setQuality('high');
}
});
// Get current stats
const stats = await wave.streams.getStats();
console.log('Current stats:', stats);Click any issue to see diagnostic commands
Best practices for camera/microphone permissions
// Check permissions before requesting
async function checkPermissions() {
const cameraPermission = await navigator.permissions.query({ name: 'camera' });
const micPermission = await navigator.permissions.query({ name: 'microphone' });
console.log('Camera:', cameraPermission.state); // 'granted' | 'denied' | 'prompt'
console.log('Mic:', micPermission.state);
// Listen for permission changes
cameraPermission.onchange = () => {
console.log('Camera permission changed:', cameraPermission.state);
};
}
// Handle permission errors gracefully
async function getMediaWithFallback() {
try {
// Try video + audio
return await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
} catch (error) {
if (error.name === 'NotAllowedError') {
// Permission denied - show UI to explain why needed
showPermissionExplanation();
} else if (error.name === 'NotFoundError') {
// No camera/mic found - try audio only
try {
return await navigator.mediaDevices.getUserMedia({ video: false, audio: true });
} catch (audioError) {
throw new Error('No media devices available');
}
} else if (error.name === 'NotReadableError') {
// Hardware error or in use
throw new Error('Camera/microphone is in use by another application');
}
throw error;
}
}
// List available devices
async function getDevices() {
const devices = await navigator.mediaDevices.enumerateDevices();
const cameras = devices.filter(d => d.kind === 'videoinput');
const mics = devices.filter(d => d.kind === 'audioinput');
const speakers = devices.filter(d => d.kind === 'audiooutput');
console.log('Cameras:', cameras.map(c => c.label));
console.log('Microphones:', mics.map(m => m.label));
console.log('Speakers:', speakers.map(s => s.label));
return { cameras, mics, speakers };
}Click to expand implementation details
How leading companies use browser streaming
300M daily meeting participants
"Integrating WAVE's browser streaming SDK reduced our development time by 6 months. The WebRTC abstraction handles all the edge cases we used to struggle with - ICE negotiation, TURN fallbacks, adaptive bitrate. Our browser-based participants now have the same quality as native apps."
Eric Yuan
Founder & CEO, Zoom Video Communications
25M users creating video content
"WAVE powers our entire browser recording infrastructure. The SDK's MediaStream handling and automatic quality adaptation means our users can record professional videos without any plugins. Screen + webcam composite streaming just works, even on older hardware."
Joe Thomas
CEO & Co-founder, Loom
4M+ real-time collaboration sessions/month
"FigJam's live video collaboration is built on WAVE's browser streaming. The low-latency WebRTC implementation lets our users collaborate in real-time with video overlays directly in the canvas. Connection quality monitoring automatically adjusts quality so collaboration never stutters."
Dylan Field
CEO & Co-founder, Figma
Browser streaming endpoints
| Method | Path | Description | Auth |
|---|---|---|---|
POST | /v1/browser/sessions | Create browser streaming session | API Key |
POST | /v1/browser/sessions/:id/start | Start publishing to session | Session Token |
POST | /v1/browser/sessions/:id/stop | Stop browser stream | Session Token |
GET | /v1/browser/sessions/:id/stats | Get real-time connection stats | Session Token |
POST | /v1/browser/sessions/:id/quality | Set quality preset | Session Token |
GET | /v1/browser/capabilities | Check browser feature support | Public |
POST | /v1/browser/ice-servers | Get ICE server configuration | API Key |
POST | /v1/browser/composite | Create multi-source composite | Session Token |
PUT | /v1/browser/composite/:id/layout | Update composite layout | Session Token |
GET | /v1/browser/devices | List user media devices | Public |
Base URL: https://api.wave.online
See the full Browser API documentation for detailed request/response schemas.