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

atoms-client-sdk

v1.1.0

Published

A TypeScript/JavaScript SDK for real-time voice and text communication with AI agents using WebRTC technology. Built on Atoms' proprietary real-time communication infrastructure for robust, low-latency audio streaming and real-time data exchange.

Readme

Atoms Client SDK

A TypeScript/JavaScript SDK for real-time voice and text communication with AI agents using WebRTC technology. Built on Atoms' proprietary real-time communication infrastructure for robust, low-latency audio streaming and real-time data exchange.

Table of Contents

Installation

npm install atoms-client-sdk

Quick Start

Voice Mode

import { AtomsClient } from "atoms-client-sdk";

const client = new AtomsClient();

// Listen for events
client.on("session_started", ({ mode }) => {
  console.log(`Session started in ${mode} mode`);
});

client.on("transcript", (data) => {
  console.log("Agent said:", data.text);
});

client.on("agent_start_talking", () => {
  console.log("Agent started talking");
});

client.on("agent_stop_talking", () => {
  console.log("Agent stopped talking");
});

// Start voice session
await client.startSession({
  accessToken: "your-access-token",
  mode: "voice",
  host: "wss://your-atoms-host.com",
  sampleRate: 48000,
});

// Don't forget to call startAudioPlayback() after user interaction
await client.startAudioPlayback();

Text Mode

import { AtomsClient } from "atoms-client-sdk";

const client = new AtomsClient();

// Listen for transcript responses
client.on("transcript", (data) => {
  console.log("Agent response:", data.text);
});

// Start text-only session
await client.startSession({
  accessToken: "your-access-token",
  mode: "text",
  host: "wss://your-atoms-host.com",
});

// Send text messages
client.sendTextMessage("Hello, how can you help me today?");

API Reference

Class: AtomsClient

The main client class that extends EventEmitter for real-time communication with AI agents.

Constructor

new AtomsClient();

Creates a new instance of the AtomsClient.

Methods

startSession(config: StartSessionConfig): Promise<void>

Initializes and starts a communication session with the AI agent.

Parameters:

  • config - Session configuration object

Throws:

  • Error - If mode is not specified, audio context is not supported, or connection fails

Example:

await client.startSession({
  accessToken: "jwt-token",
  mode: "voice",
  host: "wss://your-host.com",
  sampleRate: 48000,
  captureDeviceId: "default",
  playbackDeviceId: "default",
});
startAudioPlayback(): Promise<void>

Starts audio playback. Must be called after user interaction due to browser autoplay policies.

Example:

// Call this inside a click/tap event handler
button.addEventListener("click", async () => {
  await client.startAudioPlayback();
});
stopSession(): void

Stops the current session and cleans up all resources.

Example:

client.stopSession();
mute(): void

Mutes the microphone (voice mode only).

Example:

client.mute();
unmute(): void

Unmutes the microphone (voice mode only).

Example:

client.unmute();
sendTextMessage(message: string): void

Sends a text message to the agent (text mode only).

Parameters:

  • message - The text message to send

Example:

client.sendTextMessage("What is the weather like today?");
checkMicrophonePermissions(): Promise<{granted: boolean, error?: string}>

Manually checks microphone permissions. This is called automatically during startSession() for voice mode.

Returns:

  • granted - Whether microphone access is granted
  • error - Error message if permission check failed

Example:

const { granted, error } = await client.checkMicrophonePermissions();
if (!granted) {
  console.error("Microphone permission required:", error);
}

Properties

isConnected: boolean (read-only)

Returns whether the client is currently connected to the session.

currentMode: "voice" | "text" | undefined (read-only)

Returns the current session mode.

roomName: string | undefined (read-only)

Returns the session room name if connected.

isAgentTalking: boolean (read-only)

Returns whether the AI agent is currently speaking.

audioContext: AudioContext | undefined (read-only)

Returns the Web Audio API context (voice mode only).

sampleRate: number | undefined (read-only)

Returns the audio sample rate being used.

Interface: StartSessionConfig

Configuration object for starting a session.

interface StartSessionConfig {
  accessToken: string; // JWT token for authentication
  mode: "voice" | "text"; // Communication mode
  host?: string; // Atoms server URL
  sampleRate?: number; // Audio sample rate (default: 48000)
  captureDeviceId?: string; // Microphone device ID
  playbackDeviceId?: string; // Speaker device ID
  emitRawAudioSamples?: boolean; // Whether to emit raw audio data
}

