All Products
Search
Document Center

ApsaraVideo Live:Getting started with the ARTC Web SDK

Last Updated:Jun 20, 2026

The ApsaraVideo Real-time Communication (ARTC) Web SDK is a toolkit provided by Alibaba Cloud for developing web-based real-time communication applications. It allows you to quickly integrate high-quality features, such as audio/video calls and real-time messaging, into your web applications. This guide shows you how to quickly build your first ARTC application.

Step 1: Create an application

  1. Log on to the ApsaraVideo Live console.

  2. In the left-side navigation pane, choose Live + > ApsaraVideo Real-time Communication > Applications.

  3. Click Create Application.

  4. Enter a custom instance name, select the Terms of Service checkbox, and then click Purchase Now.

  5. After the success message appears, refresh the Applications page to view your new ApsaraVideo Real-time Communication application.

    Note

    Creating an application is free. You are charged on a pay-as-you-go basis for actual usage. For more information, see Billing of audio and video calls.

Step 2: Get application ID and AppKey

After you create the application, find it in the application list. In the Actions column, click Manage to open the Basic Information page. On this page, find the Application ID and AppKey.

Step 3: Integrate the SDK

  1. Integrate the SDK.

    Script

    In your HTML page, include the SDK script.

    <script src="https://g.alicdn.com/apsara-media-box/imp-web-rtc/7.1.9/aliyun-rtc-sdk.js"></script>

    NPM

    In your project, run the following command to install the SDK.

    npm install aliyun-rtc-sdk --save
  2. Initialize the engine.

    // Choose one of the following two import methods.
    // Use this if you are importing from an npm package.
    import AliRtcEngine from 'aliyun-rtc-sdk';
    // Use this if you are including the SDK with a script tag.
    const AliRtcEngine = window.AliRtcEngine;
    // Check browser compatibility.
    const checkResult = await AliRtcEngine.isSupported();
    if (!checkResult.support) {
      // The current environment is not supported. Prompt the user to switch to or upgrade their browser.
    }
    // Create an engine instance. You can save it as a global variable.
    const aliRtcEngine = AliRtcEngine.getInstance();
    
  3. After creating the AliRtcEngine instance, listen for and handle relevant events.

    // Fired when the local user leaves the channel.
    aliRtcEngine.on('bye', (code) => {
      // `code` is a reason code. For details, see the API reference.
      console.log(`bye, code=${code}`);
      // Handle your business logic here, such as exiting the call page.
    });
    // Fired when a remote user comes online.
    aliRtcEngine.on('remoteUserOnLineNotify', (userId, elapsed) => {
      console.log(`User ${userId} joined the channel in ${elapsed} seconds.`);
      // Handle your business logic here, such as displaying the UI module for this user.
    });
    // Fired when a remote user goes offline.
    aliRtcEngine.on('remoteUserOffLineNotify', (userId, reason) => {
      // `reason` is a reason code. For details, see the API reference.
      console.log(`User ${userId} left the channel. Reason code: ${reason}`);
      // Handle your business logic here, such as destroying the UI module for this user.
    });
    // Fired when the subscription state of a remote stream changes.
    aliRtcEngine.on('videoSubscribeStateChanged', (userId, oldState, newState, interval, channelId) => {
      // 'oldState' and 'newState' are AliRtcSubscribeState values.
      // Values: 0 (initialized), 1 (unsubscribed), 2 (subscribing), 3 (subscribed).
      // `interval` is the time between state changes, in milliseconds.
      console.log(`Subscription state of remote user ${userId} in channel ${channelId} changed from ${oldState} to ${newState}.`);
      // Handle the logic for viewing the remote stream here.
      // When `newState` becomes 3, you can play the remote stream by calling setRemoteViewConfig.
      // When `newState` becomes 1, you can stop the playback.
    });
    // Fired when the authentication information expires.
    aliRtcEngine.on('authInfoExpired', () => {
      // This callback indicates that the authentication information has expired.
      // Get a new token and other data, then call the refreshAuthInfo method to update the authentication data.
      aliRtcEngine.refreshAuthInfo({
        userId,
        token,
        timestamp
      });
    });
    // Fired when the authentication information is about to expire.
    aliRtcEngine.on('authInfoWillExpire', () => {
      // This callback is fired 30 seconds before expiration. You should update the authentication information promptly.
      // To stay in the session, get a new token and other data, and call joinChannel to rejoin the channel.
    });
    
  4. (Optional) Set the channel mode. The default is communication mode. For more information, see Set the channel mode and user role.

    // Set the channel mode. Valid values: 'communication' (communication mode), 'interactive_live' (interactive mode).
    aliRtcEngine.setChannelProfile('interactive_live');
    // Set the user role. This method is effective only in interactive mode.
    // Valid values: 'interactive' (streamer, can publish and subscribe to streams), 'live' (viewer, can only subscribe to streams).
    aliRtcEngine.setClientRole('interactive');
  5. Join a channel. For information on how to generate a token, see token-based authentication. You can choose to join with a single parameter or multiple parameters based on your needs.

    • Join with a single parameter

      const userName = 'Test User 1'; // You can change this to your username. Chinese characters are supported.
      try {
        // You need to implement fetchToken to get the Base64-encoded token from your server.
        const base64Token = await fetchToken();
        await aliRtcEngine.joinChannel(base64Token, userName);
        // Joined the channel successfully. Proceed with other operations.
      } catch (error) {
        // Failed to join the channel.
      }
    • Join with multiple parameters

      // Generate authentication information on your server or locally by following the token-based authentication guide.
      // IMPORTANT: For data security, never publish the token calculation logic that includes your AppKey to end users.
      const appId = 'yourAppId'; // Get this from the console.
      const appKey = 'yourAppKey'; // Get this from the console. Do not expose your AppKey in a production environment.
      const channelId = 'AliRtcDemo'; // You can change this to your channel ID. Only letters and digits are supported.
      const userId = 'test1'; // You can change this to your user ID. Only letters and digits are supported.
      const userName = 'Test User 1'; // You can change this to your username. Chinese characters are supported.
      const timestamp = Math.floor(Date.now() / 1000) + 3600; // Expires in one hour.
      try {
        const token = await generateToken(appId, appKey, channelId, userId, timestamp);
        // Join the channel. Parameters like token and timestamp are typically returned from the server.
        // Note: When calling this method, ensure the channelId, userId, appId, and timestamp parameters match those used to generate the token.
        await aliRtcEngine.joinChannel({
          channelId,
          userId,
          appId,
          token,
          timestamp,
        }, userName);
        // Joined the channel successfully. Proceed with other operations.
      } catch (error) {
        // Failed to join the channel.
      }
  6. Follow these steps to preview your local video. By default, after you join a channel, local audio and video data is automatically captured and published to the Global Realtime Transport Network (GRTN).

    1. In the HTML code, add a VIDEO element with an id of localPreviewer.

      <video
        id="localPreviewer"
        muted
        style="display: block;width: 320px;height: 180px;background-color: black;"
      ></video>
    2. Call the setLocalViewConfig method and pass the element ID to start the preview.

      // The first parameter accepts an HTMLVideoElement or its ID. Pass null to stop the preview.
      // The second parameter specifies the stream type: 1 for a camera stream, 2 for a screen sharing stream.
      aliRtcEngine.setLocalViewConfig('localPreviewer', 1);
  7. Subscribe to remote audio and video streams. By default, after joining a channel, the SDK automatically subscribes to the audio and video streams of other streamers. Audio streams are played automatically. To view a camera stream or screen sharing stream, call the setRemoteViewConfig method.

    1. In the HTML code, add a DIV element with an id of remoteVideoContainer as a container.

      <div id="remoteVideoContainer"></div>
    2. Listen for subscription changes to remote video streams. When a stream is subscribed, play it by calling the setRemoteViewConfig method. When it is unsubscribed, remove the video element.

      // Store Video elements.
      const remoteVideoElMap = {};
      // The remote container element.
      const remoteVideoContainer = document.querySelector('#remoteVideoContainer');
      function removeRemoteVideo(userId) {
        const el = remoteVideoElMap[userId];
        if (el) {
          aliRtcEngine.setRemoteViewConfig(null, userId, 1);
          el.pause();
          remoteVideoContainer.removeChild(el);
          delete remoteVideoElMap[userId];
        }
      }
      // This is the same example as in the "listen for and handle relevant events" step for `videoSubscribeStateChanged`.
      aliRtcEngine.on('videoSubscribeStateChanged', (userId, oldState, newState, interval, channelId) => {
        // `oldState` and `newState` are of the AliRtcSubscribeState type.
        // Values: 0 (initialized), 1 (unsubscribed), 2 (subscribing), 3 (subscribed).
        // `interval` is the time between state changes, in milliseconds.
        console.log(`Subscription state of remote user ${userId} in channel ${channelId} changed from ${oldState} to ${newState}.`);
        
        // Example handler
        if (newState === 3) {
          const video = document.createElement('video');
          video.autoplay = true;
          video.setAttribute('style', 'display: block;width: 320px;height: 180px;background-color: black;');
          remoteVideoElMap[userId] = video;
          remoteVideoContainer.appendChild(video);
          // The first parameter is an HTMLVideoElement.
          // The second parameter is the remote user ID.
          // The third parameter specifies the stream type: 1 for a camera stream, 2 for a screen sharing stream.
          aliRtcEngine.setRemoteViewConfig(video, userId, 1);
        } else if (newState === 1) {
          removeRemoteVideo(userId);
        }
      });
  8. End the session and clean up resources.

    // Stop the local preview.
    await aliRtcEngine.stopPreview();
    // Leave the channel.
    await aliRtcEngine.leaveChannel();
    // Destroy the instance to release resources.
    aliRtcEngine.destroy();

