Skip to content

StreamCrafter — Web Components

NPM:

Terminal window
npm install @livepeer-frameworks/streamcrafter-wc
import "@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.

<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.

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>

The .pc property provides direct access to the underlying IngestControllerHost:

const studio = document.querySelector("fw-streamcrafter");
// Read state
studio.pc.getState(); // "idle" | "capturing" | "streaming" | ...
studio.pc.isStreaming(); // boolean
studio.pc.getSources(); // MediaSource[]
// Actions
await 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);
// Events
studio.pc.on("stateChange", ({ state }) => console.log(state));

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>

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.

ElementPurpose
<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)
AttributeTypeDefaultDescription
whip-urlstring—Direct WHIP ingest endpoint
gateway-urlstring—Gateway GraphQL endpoint for resolver-backed ingest environments
stream-keystring—Stream key used with gateway-url
initial-profilestring"broadcast"Quality preset
controlsstring""Set to "false" for headless mode
themestring""Theme preset name (empty uses the built-in default tokens)
localestring"en"UI language
enable-compositorbooleantrueEnable compositor (set el.enableCompositor = false in JS to disable)
debugbooleanfalseEnable debug logging

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);
});
<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>
app.module.ts
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";
import "@livepeer-frameworks/streamcrafter-wc/define";
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
component.html
<fw-streamcrafter [attr.whip-url]="whipUrl" initial-profile="broadcast"></fw-streamcrafter>