Properties

  • accessToken (required): JWT token for authenticating with the Atoms server
  • mode (required): Either "voice" for audio communication or "text" for text-only
  • host (optional): WebSocket URL of the Atoms server
  • sampleRate (optional): Audio sample rate in Hz (default: 48000)
  • captureDeviceId (optional): Device ID for microphone input
  • playbackDeviceId (optional): Device ID for audio output
  • emitRawAudioSamples (optional): Enable raw audio data events

Events

The AtomsClient emits various events that you can listen to:

Session Events

session_started

Emitted when the session successfully starts.

client.on("session_started", (data: { mode: string }) => {
  console.log(`Session started in ${data.mode} mode`);
});

session_ended

Emitted when the session ends.

client.on("session_ended", () => {
  console.log("Session ended");
});

error

Emitted when an error occurs.

client.on("error", (error: string) => {
  console.error("Session error:", error);
});

Permission Events

microphone_permission_granted

Emitted when microphone permission is successfully granted.

client.on("microphone_permission_granted", () => {
  console.log("Microphone access granted");
});

microphone_permission_error

Emitted when there's an issue with microphone permissions.

client.on(
  "microphone_permission_error",
  (data: { error: string; canRetry: boolean }) => {
    console.error("Microphone permission error:", data.error);
    if (data.canRetry) {
      // Show retry button to user
    }
  },
);

microphone_access_failed

Emitted when microphone access fails during session start.

client.on(
  "microphone_access_failed",
  (data: { error: string; name: string; constraint?: string }) => {
    console.error("Microphone access failed:", data);
  },
);

Agent Events

agent_connected

Emitted when an AI agent connects to the session.

client.on("agent_connected", (participant) => {
  console.log("Agent connected:", participant.identity);
});

agent_start_talking

Emitted when the AI agent starts speaking.

client.on("agent_start_talking", () => {
  console.log("Agent is now speaking");
  // Update UI to show agent is talking
});

agent_stop_talking

Emitted when the AI agent stops speaking.

client.on("agent_stop_talking", () => {
  console.log("Agent stopped speaking");
  // Update UI to show agent is not talking
});

Communication Events

transcript

Emitted when a transcript message is received from the agent.

client.on(
  "transcript",
  (data: { type: string; topic: string; text: string; timestamp: number }) => {
    console.log("Agent said:", data.text);
    // Display message in chat UI
  },
);

update

Emitted when the agent sends an update event.

client.on("update", (event) => {
  console.log("Agent update:", event);
});

metadata

Emitted when the agent sends metadata.

client.on("metadata", (event) => {
  console.log("Agent metadata:", event);
});

Audio Events

audio_track_subscribed

Emitted when an audio track is subscribed (voice mode only).

client.on(
  "audio_track_subscribed",
  (data: { track: any; participant: any }) => {
    console.log("Audio track subscribed");
  },
);

audio

Emitted when raw audio samples are available (requires emitRawAudioSamples: true).

client.on("audio", (audioData: Float32Array) => {
  // Process raw audio samples
  console.log("Raw audio samples:", audioData);
});

Configuration

Audio Configuration

For voice mode, you can configure various audio parameters:

await client.startSession({
  accessToken: "your-token",
  mode: "voice",
  host: "wss://your-host.com",
  sampleRate: 48000, // Higher quality audio
  captureDeviceId: "specific-mic-id", // Use specific microphone
  playbackDeviceId: "specific-speaker-id", // Use specific speaker
  emitRawAudioSamples: true, // Enable raw audio events
});

Device Selection

To get available audio devices:

// Get available devices
const devices = await navigator.mediaDevices.enumerateDevices();
const audioInputs = devices.filter((device) => device.kind === "audioinput");
const audioOutputs = devices.filter((device) => device.kind === "audiooutput");

// Use specific device
await client.startSession({
  accessToken: "your-token",
  mode: "voice",
  host: "wss://your-host.com",
  captureDeviceId: audioInputs[0].deviceId,
  playbackDeviceId: audioOutputs[0].deviceId,
});

Error Handling

Common Errors and Solutions

Microphone Permission Denied

client.on("microphone_permission_error", (data) => {
  if (data.error.includes("denied")) {
    // Show instructions to enable microphone in browser settings
    showMicrophoneInstructions();
  }
});

Audio Context Issues

client.on("error", (error) => {
  if (error.includes("AudioContext")) {
    // Try starting audio playback after user interaction
    showClickToEnableAudioButton();
  }
});

Connection Failures