Quick start demo

Important

The JavaScript in this demo includes a generateToken method for calculating a token. For security reasons, never publish this code or your AppKey in a client-side JavaScript file, as this can lead to information leaks and abuse. We recommend that you perform token signing on your server and retrieve the token through an authenticated API on the client.

Prerequisites

This demo requires an HTTP server in your development environment. If you do not have the http-server npm package, run npm install --global http-server to install it globally.

Step 1: Create the directory

Create a demo folder containing two files: quick.html and quick.js.

- demo
  - quick.html
  - quick.js

Step 2: Edit quick.html

Copy the following code into quick.html and save the file.

Code example

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>aliyun-rtc-sdk quick start</title>
    <link rel="stylesheet" href="https://g.alicdn.com/code/lib/bootstrap/5.3.0/css/bootstrap.min.css" />
    <style>
      .video {
        display: inline-block;
        width: 320px;
        height: 180px;
        margin-right: 8px;
        margin-bottom: 8px;
        background-color: black;
      }
    </style>
  </head>
  <body class="container p-2">
    <h1>aliyun-rtc-sdk quick start</h1>
    <div class="toast-container position-fixed top-0 end-0 p-3">
      <div id="loginToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
        <div class="toast-header">
          <strong class="me-auto">Login Message</strong>
          <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
        </div>
        <div class="toast-body" id="loginToastBody"></div>
      </div>
      <div id="onlineToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
        <div class="toast-header">
          <strong class="me-auto">User Online</strong>
          <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
        </div>
        <div class="toast-body" id="onlineToastBody"></div>
      </div>
      <div id="offlineToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
        <div class="toast-header">
          <strong class="me-auto">User Offline</strong>
          <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
        </div>
        <div class="toast-body" id="offlineToastBody"></div>
      </div>
    </div>
    <div class="row mt-3">
      <div class="col-6">
        <form id="loginForm">
          <div class="form-group mb-2">
            <label for="channelId" class="form-label">Channel ID</label>
            <input class="form-control" id="channelId" />
          </div>
          <div class="form-group mb-2">
            <label for="userId" class="form-label">User ID</label>
            <input class="form-control" id="userId" />
          </div>
          <button id="joinBtn" type="submit" class="btn btn-primary mb-2">Join Channel</button>
          <button id="leaveBtn" type="button" class="btn btn-secondary mb-2" disabled>Leave Channel</button>
        </form>
        <div class="mt-3">
          <h4>Local Preview</h4>
          <video
            id="localPreviewer"
            muted
            class="video"
          ></video>
        </div>
      </div>
      <div class="col-6">
        <h4>Remote Users</h4>
        <div id="remoteVideoContainer"></div>
      </div>
    </div>
    <script src="https://g.alicdn.com/code/lib/jquery/3.7.1/jquery.min.js"></script>
    <script src="https://g.alicdn.com/code/lib/bootstrap/5.3.0/js/bootstrap.min.js"></script>
