All Products
Search
Document Center

ApsaraVideo Live:Getting started with the ARTC Web SDK

Last Updated:Jan 27, 2026

ApsaraVideo Real-time Communication (ARTC) SDK for Web is a set of web-based real-time communication development tools provided by Alibaba Cloud. The SDK allows you to integrate real-time interaction features, such as high-quality audio and video calling and real-time messaging, into a web client. This topic describes how to build your own ARTC application. 

Step 1: Create an application

  1. Log on to the ApsaraVideo Live console.

  2. In the navigation pane on the left, click 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 a success message appears, refresh the Applications page to view your new ARTC application.

    Note

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

Step 2: Get the AppID and AppKey

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

image

Step 3: Integrate and use the SDK

  1. Integrate the SDK.

    Use a script

    Import the SDK script into your HTML page. 

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

    Use npm

    Use npm to install the SDK in your project. 

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

    // Use one of the following methods.
    // Run the following command if you use npm to integrate the SDK.
    import AliRtcEngine from 'aliyun-rtc-sdk';
    // Run the following command if you use a script to integrate the SDK.
    const AliRtcEngine = window.AliRtcEngine;
    
    // Check the environment.
    const checkResult = await AliRtcEngine.isSupported();
    if (!checkResult.support) {
      // If the environment is unavailable, you are prompted to change or upgrade the browser.
    }
    
    // Create an AliRtcEngine instance, which can be saved as a global variable.
    const aliRtcEngine = AliRtcEngine.getInstance();
    
  3. After you create the AliRtcEngine instance, listen for and handle related events.

    // Handle the event that the local user leaves the channel.
    aliRtcEngine.on('bye', (code) => {
      // code indicates the reason for leaving the channel. For more information, see the API reference.
      console.log(`bye, code=${code}`);
      // Configure your handling logic, such as exiting the calling page.
    });
    
    // Listen to the event that a remote user gets online.
    aliRtcEngine.on('remoteUserOnLineNotify', (userId, elapsed) => {
      console.log(`The user ${userId} has joined the channel. Time consumed: ${elapsed} seconds.`);
      // Configure your handling logic, such as displaying the module of the remote user.
    });
    
    // Listen to the event that a remote user gets offline.
    aliRtcEngine.on('remoteUserOffLineNotify', (userId, reason) => {
      // reason indicates the reason why the remote user gets offline. For more information, see the API reference.
      console.log(`The user ${userId} has left the channel. Reason: ${reason}.`);
      // Configure your handling logic, such as destroying the module of the remote user.
    });
    
    // Listen to the event that the state of subscription to a remote stream changes.
    aliRtcEngine.on('videoSubscribeStateChanged', (userId, oldState, newState, interval, channelId) => {
      // oldState and newState are parameters of AliRtcSubscribeState. The valid values of the parameters are 0 (initialized), 1 (unsubscribed), 2 (subscribing), and 3 (subscribed).
      // interval specifies the time elapsed for the change between two states, in milliseconds.
      console.log(`The state of subscription to the remote user ${userId} in the channel ${channelId} is changed from ${oldState} to ${newState}.`);
      // Configure the handling logic for the remote stream.
      // Call setRemoteViewConfig to play the remote stream if the value of newState changes to 3.
      // Stop the playback if the value of newState changes to 1.
    });
    
    // Listen to the event that the authentication information expires.
    aliRtcEngine.on('authInfoExpired', () => {
      // The callback is triggered when the authentication information expires.
      // You must obtain data such as a token again and then call refreshAuthInfo to update the authentication information.
      aliRtcEngine.refreshAuthInfo({
        userId,
        token,
        timestamp
      });
    });
    
    // Listen to the event that the authentication information is about to expire.
    aliRtcEngine.on('authInfoWillExpire', () => {
      // The callback is triggered 30 seconds before the authentication information expires. After you receive the callback, update the authentication information in a timely manner.
      // If you want to keep staying in the channel, you must obtain data such as a token again and then call joinChannel to join the channel again.
    });
    
  4. Optional. Set the channel mode. The default mode is communication mode. For more information, see Set the channel mode and user role.

    // Set the channel mode. Valid arguments: communication (the communication mode) and interactive_live (the interactive mode).
    aliRtcEngine.setChannelProfile('interactive_live');
    // Set the user role. The method is valid only in the interactive mode.
    // Valid arguments: interactive (the streamer) and live (the viewer). A streamer can publish and pull streams. A viewer can only pull streams.  
    aliRtcEngine.setClientRole('interactive');
  5. Join a channel. For information about how to generate a token, see Implement token-based authentication. You can use one of the following methods to transmit the token generated:

    • Single-parameter input

      const userName = 'User 1'; // Customize the user name. The name supports Chinese characters.
      
      try {
        // Implement the fetchToken function to obtain the Base64 encoded token from the server.
        const base64Token = await fetchToken();
        await aliRtcEngine.joinChannel(base64Token, userName);
        // Continue to perform subsequent operations if you succeed in joining the channel.
      } catch (error) {
        // Handle potential errors if you fail to join the channel.
      }
    • Multi-parameter input

      // Generate a token on the server side or on the on-premises machine. 
      // Note: For your data security, never expose the token generation logic and AppKey in client-side code. 
      const appId = 'yourAppId'; // Enter the application ID obtained from the ApsaraVideo Live console.
      const appKey = 'yourAppKey'; // Enter the AppKey obtained from the ApsaraVideo Live console. Note: Do not expose your AppKey in the production environments.  
      const channelId = 'AliRtcDemo'; // Specify the channel ID. The ID can contain only letters and digits.
      const userId = 'test1'; // Specify the user ID. The ID can contain only letters and digits.
      const userName = 'User 1'; // Specify the username. The name supports Chinese characters.
      const timestamp = Math.floor(Date.now() / 1000) + 3600; // Specify the expiration time of the token. For example, a value of 3600 specifies that the token expires an hour after it is generated.
      
      try {
        const token = await generateToken(appId, appKey, channelId, userId, timestamp);
        // Join the channel. The parameters, token, and nonce are generally returned from the server.
        // The channelId, userId, appId, and timestamp parameters must be the same as those used to generate the token.
        await aliRtcEngine.joinChannel({
          channelId,
          userId,
          appId,
          token,
          timestamp,
        }, userName);
        // Continue to perform subsequent operations if you succeed in joining the channel.
      } catch (error) {
        // Handle potential errors if you fail to join the channel.
      }
  6. Start preview and stream ingest. By default, local audio and video data is automatically collected and ingested to Global Realtime Transport Network (GRTN) after you join the channel. You can preview your local stream by performing the following operations. 

    1. Add the VIDEO element whose id is localPreviewer to the HTML code. 

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

      // For the first parameter, pass in HTMLVideoElement or specify the element ID. If you set the value to null, the preview is stopped.
      // Set the value of the second parameter to 1 or 2. The value 1 specifies that the camera stream is previewed. The value 2 specifies that the screen-sharing stream is previewed.
      aliRtcEngine.setLocalViewConfig('localPreviewer', 1);
  7. Subscribe to remote audio and video streams. By default, you are automatically subscribed to the audio and video streams of other streamers after you join the channel. Audio streams are automatically played. To play camera streams or screen-sharing streams, call setRemoteViewConfig to configure the settings. 

    1. Add the DIV element whose id is remoteVideoContainer as a container to the HTML code. 

      <div id="remoteVideoContainer"></div>
    2. If the state of subscription to a remote video stream is changed to subscribed, call setRemoteViewConfig to play the stream. If the state of subscription to a remote video stream is changed to unsubscribed, remove the stream. 

      // Save the Video element.
      const remoteVideoElMap = {};
      // Set the remoteVideoContainer 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];
        }
      }
      
      // The following sample code is the same as the description about videoSubscribeStateChanged in Step 2.
      aliRtcEngine.on('videoSubscribeStateChanged', (userId, oldState, newState, interval, channelId) => {
        // oldState and newState are parameters of AliRtcSubscribeState. The valid values of the parameters are 0 (initialized), 1 (unsubscribed), 2 (subscribing), and 3 (subscribed).
        // interval specifies the time elapsed for the change between two states, in milliseconds.
        console.log(`The state of subscription to the remote user ${userId} in the channel ${channelId} is changed from ${oldState} to ${newState}.`);
        // The following sample code provides a handling example.
        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);
          // For the first parameter, pass in HTMLVideoElement.
          // For the second parameter, specify the ID of the remote user.
          // Set the value of the third parameter to 1 or 2. The value 1 specifies that the camera stream is previewed. The value 2 specifies that the screen-sharing stream is previewed.
          aliRtcEngine.setRemoteViewConfig(video, userId, 1);
        } else if (newState === 1) {
          removeRemoteVideo(userId);
        }
      });
  8. End the process.

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

