Skip to content

StreamCrafter — Advanced

WebCodecs encoding is auto-enabled on supported browsers (Chromium-family with RTCRtpScriptTransform). It provides background-safe encoding — the stream continues even when the browser tab loses focus.

The encoder runs in a Web Worker and uses RTCRtpScriptTransform to inject encoded frames into the WebRTC connection. This bypasses the browser’s default encoder, which pauses when the tab is backgrounded.

If you need to force standard WebRTC encoding:

// Vanilla / headless
const studio = createStreamCrafter({
whipUrl: "...",
useWebCodecs: false,
});

Override default encoding parameters for fine-grained control:

studio.setEncoderOverrides({
videoBitrate: 4_000_000, // 4 Mbps
videoFrameRate: 30,
keyFrameInterval: 60, // Keyframe every 2 seconds at 30fps
audioBitrate: 128_000, // 128 kbps
});
OverrideTypeDescription
videoBitratenumberTarget video bitrate in bps
videoFrameRatenumberTarget frame rate
keyFrameIntervalnumberFrames between keyframes
audioBitratenumberTarget audio bitrate in bps

When multiple sources are active, StreamCrafter automatically mixes their audio using the Web Audio API.

// Set volume (0.0 to 1.0)
studio.setSourceVolume("camera-1", 0.8);
studio.setSourceVolume("screen-1", 0.3);
// Mute/unmute
studio.setSourceMuted("camera-1", true);
studio.masterVolume = 0.5; // Affects all sources

The mixer applies basic audio processing to prevent clipping when mixing multiple sources. The output level is automatically normalized based on the number of active audio sources.

StreamCrafter automatically reconnects when the WHIP connection drops. Enabled by default.

const studio = createStreamCrafter({
whipUrl: "...",
reconnection: {
enabled: true,
maxAttempts: 5, // Maximum reconnection attempts
baseDelay: 1000, // Initial delay (ms)
maxDelay: 30000, // Maximum delay (ms)
backoffMultiplier: 2, // Exponential backoff factor
},
});
studio.on("reconnectionAttempt", ({ attempt, maxAttempts }) => {
console.log(`Reconnecting: attempt ${attempt} of ${maxAttempts}`);
});
studio.on("reconnectionSuccess", () => {
console.log("Reconnected!");
});
studio.on("reconnectionFailed", ({ error }) => {
console.log("Reconnection failed:", error);
});
studio.reactiveState.on("reconnecting", (isReconnecting) => {
reconnectBanner.hidden = !isReconnecting;
});

The SDK can call the public resolveIngestEndpoint GraphQL field to resolve a WHIP endpoint from a stream key when that resolver is enabled in your environment:

const studio = createStreamCrafter({
gatewayUrl: "https://bridge.frameworks.network/graphql",
streamKey: "<YOUR_STREAM_KEY>",
profile: "broadcast",
});

When enabled, the resolver can choose an ingest node based on:

  • Geographic proximity
  • Node load and capacity
  • Stream configuration

Run Gateway resolution from the publisher’s browser or device. If your application backend resolves the ingest endpoint, Foghorn scores the backend or proxy IP instead of the publisher location. For OBS, hardware encoders, or server-side publishing, prefer the dashboard DNS ingest URL (edge-ingest.{tenant}.cdn.{base} when present, then global or cluster-concrete fallback).

For custom UIs that need to resolve a WHIP URL before initializing in resolver-backed environments:

import { IngestClient } from "@livepeer-frameworks/streamcrafter-core";
const client = new IngestClient({
gatewayUrl: "https://bridge.frameworks.network/graphql",
streamKey: "my-stream-key",
});
const endpoints = await client.resolve();
const whipUrl = endpoints.primary?.whipUrl;
if (!whipUrl) throw new Error("No WHIP endpoint resolved");
// Now use the resolved URL
const studio = createStreamCrafter({ whipUrl });
// Camera with constraints
await studio.startCamera({
video: { width: 1920, height: 1080, frameRate: 30 },
audio: true,
});
// Screen share with system audio
await studio.startScreenShare({ audio: true });
// Custom MediaStream
studio.addCustomSource(customStream, "External Source");
// List active sources
const sources = studio.sources;
// [
// { id: "camera-1", type: "camera", label: "FaceTime HD", muted: false, volume: 1 },
// { id: "screen-1", type: "screen", label: "Screen", muted: false, volume: 1 }
// ]
// Set primary video (determines which source is shown in non-compositor mode)
studio.setPrimaryVideo("camera-1");
// Remove a source
studio.removeSource("screen-1");
studio.on("sourceAdded", (source) => {
console.log("New source:", source.label);
});
studio.on("sourceRemoved", ({ sourceId }) => {
console.log("Removed:", sourceId);
});
studio.on("sourceUpdated", (source) => {
console.log("Updated:", source.id, source.volume, source.muted);
});

StreamCrafter ships with 17 theme presets matching the player’s theme system.

// Switch theme at runtime
studio.theme = "tokyo-night";

All tokens use the --fw-sc-* prefix:

TokenPurpose
--fw-sc-bgBackground color
--fw-sc-surfacePanel surfaces
--fw-sc-borderBorder color
--fw-sc-textPrimary text
--fw-sc-text-mutedSecondary text
--fw-sc-accentInteractive elements
--fw-sc-successSuccess/live indicators
--fw-sc-dangerError/destructive actions
fw-streamcrafter,
.fw-sc-root {
--fw-sc-accent: 262 80% 60%;
--fw-sc-bg: 230 15% 12%;
}

Five built-in locales: en, es, fr, de, nl.

const studio = createStreamCrafter({
whipUrl: "...",
locale: "de",
});
// Or with custom translations
const studio = createStreamCrafter({
whipUrl: "...",
locale: "de",
translations: {
goLive: "Live gehen",
stopStreaming: "Stoppen",
addCamera: "Kamera hinzufugen",
},
});

Override default keyboard shortcuts:

const studio = createStreamCrafter({
whipUrl: "...",
keyMap: {
toggleStream: ["g"],
toggleMute: ["m"],
addCamera: ["c"],
shareScreen: ["s"],
},
});

Default hotkeys:

ActionDefault Key
Toggle streamingShift+Enter
Toggle mutem
Add camerac
Add screen shares
Toggle settings,