client.on("error", (error) => {
  console.error("Connection error:", error);
  // Implement retry logic
  setTimeout(() => {
    client.startSession(config);
  }, 5000);
});

Best Practices

  1. Always handle microphone permissions gracefully
  2. Call startAudioPlayback() after user interaction
  3. Implement proper cleanup with stopSession()
  4. Handle network disconnections with retry logic
  5. Provide user feedback for all states

Examples

Complete Voice Chat Application

import { AtomsClient } from "atoms-client-sdk";

class VoiceChatApp {
  private client: AtomsClient;
  private isSessionActive = false;

  constructor() {
    this.client = new AtomsClient();
    this.setupEventListeners();
  }

  private setupEventListeners() {
    // Session events
    this.client.on("session_started", () => {
      this.isSessionActive = true;
      this.updateUI("Connected");
    });

    this.client.on("session_ended", () => {
      this.isSessionActive = false;
      this.updateUI("Disconnected");
    });

    // Agent events
    this.client.on("agent_start_talking", () => {
      this.showAgentSpeaking(true);
    });

    this.client.on("agent_stop_talking", () => {
      this.showAgentSpeaking(false);
    });

    this.client.on("transcript", (data) => {
      this.addMessageToChat("agent", data.text);
    });

    // Error handling
    this.client.on("error", (error) => {
      console.error("Client error:", error);
      this.showError(error);
    });

    this.client.on("microphone_permission_error", (data) => {
      this.showMicrophoneError(data.error);
    });
  }

  async startSession(accessToken: string, host: string) {
    try {
      await this.client.startSession({
        accessToken,
        mode: "voice",
        host,
        sampleRate: 48000,
      });
    } catch (error) {
      console.error("Failed to start session:", error);
      this.showError("Failed to connect");
    }
  }

  async enableAudio() {
    try {
      await this.client.startAudioPlayback();
      this.updateUI("Audio enabled");
    } catch (error) {
      console.error("Failed to enable audio:", error);
    }
  }

  stopSession() {
    this.client.stopSession();
  }

  toggleMute() {
    if (this.client.isConnected) {
      // Implement mute state tracking
      this.client.mute(); // or unmute()
    }
  }

  private updateUI(status: string) {
    // Update your UI with connection status
  }

  private showAgentSpeaking(speaking: boolean) {
    // Show visual indicator when agent is talking
  }

  private addMessageToChat(sender: "user" | "agent", message: string) {
    // Add message to chat interface
  }

  private showError(error: string) {
    // Show error message to user
  }

  private showMicrophoneError(error: string) {
    // Show specific microphone error message
  }
}

// Usage
const app = new VoiceChatApp();

// Start session when user clicks connect
document.getElementById("connect-btn")?.addEventListener("click", async () => {
  await app.startSession("your-jwt-token", "wss://your-atoms-host.com");
});

// Enable audio when user clicks (required for autoplay policy)
document
  .getElementById("enable-audio-btn")
  ?.addEventListener("click", async () => {
    await app.enableAudio();
  });

Text-Only Chat Application

import { AtomsClient } from "atoms-client-sdk";

class TextChatApp {
  private client: AtomsClient;

  constructor() {
    this.client = new AtomsClient();
    this.setupEventListeners();
  }

  private setupEventListeners() {
    this.client.on("session_started", () => {
      console.log("Text chat session started");
    });

    this.client.on("transcript", (data) => {
      this.displayMessage("agent", data.text);
    });

    this.client.on("error", (error) => {
      console.error("Chat error:", error);
    });
  }

  async startTextSession(accessToken: string, host: string) {
    await this.client.startSession({
      accessToken,
      mode: "text",
      host,
    });
  }

  sendMessage(message: string) {
    this.client.sendTextMessage(message);
    this.displayMessage("user", message);
  }

  private displayMessage(sender: "user" | "agent", message: string) {
    // Display message in chat UI
    const chatContainer = document.getElementById("chat-messages");
    const messageElement = document.createElement("div");
    messageElement.className = `message ${sender}`;
    messageElement.textContent = message;
    chatContainer?.appendChild(messageElement);
  }
}

React Integration Example

import React, { useEffect, useRef, useState } from 'react';
import { AtomsClient } from 'atoms-client-sdk';

interface Message {
  sender: 'user' | 'agent';
  text: string;
  timestamp: number;
}