Demo

Important

The JavaScript part of the demo code includes the generateToken method for calculating a token. For security reasons, do not expose this code or your AppKey in a client-side JavaScript file. This can lead to information leaks and security vulnerabilities. We recommend that you generate tokens on your server and retrieve them on the frontend by making an authenticated API call.

Prerequisites

To try the demo, you need to start an HTTP service in your development environment. If you do not have the npm package that is used to install http-server, run the npm install --global http-server command to install the server globally. 

Step 1: Create a directory

Create a folder named demo, and create the quick.html and quick.js files in the folder based on the following directory structure. 

- demo
  - quick.html
  - quick.js

Step 2: Edit the quick.html file

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

Sample code

<!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">Logon 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 gets 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 gets 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 user</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 the quick.js file

Copy the following code to the quick.js file, specify the appId and appKey parameters in the code, and save the file. 

Sample code

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();
}

// Specify 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;
  }
  // Listen to the event that a remote user gets online.
  aliRtcEngine.on('remoteUserOnLineNotify', (userId, elapsed) => {
    console.log(`The user ${userId} has joined the channel. Time consumed: ${elapsed} seconds.`);
    // Configure your handling logic, such as displaying the module of the remote user.
    showToast('onlineToast', `The user ${userId} is online.`);
  });

  // Listen to the event that a remote user gets offline.
  aliRtcEngine.on('remoteUserOffLineNotify', (userId, reason) => {
    // reason indicates the reason why the remote user gets offline. For more information, see the API reference.
    console.log(`The user ${userId} has left the channel. Reason: ${reason}.`);
    // Configure your handling logic, such as destroying the module of the remote user.
    showToast('offlineToast', `The user ${userId} is offline.`);
    removeRemoteVideo(userId, 'camera');
    removeRemoteVideo(userId, 'screen');
  });

  aliRtcEngine.on('bye', code => {
    // code indicates the reason for leaving the channel. For more information, see the API reference.
    console.log(`bye, code=${code}`);
    // Configure your handling logic, such as exiting the calling page.
    showToast('loginToast', `You have left the channel. Reason: ${code}.`);
  });

  aliRtcEngine.on('videoSubscribeStateChanged', (userId, oldState, newState, interval, channelId) => {
    // oldState and newState are parameters of AliRtcSubscribeState. The valid values of the parameters are 0 (initialized), 1 (unsubscribed), 2 (subscribing), and 3 (subscribed).
    // interval specifies the time elapsed for the change between two states, in milliseconds.
    console.log(`The state of subscription to the remote user ${userId} in the channel ${channelId} is changed from ${oldState} to ${newState}.`);
    const vid = `camera_${userId}`;
    // The following sample code provides a handling example.
    if (newState === 3) {
      const video = document.createElement('video');
      video.autoplay = true;
      video.className = 'video';
      remoteVideoElMap[vid] = video;
      remoteVideoContainer.appendChild(video);
      // For the first parameter, pass in HTMLVideoElement.
      // For the second parameter, specify the ID of the remote user.
      // Set the value of the third parameter to 1 or 2. The value 1 specifies that the camera stream is previewed. The value 2 specifies that the screen-sharing stream is previewed.
      aliRtcEngine.setRemoteViewConfig(video, userId, 1);
    } else if (newState === 1) {
      removeRemoteVideo(userId, 'camera');
    }
  });

  aliRtcEngine.on('screenShareSubscribeStateChanged', (userId, oldState, newState, interval, channelId) => {
    // oldState and newState are parameters of AliRtcSubscribeState. The valid values of the parameters are 0 (initialized), 1 (unsubscribed), 2 (subscribing), and 3 (subscribed).
    // interval specifies the time elapsed for the change between two states, in milliseconds.
    console.log(`The state of subscription to the screen-sharing stream of the remote user ${userId} in the channel ${channelId} is changed from ${oldState} to ${newState}`);
    const vid = `screen_${userId}`;
    // The following sample code provides a handling example.    
    if (newState === 3) {
      const video = document.createElement('video');
      video.autoplay = true;
      video.className = 'video';
      remoteVideoElMap[vid] = video;
      remoteVideoContainer.appendChild(video);
      // For the first parameter, pass in HTMLVideoElement.
      // For the second parameter, specify the ID of the remote user.
      // Set the value of the third parameter to 1 or 2. The value 1 specifies that the camera stream is previewed. The value 2 specifies that the screen-sharing stream is previewed.
      aliRtcEngine.setRemoteViewConfig(video, userId, 2);
    } else if (newState === 1) {
      removeRemoteVideo(userId, 'screen');
    }
  });
}

