StreamCrafter — Web Components
Installation
Section titled “Installation”NPM:
npm install @livepeer-frameworks/streamcrafter-wcimport "@livepeer-frameworks/streamcrafter-wc/define";CDN (no bundler):
<!-- unpkg --><script src="https://unpkg.com/@livepeer-frameworks/streamcrafter-wc/dist/fw-streamcrafter.iife.js"></script><!-- or jsdelivr --><script src="https://cdn.jsdelivr.net/npm/@livepeer-frameworks/streamcrafter-wc/dist/fw-streamcrafter.iife.js"></script>Shadow DOM encapsulates all CSS — host page styles won’t break controls.
Drop-in Element
Section titled “Drop-in Element”<fw-streamcrafter whip-url="https://edge-ingest.frameworks.network/webrtc/your-stream-key" initial-profile="broadcast"></fw-streamcrafter>Gateway Resolution Mode:
<fw-streamcrafter gateway-url="https://bridge.frameworks.network/graphql" stream-key="<YOUR_STREAM_KEY>" initial-profile="broadcast"></fw-streamcrafter>Gateway resolution calls the public resolveIngestEndpoint GraphQL field from the publisher’s
browser, so routing reflects the publisher’s location. For backend or encoder-side publishing, use
the dashboard DNS ingest URL or Foghorn’s /ingest/{streamKey} front door instead.
The element includes the same full UI as the React/Svelte versions: video preview, camera/screen controls, quality selection, and connection status.
Headless Mode
Section titled “Headless Mode”Set controls="false" to hide the built-in UI. The element still manages the streaming connection — you provide your own controls:
<fw-streamcrafter id="studio" whip-url="https://edge-ingest.frameworks.network/webrtc/key" controls="false"></fw-streamcrafter>
<script> const studio = document.getElementById("studio");
document.getElementById("go-live").addEventListener("click", () => { studio.pc.startStreaming(); });
document.getElementById("add-camera").addEventListener("click", () => { studio.pc.startCamera(); });</script>Controller Access
Section titled “Controller Access”The .pc property provides direct access to the underlying IngestControllerHost:
const studio = document.querySelector("fw-streamcrafter");
// Read statestudio.pc.getState(); // "idle" | "capturing" | "streaming" | ...studio.pc.isStreaming(); // booleanstudio.pc.getSources(); // MediaSource[]
// Actionsawait studio.pc.startCamera();await studio.pc.startScreenShare({ audio: true });await studio.pc.startStreaming();await studio.pc.stopStreaming();studio.pc.removeSource("camera-1");studio.pc.setSourceVolume("camera-1", 0.5);
// Eventsstudio.pc.on("stateChange", ({ state }) => console.log(state));Slot-based Composition
Section titled “Slot-based Composition”With controls="false", the element renders only its default slot, so slotted content replaces the
built-in UI while the element still manages the connection. Slotted elements are not wired to the
studio automatically; connect them through .pc as shown below.
<fw-streamcrafter id="studio" whip-url="..." controls="false"> <div class="my-controls"> <fw-sc-volume id="camera-volume"></fw-sc-volume> <button id="go-live">Go Live</button> </div></fw-streamcrafter>Standalone Sub-Components
Section titled “Standalone Sub-Components”The fw-sc-* sub-components are presentational. They do not find or bind to a
<fw-streamcrafter> on their own: the for attribute is declared but not used, and nesting them
inside the element does not connect them either. Feed them data through JS properties and act on
their events or callback properties:
<fw-streamcrafter id="studio" whip-url="..." controls="false"></fw-streamcrafter>
<fw-sc-volume id="camera-volume"></fw-sc-volume><fw-sc-compositor id="layouts"></fw-sc-compositor>
<script type="module"> const studio = document.getElementById("studio"); const volume = document.getElementById("camera-volume"); const layouts = document.getElementById("layouts");
// The controller is created during the element's first update. await studio.updateComplete; const sceneManager = () => studio.pc.getController()?.getSceneManager() ?? null;
function sync() { const sources = studio.pc.getSources(); const scene = sceneManager()?.getActiveScene(); volume.value = sources[0]?.volume ?? 1; layouts.isEnabled = !!sceneManager(); layouts.isInitialized = !!scene; layouts.sources = sources; layouts.layers = scene?.layers ?? []; layouts.currentLayout = sceneManager()?.getCurrentLayout() ?? null; }
volume.addEventListener("fw-sc-volume-change", (e) => { const [source] = studio.pc.getSources(); if (source) studio.pc.setSourceVolume(source.id, e.detail.value); }); layouts.addEventListener("fw-sc-layout-apply", (e) => { sceneManager()?.applyLayout(e.detail.layout); sync(); }); layouts.addEventListener("fw-sc-cycle-source-order", (e) => { sceneManager()?.cycleSourceOrder(e.detail.direction); sync(); });
for (const event of ["sourceAdded", "sourceRemoved", "sourceUpdated"]) { studio.pc.on(event, sync); } studio.addEventListener("fw-sc-state-change", sync);</script><fw-sc-scene-switcher> and <fw-sc-layer-list> follow the same pattern: set scenes /
activeSceneId or layers / sources, and handle actions through callback properties such as
onSceneSelect, onTransitionTo, onVisibilityToggle, and onReorder.
Sub-Component Reference
Section titled “Sub-Component Reference”| Element | Purpose |
|---|---|
<fw-sc-volume> | Volume slider with mute toggle |
<fw-sc-compositor> | Layout and scaling mode selector overlay |
<fw-sc-scene-switcher> | Scene list with transition controls |
<fw-sc-layer-list> | Drag-to-reorder layer list with visibility toggles |
<fw-sc-advanced> | Advanced settings panel (WebCodecs, encoder, audio processing) |
Attributes Reference
Section titled “Attributes Reference”| Attribute | Type | Default | Description |
|---|---|---|---|
whip-url | string | — | Direct WHIP ingest endpoint |
gateway-url | string | — | Gateway GraphQL endpoint for resolver-backed ingest environments |
stream-key | string | — | Stream key used with gateway-url |
initial-profile | string | "broadcast" | Quality preset |
controls | string | "" | Set to "false" for headless mode |
theme | string | "" | Theme preset name (empty uses the built-in default tokens) |
locale | string | "en" | UI language |
enable-compositor | boolean | true | Enable compositor (set el.enableCompositor = false in JS to disable) |
debug | boolean | false | Enable debug logging |
Events
Section titled “Events”Listen for events on the element:
const studio = document.querySelector("fw-streamcrafter");
studio.addEventListener("fw-sc-state-change", (e) => { console.log("State:", e.detail.state);});Using with Vue
Section titled “Using with Vue”<template> <fw-streamcrafter :whip-url="whipUrl" initial-profile="broadcast" /></template>
<script setup>import "@livepeer-frameworks/streamcrafter-wc/define";import { ref } from "vue";
const whipUrl = ref("https://edge-ingest.frameworks.network/webrtc/my-key");</script>Using with Angular
Section titled “Using with Angular”import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";import "@livepeer-frameworks/streamcrafter-wc/define";
@NgModule({ schemas: [CUSTOM_ELEMENTS_SCHEMA],})export class AppModule {}<fw-streamcrafter [attr.whip-url]="whipUrl" initial-profile="broadcast"></fw-streamcrafter>