const VoiceChat: React.FC = () => {
  const [client] = useState(() => new AtomsClient());
  const [isConnected, setIsConnected] = useState(false);
  const [isAgentTalking, setIsAgentTalking] = useState(false);
  const [messages, setMessages] = useState<Message[]>([]);
  const [isMuted, setIsMuted] = useState(false);

  useEffect(() => {
    // Setup event listeners
    client.on('session_started', () => {
      setIsConnected(true);
    });

    client.on('session_ended', () => {
      setIsConnected(false);
    });

    client.on('agent_start_talking', () => {
      setIsAgentTalking(true);
    });

    client.on('agent_stop_talking', () => {
      setIsAgentTalking(false);
    });

    client.on('transcript', (data) => {
      setMessages(prev => [...prev, {
        sender: 'agent',
        text: data.text,
        timestamp: Date.now()
      }]);
    });

    // Cleanup
    return () => {
      client.stopSession();
    };
  }, [client]);

  const startSession = async () => {
    try {
      await client.startSession({
        accessToken: process.env.REACT_APP_ATOMS_TOKEN!,
        mode: 'voice',
        host: process.env.REACT_APP_ATOMS_HOST!,
      });
      await client.startAudioPlayback();
    } catch (error) {
      console.error('Failed to start session:', error);
    }
  };

  const toggleMute = () => {
    if (isMuted) {
      client.unmute();
    } else {
      client.mute();
    }
    setIsMuted(!isMuted);
  };

  return (
    <div className="voice-chat">
      <div className="chat-messages">
        {messages.map((message, index) => (
          <div key={index} className={`message ${message.sender}`}>
            {message.text}
          </div>
        ))}
      </div>

      <div className="controls">
        {!isConnected ? (
          <button onClick={startSession}>Start Voice Chat</button>
        ) : (
          <>
            <button onClick={toggleMute}>
              {isMuted ? 'Unmute' : 'Mute'}
            </button>
            <button onClick={() => client.stopSession()}>
              End Session
            </button>
          </>
        )}
      </div>

      {isAgentTalking && (
        <div className="agent-indicator">Agent is speaking...</div>
      )}
    </div>
  );
};

export default VoiceChat;

Browser Support

Minimum Requirements

  • Chrome/Edge: 66+
  • Firefox: 60+
  • Safari: 11.1+
  • Mobile Safari: 11.3+
  • Chrome Android: 66+

Required APIs

  • WebRTC (RTCPeerConnection)
  • Web Audio API (for voice mode)
  • WebSockets
  • MediaDevices API (for microphone access)

Feature Detection

function checkBrowserSupport() {
  const hasWebRTC = "RTCPeerConnection" in window;
  const hasWebAudio =
    "AudioContext" in window || "webkitAudioContext" in window;
  const hasMediaDevices =
    navigator.mediaDevices && navigator.mediaDevices.getUserMedia;

  return {
    webRTC: hasWebRTC,
    webAudio: hasWebAudio,
    mediaDevices: hasMediaDevices,
    supported: hasWebRTC && hasWebAudio && hasMediaDevices,
  };
}

Troubleshooting

Common Issues

1. "AudioContext not supported"

Cause: Browser doesn't support Web Audio API Solution: Check browser compatibility or fallback to text mode

2. "Microphone access required for voice mode"

Cause: User denied microphone permissions Solutions:

  • Guide user to enable microphone in browser settings
  • Fallback to text mode
  • Implement retry mechanism

3. Audio not playing

Cause: Browser autoplay policy Solution: Always call startAudioPlayback() after user interaction

4. Connection timeouts

Cause: Network issues or invalid credentials Solutions:

  • Verify access token is valid and not expired
  • Check network connectivity
  • Implement retry logic with exponential backoff

5. Poor audio quality

Solutions:

  • Increase sample rate (up to 48000)
  • Enable audio processing features
  • Check for background noise cancellation

Debug Mode

Enable console logging to debug issues:

// The SDK automatically logs important events to console
// Check browser developer tools for detailed logs

Performance Optimization

  1. Use appropriate sample rates: 16000 for speech, 48000 for high quality
  2. Clean up resources: Always call stopSession() when done
  3. Handle connection states: Implement proper state management
  4. Optimize for mobile: Consider battery usage and data consumption

Getting Help

If you encounter issues:

  1. Check the browser console for error messages
  2. Verify your access token and host configuration
  3. Test with different browsers
  4. Check network connectivity
  5. Ensure microphone permissions are granted

For additional support, contact the Atoms team or visit our documentation portal.


License

This SDK is part of the Atoms platform by Smallest AI. Please refer to your license agreement for usage terms.

© 2024 Smallest AI. All rights reserved.