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

@talker-network/talker-sdk

v1.1.0

Published

Talker Web SDK for push-to-talk and real-time communication

Readme

Talker Web SDK

A JavaScript/TypeScript SDK for integrating Talker's push-to-talk (PTT) and real-time communication features into web applications.

Installation

npm install @talker-network/talker-sdk

Getting Started

To use the Talker SDK, you need an SDK key. Contact [email protected] to obtain your SDK key.

Quick Start

The SDK provides two main classes:

  • TalkerAdmin - For backend/server-side operations (uses SDK key)
  • TalkerClient - For frontend/client-side operations (uses user auth token)

Backend Setup (Node.js / Next.js API Route)

import { TalkerAdmin } from '@talker-network/talker-sdk';

// Initialize with your SDK key (keep secret on server!)
const admin = new TalkerAdmin({
  sdkKey: process.env.TALKER_SDK_KEY!,
});

// Create a new user
const user = await admin.createUser({ name: 'John Doe' });

// Or get an existing user
const existingUser = await admin.setUser({ userId: 'existing-user-id' });

// Return credentials to frontend
return {
  userAuthToken: user.userAuthToken,
  userId: user.userId,
  a_username: user.aUsername,
  a_password: user.aPassword,
};

Frontend Setup (Browser)

import { TalkerClient } from '@talker-network/talker-sdk';

// Get credentials from your backend
const { userAuthToken, userId, a_username, a_password } = await fetch('/api/auth').then(r => r.json());

// Initialize client - connection starts automatically
const talker = new TalkerClient({
  userAuthToken,
  userId,
  a_username,
  a_password,
});

// Monitor connection status
talker.on('connection_change', ({ connected, status }) => {
  console.log(`Connection: ${status}`); // 'connected' | 'connecting' | 'disconnected'
});

// Listen to broadcast events
talker.on('broadcast_start', (data) => {
  console.log(`${data.senderName} started speaking`);
});

talker.on('broadcast_end', (data) => {
  console.log(`User ${data.senderId} stopped speaking`);
});

// PTT button handlers
const pttButton = document.getElementById('ptt-button');

pttButton.onmousedown = async () => {
  try {
    await talker.startTalking(channelId);
  } catch (error) {
    if (error.message === 'Establishing connection to servers') {
      // Connection in progress, retry shortly
    }
  }
};

pttButton.onmouseup = () => talker.stopTalking();

API Reference

TalkerAdmin (Backend)

Use this class on your backend server to manage users and channels.

Constructor

const admin = new TalkerAdmin({
  sdkKey: string,       // Required: Your SDK key (keep secret!)
  baseUrl?: string,     // Optional: API base URL
});

User Management

| Method | Description | Returns | |--------|-------------|---------| | createUser(config) | Create a new SDK user | Promise<UserData> | | setUser(config) | Get/switch to existing user | Promise<UserData> | | getAllUsers() | Get all users in workspace | Promise<UserInfo[]> |

// Create user
const user = await admin.createUser({
  name: 'John Doe',           // Required: Display name
  platform?: 'WEB',           // Optional: 'WEB' | 'ANDROID' | 'IOS'
  fcmToken?: 'token',         // Optional: Firebase token for push notifications
});

// Set existing user
const user = await admin.setUser({
  userId: 'user-id',          // Required: Existing user ID
  platform?: 'WEB',
  fcmToken?: 'token',
});

// Returns UserData:
// {
//   userId: string,
//   name: string,
//   userAuthToken: string,    // Pass to TalkerClient
//   aUsername: string,        // Pass to TalkerClient
//   aPassword: string,        // Pass to TalkerClient
// }

Channel Management

| Method | Description | Returns | |--------|-------------|---------| | createChannel(config) | Create a new channel | Promise<{ channelId, channelName }> | | updateChannelName(channelId, name) | Rename a channel | Promise<void> | | addParticipant(channelId, userId) | Add user to channel | Promise<void> | | removeParticipant(channelId, userId) | Remove user from channel | Promise<void> | | addAdmin(channelId, userId) | Make user a channel admin | Promise<void> | | removeAdmin(channelId, userId) | Remove admin privileges | Promise<void> |

// Create channel
const channel = await admin.createChannel({
  name: 'Team Chat',              // Required: Channel name
  participants: ['user1', 'user2'], // Required: User IDs
  type?: 'group',                 // Optional: 'group' | 'direct'
});

Credentials

