npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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):

  1. Web Components (<ograf-...>): Ready-to-use Custom Elements with encapsulated Shadow DOM, responsive styling, autoplay handling, and production modes.
  2. Headless Base Classes (Ograf...Receiver): Pure UI-agnostic logic classes (extending EventTarget) 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:

  1. Root-Level Isolation: No unconditional DOM mutations or customElements.define() calls run during raw module evaluation. Importing in Node.js / SSR will not throw ReferenceError: HTMLElement is not defined.
  2. Safe Auto-Registration: When executed in a browser environment, custom elements register safely via if (typeof window !== 'undefined').
  3. Explicit Helpers: Developers who prefer fine-grained registration control can use registerOgrafCallInElement(customTagName) or registerAllComponents().
  4. 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