ograf-tools
v0.1.0
Published
Custom Web Components library for ograf tools
Readme
ograf-tools
A library of custom Web Components and headless core classes designed for browser-based graphics, WebRTC video feeds, and broadcast overlays (e.g., CasparCG, OBS Studio, vMix, and web-based video production suites).
ograf-tools is built with a dual-mode architecture and is fully isomorphic (safe for both browser and Node.js / SSR environments):
- Web Components (
<ograf-...>): Ready-to-use Custom Elements with encapsulated Shadow DOM, responsive styling, autoplay handling, and production modes. - Headless Base Classes (
Ograf...Receiver): Pure UI-agnostic logic classes (extendingEventTarget) for building custom UI implementations in React, Vue, Svelte, WebGL/Canvas, or headless Node.js.
📦 Installation
# Using yarn
yarn add ograf-tools
# Using npm
npm install ograf-tools🚀 Quick Usage
1. Using Web Components (HTML / Vanilla JS)
<!-- Load the bundled library (auto-registers elements in browser) -->
<script type="module" src="node_modules/ograf-tools/dist/ograf-tools.js"></script>
<!-- Place the element anywhere in your DOM -->
<ograf-call-in
user-id="studio-1"
video-id="cam-1"
server-url="ws://localhost:3000/tools/call-in/ws"
autoplay
></ograf-call-in>2. Using in Modern Frameworks (React, Vue, Svelte, Angular)
You can import and register components explicitly, optionally using a custom tag name:
import { registerOgrafCallInElement, registerAllComponents } from 'ograf-tools';
// Register under default tag name '<ograf-call-in>'
registerOgrafCallInElement();
// Or register under a custom tag name
registerOgrafCallInElement('my-custom-call-in');📹 Component 1: Call-In (ograf-call-in)
The Call-In tool provides real-time WebRTC audio/video receiving from a caller studio over WebSocket signaling.
Approach A: As a Web Component (<ograf-call-in>)
The <ograf-call-in> custom element renders video playback, connection status badges, loading spinners, and handles browser autoplay policies automatically.
Basic Example
<ograf-call-in
user-id="user-demo"
video-id="cam-1"
autoplay
></ograf-call-in>Broadcast / Production Overlay Example
In live broadcast overlays (OBS, CasparCG, vMix), UI badges and background colors should usually be hidden:
<ograf-call-in
user-id="user-demo"
video-id="cam-1"
production-mode
transparent
aspect-ratio="16/9"
fit="cover"
></ograf-call-in>Attributes & Properties Reference
| Attribute | Property | Type | Default | Description |
| :--- | :--- | :--- | :--- | :--- |
| user-id | userId | string | "" | User / session namespace identifier. |
| video-id | videoId | string | "" | Video stream / channel identifier. |
| server-url | serverUrl | string | Auto (current host ws) | WebSocket URL of the signaling server (ws://... or wss://...). |
| production-mode | productionMode | boolean | false | Hides header badges, status text, and controls. Renders only raw video. |
| autoplay | autoplay | boolean | true | Starts playback immediately upon stream reception with muted fallback. |
| muted | muted | boolean | false | Mutes audio playback on the video element. |
| enable-audio / disable-audio | enableAudio | boolean | true | Requests audio track from the caller stream. |
| controls | controls | boolean | false | Displays native HTML5 video player controls. |
| fit / object-fit | fit | 'cover' \| 'fit' \| 'contain' | 'cover' | Video scaling mode ('cover' fills & crops; 'contain' letterboxes). |
| aspect-ratio | aspectRatio | string | '16/9' | CSS aspect ratio (e.g. '16/9', '9/16', '1/1', '4/3', '21/9'). |
| fallback-bg / fallback-color | fallbackBg | string | 'black' | Background color when disconnected or waiting. |
| transparent | transparent | boolean | false | Sets fallback background to transparent. |
| low-res / low-bandwidth / peek | lowRes | boolean | false | Requests low-resolution / bandwidth-saving stream from caller. |
DOM Events
const el = document.querySelector('ograf-call-in');
// General status change event
el.addEventListener('status-change', (e) => {
const { status, previousStatus, message, userId, videoId, stream } = e.detail;
console.log(`Status changed to ${status}: ${message}`);
});
// Specific lifecycle events: 'streaming', 'waiting', 'connecting', 'disconnected', 'error'
el.addEventListener('streaming', (e) => {
console.log('Live video stream active:', e.detail.stream);
});Approach B: As a Base Class (OgrafCallInReceiver)
For consumers rolling their own UI in React, Vue, Svelte, custom WebGL/Canvas, or running headlessly in Node.js:
OgrafCallInReceiver encapsulates all WebSocket signaling, WebRTC peer connection negotiation, ICE candidate handling, and auto-reconnection without touching the DOM.
Example: Binding to a custom <video> element
import { OgrafCallInReceiver } from 'ograf-tools';
const videoEl = document.getElementById('my-custom-video') as HTMLVideoElement;
const receiver = new OgrafCallInReceiver({
userId: 'user-demo',
videoId: 'cam-1',
serverUrl: 'ws://localhost:3000/tools/call-in/ws',
enableAudio: true,
fit: 'cover',
aspectRatio: '16/9',
});
// Listen for incoming MediaStream
receiver.on('stream', ({ stream }) => {
videoEl.srcObject = stream;
videoEl.play();
});
// Listen for lifecycle / status events
receiver.on('status-change', (detail) => {
console.log(`[Status] ${detail.status}: ${detail.message}`);
});
// Connect to signaling server
receiver.connect();
// Later: update parameters on the fly
receiver.updateParams({ fit: 'contain', enableAudio: false });
// When done:
// receiver.disconnect();Example: React Custom Hook
import React, { useEffect, useRef, useState } from 'react';
import { OgrafCallInReceiver, OgrafCallInStatus } from 'ograf-tools';
export function CallInVideo({ userId, videoId, serverUrl }: { userId: string; videoId: string; serverUrl?: string }) {
const videoRef = useRef<HTMLVideoElement>(null);
const [status, setStatus] = useState<OgrafCallInStatus>('disconnected');
useEffect(() => {
const receiver = new OgrafCallInReceiver({ userId, videoId, serverUrl });
receiver.on('stream', ({ stream }) => {
if (videoRef.current) {
videoRef.current.srcObject = stream;
videoRef.current.play().catch(() => {});
}
});
receiver.on('status-change', (detail) => {
setStatus(detail.status);
});
receiver.connect();
return () => {
receiver.disconnect();
};
}, [userId, videoId, serverUrl]);
return (
<div className="call-in-container">
<div className={`badge badge-${status}`}>{status}</div>
<video ref={videoRef} autoPlay playsInline />
</div>
);
}Example: Headless / Node.js Usage
In Node.js or SSR environments, ograf-tools can be imported safely without DOM errors. You can optionally supply custom WebSocket and RTCPeerConnection implementations (such as ws and node-datachannel or wrtc):
import { OgrafCallInReceiver } from 'ograf-tools';
import WebSocket from 'ws';
// import { RTCPeerConnection } from 'node-datachannel/polyfill';
const receiver = new OgrafCallInReceiver({
userId: 'server-bot',
videoId: 'cam-1',
serverUrl: 'ws://localhost:3000/tools/call-in/ws',
WebSocketClass: WebSocket as any,
// RTCPeerConnectionClass: RTCPeerConnection as any,
});
receiver.on('status-change', (detail) => {
console.log('Node receiver status:', detail.status, detail.message);
});
receiver.connect();🏛️ Architecture & Best Practices
To ensure maximum versatility across environments:
- Root-Level Isolation: No unconditional DOM mutations or
customElements.define()calls run during raw module evaluation. Importing in Node.js / SSR will not throwReferenceError: HTMLElement is not defined. - Safe Auto-Registration: When executed in a browser environment, custom elements register safely via
if (typeof window !== 'undefined'). - Explicit Helpers: Developers who prefer fine-grained registration control can use
registerOgrafCallInElement(customTagName)orregisterAllComponents(). - Standard EventTarget: Base logic classes extend the native
EventTarget(standard in modern browsers and Node.js 16+), and provide both.on(event, handler)/.off(...)and standard.addEventListener(...).
🛠️ Development & Building
# Start Vite local testbed
yarn dev
# Run TypeScript typechecks and build output bundles (dist/)
yarn build
# Preview build
yarn preview