| Method | Description | Returns | |--------|-------------|---------| | getCredentials() | Get SDK credentials | Promise<CredentialsData> | | getWebRTCCredentials(aUsername, aPassword) | Get WebRTC credentials | Promise<WebRTCCredentials> |


TalkerClient (Frontend)

Use this class in your frontend application for real-time PTT communication.

Constructor

const client = new TalkerClient({
  userAuthToken: string,    // Required: From backend
  userId: string,           // Required: From backend (filters own broadcasts)
  a_username: string,       // Required: From backend
  a_password: string,       // Required: From backend
  baseUrl?: string,         // Optional: API base URL
  socketUrl?: string,       // Optional: Socket.IO URL
  debug?: boolean,          // Optional: Enable debug logging (deprecated)
  loggerConfig?: {          // Optional: Logger configuration
    level?: LogLevel,       // DEBUG, INFO, WARN, ERROR, OFF
    enableConsole?: boolean,
    customHandler?: (entry: LogEntry) => void,
  },
});

Note: Connection to servers starts automatically on initialization. Monitor status via getConnectionStatus() or the connection_change event.

Connection Methods

| Method | Description | Returns | |--------|-------------|---------| | getConnectionStatus() | Get connection status | { connected: boolean, status: 'connected' \| 'connecting' \| 'disconnected' } | | isFullyConnected() | Check if ready for PTT | boolean | | connect() | Manually trigger connection | void | | reconnect() | Force reconnection | Promise<boolean> | | disconnect() | Disconnect from all services | Promise<void> |

// Check connection
const { connected, status } = talker.getConnectionStatus();

// Manual reconnection
const success = await talker.reconnect();

Push-to-Talk Methods

| Method | Description | Returns | |--------|-------------|---------| | startTalking(channelId) | Start recording and streaming | Promise<StartTalkingResult> | | stopTalking(channelId?) | Stop recording | Promise<void> | | isTalking() | Check if currently talking | boolean |

StartTalkingResult:

interface StartTalkingResult {
  success: boolean;
  code: 'success' | 'already_talking' | 'not_connected' | 'no_user_id' | 'channel_busy' | 'error';
  message: string;
}

| Code | Success | Description | |------|---------|-------------| | success | true | PTT started successfully | | already_talking | true | Already in a PTT session (no action needed) | | not_connected | false | Not connected, auto-reconnecting in background | | no_user_id | false | User ID not set (pass in constructor or call setUserId) | | channel_busy | false | Channel is busy, someone else is currently talking | | error | false | Other error (check message) |

// PTT flow with deterministic handling
const result = await talker.startTalking('channel-id');

if (!result.success) {
  switch (result.code) {
    case 'not_connected':
      // Show "Connecting..." UI, retry after connection_change event
      break;
    case 'no_user_id':
      // Developer error - userId not provided
      console.error(result.message);
      break;
    case 'channel_busy':
      // Someone else is talking, show feedback to user
      showToast('Channel is busy. Please wait...');
      break;
    case 'error':
      // Show error to user
      alert(result.message);
      break;
  }
}

// Release button
await talker.stopTalking();

Audio Control Methods

| Method | Description | Returns | |--------|-------------|---------| | setSpeakerEnabled(enabled) | Enable/disable audio playback | void | | setVolume(volume) | Set playback volume (0-1) | void | | setMicrophoneEnabled(enabled) | Enable/disable microphone | void | | getAudioLevel() | Get current mic input level (0-1) | number | | unlockAudio() | Unlock audio context (required for iOS/Safari) | Promise<void> |

// Volume control
talker.setVolume(0.8);

// Mute speaker
talker.setSpeakerEnabled(false);

// Get mic level for UI visualization
const level = talker.getAudioLevel();

Audio Device Selection

| Method | Description | Returns | |--------|-------------|---------| | getAudioInputDevices() | List available microphones | Promise<MediaDeviceInfo[]> | | getAudioOutputDevices() | List available speakers | Promise<MediaDeviceInfo[]> | | setAudioInputDevice(deviceId) | Select microphone | Promise<void> | | setAudioOutputDevice(deviceId) | Select speaker | Promise<void> |

// List and select devices
const microphones = await talker.getAudioInputDevices();
await talker.setAudioInputDevice(microphones[1].deviceId);

const speakers = await talker.getAudioOutputDevices();
await talker.setAudioOutputDevice(speakers[0].deviceId);

Playback Queue Methods

The SDK automatically queues incoming broadcasts and plays them in order.

