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

@blendvision/web-bv-communication

v0.0.1-beta.5

Published

A comprehensive communication SDK, providing audio/video communication, participant management, and remote control capabilities for real-time, multi-participant video conferencing and streaming applications.

Readme

BlendVision Communication SDK

A comprehensive communication SDK, providing audio/video communication, participant management, and remote control capabilities for real-time, multi-participant video conferencing and streaming applications.

Core Features

  • Audio/Video Communication: Audio/Video stream publishing and subscription using Real-Time Streaming.
  • Participant Management: Real-time tracking of participant join/leave events and status.
  • Role-based Permission Control: Admin/user roles for managing remote control permissions.
  • Real-time Status Monitoring: Audio/video status and volume level monitoring.
  • Canvas Rendering: Participant video rendering.
  • Remote Control: Admins can remotely mute/unmute participant audio/video via SEI messages.
  • Volume Detection: Real-time microphone volume level detection for active speaker indication.
  • Multi-Canvas Support: Dynamic creation and management of participant canvases.

Installation

Using npm/yarn

# Install dependencies
npm install @blendvision/web-bv-communication

# Or using yarn
yarn add @blendvision/web-bv-communication

Import in Your Project

import { CommunicationSdk, SdkConfig, SdkCallbacks } from '@blendvision/web-bv-communication';

Integration Guide

The SDK is designed for seamless integration into various web applications, including vanilla JavaScript, React, and other frameworks. Below are detailed steps and examples for integrating the SDK, focusing on initialization, UI integration, and state management.

1. Basic Integration (Vanilla JavaScript)

Initialize the SDK

Create an instance of CommunicationSdk with a configuration object and optional callbacks to handle events.

// Configure Sdk
const config: SdkConfig = {
  token: 'your-ivs-participant-token', // AWS IVS participant token
  role: 'admin', // or 'user'
  videoInputDeviceId: 'camera-device-id',
  audioInputDeviceId: 'microphone-device-id',
  enableInBandMessaging: true, // enable in-band messaging for remote control (default: true)
  canvasConfig: {
    width: 640,
    height: 480,
    style: { borderRadius: '8px' },
  },
};

// Define event callbacks
const callbacks: SdkCallbacks = {
  onInitialized: () => console.log('SDK initialized'),
  onConnected: () => console.log('Connected to Stage'),
  onParticipantJoined: (participantId, attributes) => console.log(`Participant joined: ${participantId}`, attributes),
  onParticipantLeft: (participantId) => console.log(`Participant left: ${participantId}`),
  onRemoteStreamAdded: (stream, participantId) => console.log(`Remote stream received: ${participantId}`),
  onParticipantVolumeChanged: (participantId, volume) => console.log(`Volume changed: ${participantId}, ${volume}`),
  onParticipantCanvasCreated: (participantId, canvas) => console.log(`Canvas created for: ${participantId}`),
  onError: (error) => console.error('SDK error:', error),
};

// Create and initialize Sdk
const sdk = new CommunicationSdk(config, callbacks);
await sdk.initialize();

Start Publishing and Control Media

// Start publishing audio/video
await sdk.startPublishing();

// Toggle video or audio
sdk.toggleVideo(); // Toggles video on/off
sdk.toggleAudio(); // Toggles audio on/off

// Explicitly set media state
sdk.setLocalAudioEnabled(true);
sdk.setLocalVideoEnabled(false);

Render Participant Canvases

Create canvases for participants to display their video or avatar.

// Create a canvas for a specific participant
const container = document.getElementById('participant-1-container') as HTMLElement;
const canvas = sdk.createParticipantCanvas('participant-1', container);

// Remove canvas when no longer needed
sdk.removeParticipantCanvas('participant-1');

2. React Integration

For React applications, use a context-based approach to manage the Sdk instance and state, ensuring clean integration with React components.

Create a Communication Context