<script src="https://g.alicdn.com/apsara-media-box/imp-web-rtc/7.1.9/aliyun-rtc-sdk.js"></script>
    <script src="./quick.js"></script>
  </body>
</html>

Step 3: Edit quick.js

Copy the following code into quick.js. Paste your application ID and AppKey into the specified variables and save the file.

Code example

function hex(buffer) {
  const hexCodes = [];
  const view = new DataView(buffer);
  for (let i = 0; i < view.byteLength; i += 4) {
    const value = view.getUint32(i);
    const stringValue = value.toString(16);
    const padding = '00000000';
    const paddedValue = (padding + stringValue).slice(-padding.length);
    hexCodes.push(paddedValue);
  }
  return hexCodes.join('');
}
async function generateToken(appId, appKey, channelId, userId, timestamp) {
  const encoder = new TextEncoder();
  const data = encoder.encode(`${appId}${appKey}${channelId}${userId}${timestamp}`);
  const hash = await crypto.subtle.digest('SHA-256', data);
  return hex(hash);
}
function showToast(baseId, message) {
  $(`#${baseId}Body`).text(message);
  const toast = new bootstrap.Toast($(`#${baseId}`));
  toast.show();
}
// Enter your application ID and AppKey.
const appId = '';
const appKey = '';
AliRtcEngine.setLogLevel(0);
let aliRtcEngine;
const remoteVideoElMap = {};
const remoteVideoContainer = document.querySelector('#remoteVideoContainer');
function removeRemoteVideo(userId, type = 'camera') {
  const vid = `${type}_${userId}`;
  const el = remoteVideoElMap[vid];
  if (el) {
    aliRtcEngine.setRemoteViewConfig(null, userId, type === 'camera' ? 1: 2);
    el.pause();
    remoteVideoContainer.removeChild(el);
    delete remoteVideoElMap[vid];
  }
}
function listenEvents() {
  if (!aliRtcEngine) {
    return;
  }
  // Fired when a remote user comes online.
  aliRtcEngine.on('remoteUserOnLineNotify', (userId, elapsed) => {
    console.log(`User ${userId} joined the channel in ${elapsed} seconds.`);
    // Handle your business logic here, such as displaying the UI module for this user.
    showToast('onlineToast', `User ${userId} is online.`);
  });
  // Fired when a remote user goes offline.
  aliRtcEngine.on('remoteUserOffLineNotify', (userId, reason) => {
    // `reason` is a reason code. For details, see the API reference.
    console.log(`User ${userId} left the channel. Reason code: ${reason}`);
    // Handle your business logic here, such as destroying the UI module for this user.
    showToast('offlineToast', `User ${userId} is offline.`);
    removeRemoteVideo(userId, 'camera');
    removeRemoteVideo(userId, 'screen');
  });
  aliRtcEngine.on('bye', code => {
    // `code` is a reason code. For details, see the API reference.
    console.log(`bye, code=${code}`);
    // Handle your business logic here, such as exiting the call page.
    showToast('loginToast', `You have left the channel. Reason code: ${code}`);
  });
  aliRtcEngine.on('videoSubscribeStateChanged', (userId, oldState, newState, interval, channelId) => {
    // 'oldState' and 'newState' are AliRtcSubscribeState values.
    // Values: 0 (initialized), 1 (unsubscribed), 2 (subscribing), 3 (subscribed).
    // `interval` is the time between state changes, in milliseconds.
    console.log(`Subscription state of remote user ${userId} in channel ${channelId} changed from ${oldState} to ${newState}.`);
    const vid = `camera_${userId}`;
    // Example handler
    if (newState === 3) {
      const video = document.createElement('video');
      video.autoplay = true;
      video.className = 'video';
      remoteVideoElMap[vid] = video;
      remoteVideoContainer.appendChild(video);
      // The first parameter is an HTMLVideoElement.
      // The second parameter is the remote user ID.
      // The third parameter specifies the stream type: 1 for a camera stream, 2 for a screen sharing stream.
      aliRtcEngine.setRemoteViewConfig(video, userId, 1);
    } else if (newState === 1) {
      removeRemoteVideo(userId, 'camera');
    }
  });
  aliRtcEngine.on('screenShareSubscribeStateChanged', (userId, oldState, newState, interval, channelId) => {
    // 'oldState' and 'newState' are AliRtcSubscribeState values.
    // Values: 0 (initialized), 1 (unsubscribed), 2 (subscribing), 3 (subscribed).
    // `interval` is the time between state changes, in milliseconds.
    console.log(`Screen sharing stream subscription state for user ${userId} in channel ${channelId} changed from ${oldState} to ${newState}.`);
    const vid = `screen_${userId}`;
    // Example handler
    if (newState === 3) {
      const video = document.createElement('video');
      video.autoplay = true;
      video.className = 'video';
      remoteVideoElMap[vid] = video;
      remoteVideoContainer.appendChild(video);
      // The first parameter is an HTMLVideoElement.
      // The second parameter is the remote user ID.
      // The third parameter specifies the stream type: 1 for a camera stream, 2 for a screen sharing stream.
      aliRtcEngine.setRemoteViewConfig(video, userId, 2);
    } else if (newState === 1) {
      removeRemoteVideo(userId, 'screen');
    }
  });
}
$('#loginForm').submit(async e => {
  // Prevent the form's default submission action.
  e.preventDefault();
  const channelId = $('#channelId').val();
  const userId = $('#userId').val();
  const timestamp = Math.floor(Date.now() / 1000) + 3600 * 3;
  if (!channelId || !userId) {
    showToast('loginToast', 'Incomplete information.');
    return;
  }
  aliRtcEngine = AliRtcEngine.getInstance();
  listenEvents();
  try {
    const token = await generateToken(appId, appKey, channelId, userId, timestamp);
    // Set the channel mode. Valid values: 'communication' (communication mode), 'interactive_live' (interactive mode).
    aliRtcEngine.setChannelProfile('communication');
    // Set the user role. This method is effective only in interactive mode.
    // Valid values: 'interactive' (streamer, can publish and subscribe to streams), 'live' (viewer, can only subscribe to streams).
    // await aliRtcEngine.setClientRole('interactive');
    // Join the channel. Parameters like token and timestamp are typically returned from the server.
    await aliRtcEngine.joinChannel(
      {
        channelId,
        userId,
        appId,
        token,
        timestamp,
      },
      userId
    );
    showToast('loginToast', 'Successfully joined the channel.');
    $('#joinBtn').prop('disabled', true);
    $('#leaveBtn').prop('disabled', false);
    // Start the local preview.
    aliRtcEngine.setLocalViewConfig('localPreviewer', 1);
  } catch (error) {
    console.log('Failed to join the channel.', error);
    showToast('loginToast', 'Failed to join the channel.');
  }
});
$('#leaveBtn').click(async () => {
  Object.keys(remoteVideoElMap).forEach(vid => {
    const arr = vid.split('_');
    removeRemoteVideo(arr[1], arr[0]);
  });
  // Stop the local preview.
  await aliRtcEngine.stopPreview();
  // Leave the channel.
  await aliRtcEngine.leaveChannel();
  // Destroy the instance.
  aliRtcEngine.destroy();
  aliRtcEngine = undefined;
  $('#joinBtn').prop('disabled', false);
  $('#leaveBtn').prop('disabled', true);
  showToast('loginToast', 'Left the channel.');
});

Step 4: Run the demo

  1. In your terminal, navigate to the demo folder and run http-server -p 8080 to start an HTTP server.

  2. Open a new browser tab and navigate to localhost:8080/quick.html. Enter a Channel ID and a User ID, then click Join Channel.

  3. Open a second browser tab and navigate to localhost:8080/quick.html. Enter the same Channel ID but a different User ID, then click Join Channel.

  4. Verify that the media stream from the other user is automatically subscribed to and displayed on the page.