| Method | Description | Returns | |--------|-------------|---------| | pauseQueue() | Pause queue (current playback continues) | void | | resumeQueue() | Resume queue processing | void | | stopPlaybackAndPause() | Stop current playback and pause queue | void | | getPlaybackQueueLength() | Get number of pending broadcasts | number | | getCurrentlyPlaying() | Get currently playing broadcast | BroadcastStartEvent \| null |

// Pause incoming broadcasts while user is busy
talker.pauseQueue();

// Later, resume
talker.resumeQueue();

// Need immediate silence
talker.stopPlaybackAndPause();

// Check queue status
console.log(`${talker.getPlaybackQueueLength()} broadcasts waiting`);

Channel Methods

| Method | Description | Returns | |--------|-------------|---------| | getChannels() | Get channels list | Promise<Channel[]> | | createChannel(config) | Create a channel | Promise<{ channelId, channelName }> | | leaveChannel(channelId) | Leave a channel | Promise<void> | | addParticipant(channelId, userId) | Add user to channel | Promise<void> | | removeParticipant(channelId, userId) | Remove user | Promise<void> | | addAdmin(channelId, userId) | Make user admin | Promise<void> | | removeAdmin(channelId, userId) | Remove admin | Promise<void> | | updateChannelName(channelId, name) | Rename channel | Promise<void> |

Example: Create Channel
// Create a group channel with participants
const channel = await talker.createChannel({
  name: 'Field Team Alpha',
  type: 'group',
  participants: ['user-id-1', 'user-id-2', 'user-id-3'],
});

console.log(`Channel created: ${channel.channelName} (${channel.channelId})`);
Example: Update Channel Members
// Add a participant
await talker.addParticipant('channel-id', 'new-user-id');

// Remove a participant
await talker.removeParticipant('channel-id', 'user-to-remove');

// Promote to admin
await talker.addAdmin('channel-id', 'user-id');

// Remove admin privileges
await talker.removeAdmin('channel-id', 'user-id');

// Listen for member changes
talker.on('channel_participant_added', (event) => {
  console.log(`${event.userName} joined channel ${event.channelId}`);
});

talker.on('channel_participant_removed', (event) => {
  console.log(`User ${event.userId} left channel ${event.channelId}`);
});
Example: Join/Leave Channel
// Get available channels
const channels = await talker.getChannels();
console.log(`You are in ${channels.length} channels`);

channels.forEach((ch) => {
  console.log(`- ${ch.name} (${ch.participants.length} members)`);
});

// Leave a channel
await talker.leaveChannel('channel-id');

Live Status Methods

Live status indicates a user's availability for real-time audio in a channel. When live, users can send and receive audio instantly.

| Method | Description | Returns | |--------|-------------|---------| | goLive(channelId) | Set yourself as live in a channel | Promise<void> | | dropFromLive(channelId) | Remove yourself from live | Promise<void> | | isLive(channelId) | Check if you are live in a channel | boolean |

// Go live (available for real-time audio)
await talker.goLive('channel-id');

// Check live status
if (talker.isLive('channel-id')) {
  console.log('You are live');
}

// Drop from live
await talker.dropFromLive('channel-id');

// Listen for live status changes
talker.on('channel_live_status', (event) => {
  console.log(`Channel ${event.channelId} live: ${event.live}`);
});

talker.on('channel_participant_live_status', (event) => {
  console.log(`User ${event.participantId} live: ${event.live}`);
});

User Methods

| Method | Description | Returns | |--------|-------------|---------| | setUserId(userId) | Set current user ID | void | | getUserId() | Get current user ID | string \| null | | getAllUsers() | Get all workspace users | Promise<UserInfo[]> |

Stream Capturing

Access raw incoming audio for custom processing, visualization, speech-to-text, or recording.

| Event | Payload | Description | |-------|---------|-------------| | audio_received | ArrayBuffer | Raw PCM audio from incoming broadcast |

Audio Format: 16-bit signed PCM, 16kHz, mono

// Audio visualization
talker.on('audio_received', (pcmData: ArrayBuffer) => {
  const samples = new Int16Array(pcmData);
  const level = samples.reduce((sum, s) => sum + Math.abs(s), 0) / samples.length / 32768;
  updateWaveform(level);
});

// Recording
const chunks: Int16Array[] = [];
talker.on('audio_received', (pcmData) => chunks.push(new Int16Array(pcmData)));

// Speech-to-text
talker.on('audio_received', (pcmData) => speechRecognizer.feedAudio(pcmData));

Messaging

| Method | Description | Returns | |--------|-------------|---------| | sendMessage(channelId, content) | Send text/file message | Promise<void> |