import { FC, ReactNode, createContext, useContext, useReducer, useEffect } from 'react';
import { CommunicationSdk, SdkConfig, SdkCallbacks, ParticipantInfo } from '@blendvision/web-bv-communication';

interface CommunicationState {
  sdk: CommunicationSdk | null;
  isConnected: boolean;
  participants: ParticipantInfo[];
  error: string | null;
}

interface CommunicationAction {
  type: 'SET_SDK' | 'SET_CONNECTED' | 'ADD_PARTICIPANT' | 'REMOVE_PARTICIPANT' | 'SET_ERROR';
  payload: any;
}

const initialState: CommunicationState = {
  sdk: null,
  isConnected: false,
  participants: [],
  error: null,
};

const communicationReducer = (state: CommunicationState, action: CommunicationAction): CommunicationState => {
  switch (action.type) {
    case 'SET_SDK':
      return { ...state, sdk: action.payload };
    case 'SET_CONNECTED':
      return { ...state, isConnected: action.payload };
    case 'ADD_PARTICIPANT':
      return { ...state, participants: [...state.participants, action.payload] };
    case 'REMOVE_PARTICIPANT':
      return { ...state, participants: state.participants.filter((p) => p.id !== action.payload) };
    case 'SET_ERROR':
      return { ...state, error: action.payload };
    default:
      return state;
  }
};

const CommunicationContext = createContext<{
  state: CommunicationState;
  initializeSdk: (config: SdkConfig) => Promise<void>;
} | null>(null);

export const CommunicationProvider: FC<{ children: ReactNode }> = ({ children }) => {
  const [state, dispatch] = useReducer(communicationReducer, initialState);

  const initializeSdk = async (config: SdkConfig) => {
    try {
      const callbacks: SdkCallbacks = {
        onInitialized: () => console.log('Sdk initialized'),
        onConnected: () => dispatch({ type: 'SET_CONNECTED', payload: true }),
        onParticipantJoined: (participantId, attributes) => {
          dispatch({ type: 'ADD_PARTICIPANT', payload: { id: participantId, attributes } });
        },
        onParticipantLeft: (participantId) => {
          dispatch({ type: 'REMOVE_PARTICIPANT', payload: participantId });
        },
        onError: (error) => dispatch({ type: 'SET_ERROR', payload: error.message }),
      };

      const sdk = new CommunicationSdk(config, callbacks);
      await sdk.initialize();
      dispatch({ type: 'SET_SDK', payload: sdk });
    } catch (error) {
      dispatch({ type: 'SET_ERROR', payload: (error as Error).message });
    }
  };

  return <CommunicationContext.Provider value={{ state, initializeSdk }}>{children}</CommunicationContext.Provider>;
};

export const useCommunication = () => {
  const context = useContext(CommunicationContext);
  if (!context) {
    throw new Error('useCommunication must be used within CommunicationProvider');
  }
  return context;
};

Participant Video Component

Render participant videos or avatars using a React component.

import { useEffect, useRef, FC } from 'react';
import { useCommunication } from './CommunicationContext';

interface ParticipantVideoProps {
  participantId: string;
}

export const ParticipantVideo: FC<ParticipantVideoProps> = ({ participantId }) => {
  const { state } = useCommunication();
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (containerRef.current && state.sdk) {
      const canvas = state.sdk.createParticipantCanvas(participantId, containerRef.current);
      return () => {
        state.sdk.removeParticipantCanvas(participantId);
      };
    }
  }, [participantId, state.sdk]);

  return (
    <div
      ref={containerRef}
      style={{
        width: '100%',
        height: '200px',
        backgroundColor: '#3c4043',
        borderRadius: '8px',
        overflow: 'hidden',
      }}
    />
  );
};

Usage in a React App

import { FC } from 'react';
import { CommunicationProvider, ParticipantVideo } from './CommunicationContext';