$('#loginForm').submit(async e => {
  // Prevent the form from being submitted by default.
  e.preventDefault();
  const channelId = $('#channelId').val();
  const userId = $('#userId').val();
  const timestamp = Math.floor(Date.now() / 1000) + 3600 * 3;

  if (!channelId || !userId) {
    showToast('loginToast', 'The data is incomplete.');
    return;
  }

  aliRtcEngine = AliRtcEngine.getInstance();
  listenEvents();

  try {
    const token = await generateToken(appId, appKey, channelId, userId, timestamp);
    // Set the mode of the channel. Valid values: communication and interactive_live.
    aliRtcEngine.setChannelProfile('communication');
    // Specify the user role. This setting takes effect only in the interactive mode.
    // Valid values: interactive and live. The value interactive specifies the streamer role who can ingest and pull streams. The value live specifies the viewer role who can only pull streams.
    // aliRtcEngine.setClientRole('interactive');
    // Join the channel. The token and nonce parameters are returned by the server.
    await aliRtcEngine.joinChannel(
      {
        channelId,
        userId,
        appId,
        token,
        timestamp,
      },
      userId
    );
    showToast('loginToast', 'You succeed in joining the channel.');
    $('#joinBtn').prop('disabled', true);
    $('#leaveBtn').prop('disabled', false);

    // Start preview.
    aliRtcEngine.setLocalViewConfig('localPreviewer', 1);
  } catch (error) {
    console.log('Failed to join the channel.', error);
    showToast('loginToast', 'You fail 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', 'You have left the channel.');
});

Step 4: Run the demo

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

  2. Create a tab in the browser. On the tab, access localhost:8080/quick.html, fill in your channel ID and user ID, and click the Join Channel button. 

  3. Create another tab in the browser. On the tab, access localhost:8080/quick.html, fill in the same channel ID and another user ID, and click the Join Channel button. 

  4. Verify that you are automatically subscribed to the media stream of the remote user.