// Send text message
await talker.sendMessage('channel-id', { text: 'Hello!' });

// Send file
await talker.sendMessage('channel-id', {
  file: fileInput.files[0],
  description: 'Photo from today',
});

Events

Subscribe to events using client.on(event, handler) and unsubscribe with client.off(event, handler).

Connection Events

client.on('connection_change', (event: ConnectionChangeEvent) => {
  // event.connected: boolean - true if fully connected
  // event.status: 'connected' | 'connecting' | 'disconnected'
});

Broadcast Events

client.on('broadcast_start', (event: BroadcastStartEvent) => {
  // event.channelId: string
  // event.senderId: string
  // event.senderName: string
  // event.messageId?: string
  // event.timestamp: number
});

client.on('broadcast_end', (event: BroadcastEndEvent) => {
  // event.channelId: string
  // event.senderId: string
  // event.timestamp: number
});

client.on('playback_progress', (event: PlaybackProgressEvent) => {
  // event.duration: number (seconds elapsed)
});

Channel Events

client.on('new_channel', (event: NewChannelEvent) => {
  // event.channel: { channelId, channelName, channelType }
  // event.timestamp: number
});

client.on('channel_name_update', (event: ChannelNameUpdateEvent) => {
  // event.channelId: string
  // event.newName: string
  // event.timestamp: number
});

client.on('channel_participant_added', (event: ParticipantAddedEvent) => {
  // event.channelId: string
  // event.userId: string
  // event.userName: string
  // event.timestamp: number
});

client.on('channel_participant_removed', (event: ParticipantRemovedEvent) => {
  // event.channelId: string
  // event.userId: string
  // event.timestamp: number
});

client.on('channel_admin_added', (event: AdminAddedEvent) => {
  // event.channelId: string
  // event.userId: string
  // event.timestamp: number
});

client.on('channel_admin_removed', (event: AdminRemovedEvent) => {
  // event.channelId: string
  // event.userId: string
  // event.timestamp: number
});

User Events

client.on('new_sdk_user', (event: NewSdkUserEvent) => {
  // event.userId: string
  // event.userName: string
  // event.timestamp: number
});

Message Events

client.on('message', (event: MessageEvent) => {
  // event.message: {
  //   messageId: string,
  //   channelId: string,
  //   senderId: string,
  //   senderName: string,
  //   type: string,
  //   text?: string,
  //   mediaUrl?: string,
  //   timestamp: string,
  // }
});

TypeScript Types

All types are exported from the package:

import type {
  // User types
  Platform,              // 'WEB' | 'ANDROID' | 'IOS'
  CreateUserConfig,
  SetUserConfig,
  UserData,
  UserInfo,

  // Channel types
  ChannelType,           // 'group' | 'direct'
  CreateChannelConfig,
  ChannelParticipant,
  Channel,

  // Event types
  BroadcastStartEvent,
  BroadcastEndEvent,
  ConnectionChangeEvent,
  ConnectionStatus,
  NewChannelEvent,
  ChannelNameUpdateEvent,
  ParticipantAddedEvent,
  ParticipantRemovedEvent,
  AdminAddedEvent,
  AdminRemovedEvent,
  NewSdkUserEvent,
  MessageEvent,
  PlaybackProgressEvent,

  // PTT result types
  StartTalkingResult,
  StartTalkingResultCode,

  // Config types
  TalkerAdminConfig,
  TalkerClientConfig,
  LoggerConfig,

  // Credentials
  CredentialsData,
  WebRTCCredentials,
} from '@talker-network/talker-sdk';

// Logger utilities
import { LogLevel, logger } from '@talker-network/talker-sdk';

Error Handling

Common Errors

| Error | Cause | Solution | |-------|-------|----------| | "Establishing connection to servers" | PTT called while reconnecting | Retry after connection_change event shows connected | | "TalkerClient requires a userAuthToken" | Missing constructor parameter | Pass all required credentials from backend | | "Failed to get credentials" | Invalid SDK key or network error | Check SDK key and network connectivity | | "WebRTC data channel failed to open" | Connection timeout | Check network, retry connection |

Error Handling Pattern

// PTT with retry
async function startPTT(channelId: string) {
  try {
    await talker.startTalking(channelId);
  } catch (error) {
    if (error.message === 'Establishing connection to servers') {
      // Wait for connection and retry
      const unsubscribe = talker.on('connection_change', async ({ connected }) => {
        if (connected) {
          unsubscribe();
          await talker.startTalking(channelId);
        }
      });
    } else {
      console.error('PTT error:', error);
    }
  }
}

