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

streaming-avatar-sdk

v1.0.21

Published

The JSSDK provides access to Akool Streaming Avatar services, enabling programmatic control of avatar interactions.

Readme

Streaming Avatar SDK Best Practice

Learn how implement Streaming Avatar SDK step by step

Overview

When implementing a JavaScript SDK, especially one that interacts with sensitive resources or APIs, it is critical to ensure the security of private keys, tokens, and other sensitive credentials. Exposing such sensitive information in a client-side environment (e.g., browsers) can lead to vulnerabilities, including unauthorized access, token theft, and API abuse. This document outlines best practices for securing private keys and tokens in your Streaming Avatar SDK implementation while exposing only the necessary session data to the client.

  • Never Expose Private Keys in the Client-Side Code
  • Use Short-Lived Session as token
  • Delegate Authentication to a Backend Server
  • Handling avatar interactions and responses
  • Managing audio streams and events

The integration uses Agora's Real-Time Communication (RTC) SDK for reliable, low-latency streaming and our avatar service for generating responsive avatar behaviors.

Prerequisites

Getting Started

  1. npm install '@akool/streaming-avatar-sdk'
npm install @akool/streaming-avatar-sdk
# or
yarn add @akool/streaming-avatar-sdk
  1. To get started with the Streaming Avatar SDK, you just need one html page with lots of elements like below:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Streaming Avatar SDK</title>
</head>
<body>
  <div id="app">
    <h1>Streaming Avatar Demo</h1>
    <div id="yourAvatarContainer" class="avatarContainer">
      <div id="videoWrap" class="videoWrap" style="width: fit-content;">
        <video id="yourStreamingVideoDom" autoplay="" playsinline="" loop=""></video>
        <div id="messageWrap"></div>
      </div>
      <div id="controls">
        <div>
          <button id="toggleSession">Start Session</button>
        </div>
        <div style="display: flex; gap: 10px;">
          <input type="text" id="userInput" disabled="" placeholder="Type anything to communicate with the avatar...">
          <button id="sendButton" disabled="">Send</button>
          <button id="voiceButton" disabled="">Turn Voice on</button>
        </div>
      </div>
    </div>
  </div>
</body>
</html>
  1. importing Streaming Avatar SDK:
  import { StreamingAvatar,StreamEvents } from 'streaming-avatar-sdk'
  1. Instantiation StreamingAvatar class and get session params form your backend:
  var stream = new StreamingAvatar();
  // info: start your stream session with Credentials.
  // Best Practice: get from Akool Session_ID and Credentials from your backend service.
  const paramsWithCredentials = await YOUR_BACK_END_API_FOR_START_SESSION()

YOUR_BACK_END_API_FOR_START_SESSION may like below:

  async function fetchAccessToken() {
    const id = YOUR_CLIENT_ID;
    const apiKey = AKOOL_SECRET_KEY;
    const response = await fetch(
      "https://openapi.akool.com/api/open/v3/getToken",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          "clientId": id
          "clientSecret": apiKey
        }),
        redirect: "follow",
      }
    );

    const { data } = await response.json();
    return data.token;
  }

  async function createSession(token,avatar_id,duration) {
    const id = YOUR_CLIENT_ID;
    const apiKey = AKOOL_SECRET_KEY;
    const response = await fetch(
      "https://openapi.akool.com/api/open/v3/getToken",
      {
        method: "POST",
        headers: {
          "Authorization": `Bearer ${token}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          avatar_id;
          duration;
        }),
        redirect: "follow",
      }
    );

    const { data } = await response.json();
    return data;
  }

  const token = await fetchAccessToken()
        avatar_id = "dvp_Tristan_cloth2_1080P"
        duration = 300;
  const paramsWithCredentials = await createSession(token,avatar_id,duration)

Make sure the YOUR_BACK_END_API_FOR_START_SESSION return the paramsWithCredentials from your backend

  1. Sign up functions for steaming events and button click events:
  function handleStreamReady(event:any) {
    console.log('Stream is ready:',event.detail)
  }
  function handleMessageReceive(event:any) {
    console.log('Message:', event.detail)
  }
  function handleWillExpire(event:any) {
    console.log('Warning:',event.detail.msg)
  }
  function handleExpired(event:any) {
    console.log('Warning:',event.detail.msg)
  }
  function handleERROR(event:any) {
    console.error('ERROR has occurred:',event.detail.msg)
  }
  function handleStreamClose(event:any) {
    console.log('Stream is close:',event.detail)
    // when you leave the page you'd better off the eventhandler
    stream.off(StreamEvents.READY,handleStreamReady)
    stream.off(StreamEvents.ONMESSAGE,handleMessageReceive)
    stream.off(StreamEvents.WILLEXPIRE,handleWillExpire)
    stream.off(StreamEvents.EXPIRED,handleExpired)
    stream.off(StreamEvents.ERROR,handleERROR)
    stream.off(StreamEvents.CLOSED,handleStreamClose)
  }

  stream.on(StreamEvents.READY,handleStreamReady)
  stream.on(StreamEvents.ONMESSAGE,handleMessageReceive)
  stream.on(StreamEvents.WILLEXPIRE,handleWillExpire)
  stream.on(StreamEvents.EXPIRED,handleExpired)
  stream.on(StreamEvents.ERROR,handleERROR)
  stream.on(StreamEvents.CLOSED,handleStreamClose)

  async function handleToggleSession() {
    if (window.toggleSession.innerHTML == "&nbsp;&nbsp;&nbsp;...&nbsp;&nbsp;&nbsp;") return
    if (window.toggleSession.innerHTML == "Start Session") {
      window.toggleSession.innerHTML = "&nbsp;&nbsp;&nbsp;...&nbsp;&nbsp;&nbsp;"
      await stream.startSessionWithCredentials('yourStreamingVideoDom',paramsWithCredentials)
      window.toggleSession.innerHTML = "End Session"
      window.userInput.disabled = false;
      window.sendButton.disabled = false;
      window.voiceButton.disabled = false;
    }else{
      // info: close your stream session
      stream.closeStreaming()
      window.messageWrap.innerHTML = ''
      window.toggleSession.innerHTML = "Start Session"
      window.userInput.disabled = true;
      window.sendButton.disabled = true;
      window.voiceButton.disabled = true;
    }
  }

  async function handleSendMessage() {
    await stream.sendMessage(window.userInput.value ?? '')
  }

  async function handleToggleMic() {
    await stream.toggleMic()
    if (stream.micStatus) {
      window.voiceButton.innerHTML = "Turn mic off"
    }else{
      window.voiceButton.innerHTML = "Turn mic on"
    }
  }

  window.toggleSession.addEventListener("click", handleToggleSession);
  window.sendButton.addEventListener("click", handleSendMessage);
  window.voiceButton.addEventListener("click", handleToggleMic);

Additional Resources