const App: FC = () => {
  const { state, initializeSdk } = useCommunication();

  useEffect(() => {
    initializeSdk({
      token: 'your-ivs-participant-token',
      role: 'admin',
      videoInputDeviceId: 'camera-device-id',
      audioInputDeviceId: 'microphone-device-id',
    });
  }, []);

  return (
    <div>
      {state.participants.map((participant) => (
        <ParticipantVideo key={participant.id} participantId={participant.id} />
      ))}
    </div>
  );
};

export default () => (
  <CommunicationProvider>
    <App />
  </CommunicationProvider>
);

3. Integration with Other Frameworks

The Sdk is framework-agnostic and can be integrated with Vue, Angular, or other JavaScript frameworks by following similar patterns:

  • Vue: Use a reactive store (e.g., Pinia) to manage the Sdk instance and participant state, similar to the React Context example.
  • Angular: Inject the Sdk as a service, using RxJS to handle participant and stream events.
  • Svelte: Store the Sdk instance in a Svelte store, binding participant canvases to DOM elements.

4. Admin Remote Control (Admin Role Only)

Admins can control participant media states using SEI messages.

if (config.role === 'admin') {
  // Mute/unmute participant audio or video
  sdk.muteParticipantAudio('participant-id');
  sdk.unmuteParticipantAudio('participant-id');
  sdk.muteParticipantVideo('participant-id');
  sdk.unmuteParticipantVideo('participant-id');

  // Broadcast custom command
  await sdk.broadcastCommand({
    type: 'MUTE_AUDIO',
    targetParticipantId: 'participant-id',
  });
}

5. Best Practices for Integration

  • State Management: Use a centralized state (e.g., React Context, Vue Pinia) to track Sdk instance, participants, and errors.
  • Resource Cleanup: Always call sdk.leaveStage() when unmounting components or closing the app to release resources.
  • Error Handling: Implement onError to display user-friendly messages for permission or network issues.
  • Performance: Limit canvas sizes (e.g., canvasConfig: { width: 640, height: 480 }) to reduce rendering load.

Core Features and Implementation Notes

  • Canvas Rendering: Renders at 15 FPS for balanced performance. Each canvas displays participant video or initials (if no video) and a volume bar.
  • Volume Detection: Monitors volume every 500ms with exponential smoothing (0.6/0.4 weights) for smooth UI updates.
  • SEI Messaging: Commands (e.g., mute/unmute) are sent via video track SEI messages, requiring an active video stream.
  • Device Checking: Added pre-initialization device availability checks to prevent publishing failures.
  • State Reset: Audio/video enabled states reset to true on stopPublishing() to ensure consistent behavior.

Known Limitations

  • SEI Dependency: Remote control commands require an active video stream, limiting functionality in audio-only scenarios.
  • Browser Compatibility: Full support for Chrome 88+, Firefox 85+, Edge 88+. Safari 14+ has limited volume detection due to AudioContext restrictions.
  • Performance: Optimized for small to medium stages (up to 10 participants). Large stages may require further optimization (e.g., dynamic FPS, subscription batching).
  • Mobile Support: Not officially tested on mobile browsers; performance may vary.

API Reference

CommunicationSdk

Constructor

constructor(config: SdkConfig, callbacks?: SdkCallbacks)

Main Methods