Connection Behavior

  • Auto-connect on init: TalkerClient automatically connects when created
  • Auto-reconnect: Both Socket and WebRTC connections auto-reconnect if disconnected
  • Credentials caching: WebRTC credentials are cached with 5-minute expiry buffer
  • PTT auto-reconnect: If startTalking() is called while disconnected, reconnection is triggered automatically

Connection States

| Status | Meaning | |--------|---------| | connected | Socket and WebRTC data channel both connected, ready for PTT | | connecting | Connection is being established | | disconnected | Not connected (will auto-reconnect) |


Logging

Default Behavior

The SDK automatically detects your environment:

| Environment | Console Output | Log Level | |-------------|---------------|-----------| | Development (localhost, ports 3000/5173/8080) | Enabled | DEBUG | | Production (npm package in prod) | Disabled | ERROR only |

This means no console spam in production by default.

Enable Logging in Production

To enable logging in production for debugging:

import { TalkerClient, LogLevel } from '@talker-network/talker-sdk';

const client = new TalkerClient({
  userAuthToken,
  userId,
  a_username,
  a_password,
  loggerConfig: {
    level: LogLevel.DEBUG,  // TRACE, DEBUG, INFO, WARN, ERROR, OFF
    enableConsole: true,    // Force console output
  },
});

Custom Log Handler

Send logs to external services (works in both dev and production):

const client = new TalkerClient({
  // ...credentials
  loggerConfig: {
    level: LogLevel.INFO,
    customHandler: (entry) => {
      if (entry.level >= LogLevel.ERROR) {
        Sentry.captureMessage(entry.event, {
          extra: { component: entry.component, data: entry.data },
        });
      }
    },
  },
});

See LOGGING.md for detailed logging documentation.


React Example

import { useEffect, useState, useCallback, useRef } from 'react';
import { TalkerClient, LogLevel } from '@talker-network/talker-sdk';

function useTalker(credentials: Credentials | null) {
  const [connectionStatus, setConnectionStatus] = useState<'disconnected' | 'connecting' | 'connected'>('disconnected');
  const [isTalking, setIsTalking] = useState(false);
  const clientRef = useRef<TalkerClient | null>(null);

  useEffect(() => {
    if (!credentials) return;

    const client = new TalkerClient({
      userAuthToken: credentials.userAuthToken,
      userId: credentials.userId,
      a_username: credentials.a_username,
      a_password: credentials.a_password,
      loggerConfig: { level: LogLevel.DEBUG, enableConsole: true },
    });

    clientRef.current = client;

    client.on('connection_change', ({ status }) => {
      setConnectionStatus(status);
    });

    client.on('broadcast_start', (data) => {
      console.log(`${data.senderName} is speaking`);
    });

    return () => {
      client.disconnect();
    };
  }, [credentials]);

  const startTalking = useCallback(async (channelId: string) => {
    if (!clientRef.current) return;
    try {
      await clientRef.current.startTalking(channelId);
      setIsTalking(true);
    } catch (error) {
      console.error('Failed to start talking:', error);
    }
  }, []);

  const stopTalking = useCallback(async () => {
    if (!clientRef.current) return;
    await clientRef.current.stopTalking();
    setIsTalking(false);
  }, []);

  return { connectionStatus, isTalking, startTalking, stopTalking };
}

Browser Support

| Browser | Minimum Version | |---------|-----------------| | Chrome | 80+ | | Firefox | 75+ | | Safari | 14+ | | Edge | 80+ |

iOS/Safari Notes

Call unlockAudio() on first user interaction to enable audio playback:

document.addEventListener('click', () => {
  talker.unlockAudio();
}, { once: true });

Troubleshooting

Connection Issues

  1. Check network connectivity
  2. Verify credentials are correct
  3. Enable DEBUG logging to see connection details
  4. Check browser console for errors

PTT Not Working

  1. Verify isFullyConnected() returns true
  2. Check microphone permissions
  3. Ensure setUserId() was called
  4. Enable DEBUG logging and check for errors

No Audio Playback

  1. Call unlockAudio() on user interaction (iOS/Safari)
  2. Check setSpeakerEnabled(true)
  3. Verify volume with setVolume(1.0)
  4. Check speaker device selection

Audio Permission Denied

try {
  await navigator.mediaDevices.getUserMedia({ audio: true });
} catch (error) {
  if (error.name === 'NotAllowedError') {
    // User denied permission
  }
}

License

MIT