| Method | Description | Parameters | Return Value | | --------------------------- | -------------------------------------- | ----------------------------------------------- | ----------------------------------- | | initialize() | Initialize Sdk and join stage | - | Promise<void> | | startPublishing() | Start publishing audio/video | - | Promise<void> | | stopPublishing() | Stop publishing and reset media states | - | void | | toggleVideo() | Toggle video on/off | - | void | | toggleAudio() | Toggle audio on/off | - | void | | getParticipants() | Get participants list | - | IterableIterator<ParticipantInfo> | | getSelfId() | Get self id | - | string \| null | | leaveStage() | Leave stage and clean up | - | void | | muteParticipantAudio() | Mute participant audio (admin) | participantId: string | void | | unmuteParticipantAudio() | Unmute participant audio (admin) | participantId: string | void | | muteParticipantVideo() | Mute participant video (admin) | participantId: string | void | | unmuteParticipantVideo() | Unmute participant video (admin) | participantId: string | void | | getParticipantVolume() | Get participant volume | participantId: string | number | | getParticipantCanvases() | Get all participant canvases | - | Map<string, HTMLCanvasElement> | | getParticipantCanvas() | Get specific participant canvas | participantId: string | HTMLCanvasElement \| null | | createParticipantCanvas() | Create custom canvas | participantId: string, container: HTMLElement | HTMLCanvasElement | | removeParticipantCanvas() | Remove custom canvas | participantId: string | void | | broadcastCommand() | Broadcast command via SEI | command: RemoteCommand, repeatCount?: number | Promise<boolean> |

SdkConfig

interface SdkConfig {
  token: string; // Participant token
  role: 'admin' | 'user';
  videoInputDeviceId: string;
  audioInputDeviceId: string;
  enableInBandMessaging?: boolean; // Enable SEI messaging (default: true)
  canvasConfig?: {
    width?: number;
    height?: number;
    style?: Partial<CSSStyleDeclaration>;
  };
}

SdkCallbacks

interface SdkCallbacks {
  onInitialized?: () => void;
  onConnected?: () => void;
  onDisconnected?: () => void;
  onLocalStreamReady?: (stream: MediaStream) => void;
  onRemoteStreamAdded?: (stream: MediaStream, participantId: string) => void;
  onRemoteStreamRemoved?: (participantId: string) => void;
  onParticipantJoined?: (participantId: string, attributes?: Record<string, unknown>) => void;
  onParticipantLeft?: (participantId: string) => void;
  onVideoToggled?: (enabled: boolean) => void;
  onAudioToggled?: (enabled: boolean) => void;
  onPublishingStarted?: () => void;
  onPublishingStopped?: () => void;
  onError?: (error: Error) => void;
  onStageLeft?: (reason?: string) => void;
  onParticipantMuted?: (participantId: string, type: 'audio' | 'video') => void;
  onParticipantUnmuted?: (participantId: string, type: 'audio' | 'video') => void;
  onCommandReceived?: (command: RemoteCommand) => void;
  onStreamStatusChanged?: (participantId: string, status: StreamStatus) => void;
  onParticipantVolumeChanged?: (participantId: string, volume: number) => void;
  onParticipantCanvasCreated?: (participantId: string, canvas: HTMLCanvasElement) => void;
  onParticipantCanvasRemoved?: (participantId: string) => void;
}

Troubleshooting

Common Issues

Permission Errors

  • Verify browser permissions for camera/microphone.
  • Handle onError callback to display user prompts.

Connection Failures

  • Validate token and stage accessibility.
  • Monitor onDisconnected for network issues.

Canvas Rendering Issues

  • Ensure container elements have valid dimensions.
  • Check browser support for Canvas 2D context.

Volume Detection Issues

  • Ensure audio tracks are active and permissions granted.

Debugging Tips

const callbacks: SdkCallbacks = {
  onError: (error) => console.error('Sdk error:', error, { timestamp: new Date().toISOString() }),
  onParticipantVolumeChanged: (id, volume) => console.log(`Volume for ${id}: ${volume}`),
};

// Periodically log status
setInterval(() => {
  if (sdk) {
    console.log('SDK status:', {
      participants: Array.from(sdk.getParticipants()),
      canvases: sdk.getParticipantCanvases().size,
    });
  }
}, 5000);

Browser Support

  • Chrome 88+: ✅ Full support
  • Firefox 85+: ✅ Full support
  • Safari 14+: ✅ Full support
  • Edge 88+: ✅ Full support
  • Mobile Browsers: Not officially tested; expect performance limitations.

🆘 Support

For questions or suggestions, contact the development team at [email protected].