/ Blog / 

Comparing the Code: Migrating from Agora to 100ms [Web Edition]

Comparing the Code: Migrating from Agora to 100ms [Web Edition]

October 10, 202216 min read


Agora Migration Guide | Cover Image

Trying to migrate your tech stack from Agora to 100ms?

This guide will help you do exactly that. But first, let’s look at the differences between Agora and 100ms’ Web SDKs — Agora Video JS SDK and 100ms JS SDK, from a technical perspective. We’ve also offered a comparison between them at the code-level to help you scope code changes for your migration.

Understanding the Differences in SDKs

First, let’s explore some basic conceptual differences between the 100ms SDK and the Agora Video SDK from an implementation POV.

Agora 100ms
Rooms and Authentication Agora uses the concept of channels. joinChannel call is made to create and join a channel. App clients that pass the same channel name join the same channel.
A token is a credential that authenticates a user when an app client joins a channel.
100ms uses the concept of rooms. The rooms are identified by a “Room Id” that is generated automatically.
Clients are authenticated to join the room requested with the room id and configuration, using the auth token
Peers Every user in a channel has a unique user ID. To access the local device methods LocalTrack object is used. For every other participant in the room, RemoteTrack object is used. Every peer corresponds to a Peer object including the local device.
The methods related to the local device are associated with HMSActions that directly connect to the 100ms backend.
Media Every user can create a set of Tracks and publish them to the channel. Objects of type LocalTrack and RemoteTrack are used for sending/managing audio and video tracks. UI can be rendered by listening to every individual track with callback functions. No complex concept like “tracks” for handling media. Straightforward methods available on HMSActions to enable/disable camera, microphone, screen-share.
Easy to render UI with appropriate Selectors and callback functions.
Permissions and Pub/Sub The type of user role determines whether the user in the channel has streaming permissions. A user role is either host or audience. Roles can be used to define permissions for a peer, including Pub/Sub permissions, permission to mute/unmute another peer in the same room & more.
Room Configuration The channels have to be individually configured every time they are created. A Template consists of all configurations for a room, along with the possible Roles to define peer behaviour.
When a room is created dynamically, it can be configured with a pre-defined or a custom Template by referring to its ID or name. Easy to create your own Templates from the Dashboard.

Now that we’ve discussed differences in these SDKs, let’s explore the actual process of migrating your app to 100ms. For this article, let’s walk through the steps on migrating to 100ms JS SDK (Web) with some sample code comparison from the Agora Video SDK (Web).

Creating your Project

This 100ms product walkthrough takes you through create a project from the 100ms dashboard. Create the project yourself by following the steps:

  1. Go to 100ms Dashboard and sign up for an account, if you haven’t already.
  2. Now, click on the Create New button. You can either choose from one of the starter kits or create your own custom app. If you’re creating a custom app, create the roles and choose a subdomain to get started.

Now that you’ve created an app, let’s implement some real features with 100ms. While doing so, we’ll compare our code with that of Agora side-by-side.

Joining the Room

<form id="join">
    <h2>Join Room</h2>

    // Channel name (required for Agora)
    <div class="input-container">
        <input id="cname" type="text" name="channelname" placeholder="Channel name" />

		// Username (required for 100ms)
    <div class="input-container">
        <input id="uname" type="text" name="username" placeholder="Username" />

		// Auth token (required for both Agora and 100ms)
    <div class="input-container">
        <input id="token" type="text" name="token" placeholder="Auth token" />
    <button type="button" class="btn-primary" id="join-btn">Join</button>


const client = AgoraRTC.createClient({
  codec: "vp8",
  mode: "rtc",

// Join room on button click
document.getElementById("join-btn").onclick = () => {
				APPID: "Specify an APPID for your Project", 
				CHANNEL: document.getElementById("cname").value, 
				TOKEN: document.getElementById("token").value,


The 100ms SDK gives you access to abstracted objects like hmsStore and hmsActions so as to easily manage the state of your room. We’ll see how these objects are used in the upcoming sections.

// Initialize HMS Store
const hmsManager = new HMSReactiveStore();
const hmsStore = hmsManager.getStore();
const hmsActions = hmsManager.getHMSActions();

// Join room on button click
document.getElementById("join-btn").onclick = () => {
        userName: document.getElementById("uname").value,
        authToken: document.getElementById("token").value,


We can use the dashboard to create a temporary token that will automatically expire in 24hrs. You can follow this guide to generate tokens on your server.

Auth tokens are generally issued by the server upon request from the client application.

  1. In your Dashboard, go to the Rooms section. Now, click on the RoomId of the room you want to generate a token for.

100ms Dashboard

  1. Click on the Join room button.

100ms Join Room

  1. Now, by clicking the key icon, you can generate and copy the token for your room based on the role.

100ms Token Generation

  1. Once this token has expired (after 24hrs), you can generate another token by following the same method.

Rendering Audio/Video


// Listen for the "user-published" event, from which you can get an AgoraRTCRemoteUser object.
rtc.client.on("user-published", async (user, mediaType) => {
    // Subscribe to the remote user when the SDK triggers the "user-published" event
    await rtc.client.subscribe(user, mediaType);
    console.log("subscribe success");

    // If the remote user publishes a video track.
    if (mediaType === "video") {
        // Get the RemoteVideoTrack object in the AgoraRTCRemoteUser object.
        const remoteVideoTrack = user.videoTrack;
        // Dynamically create a container in the form of a DIV element for playing the remote video track.
        const remotePlayerContainer = document.createElement("div");
        // Specify the ID of the DIV container. You can use the uid of the remote user.
        remotePlayerContainer.id = user.uid.toString();
        remotePlayerContainer.textContent =
            "Remote user " + user.uid.toString();
        remotePlayerContainer.style.width = "640px";
        remotePlayerContainer.style.height = "480px";

        // Play the remote video track.
        // Pass the DIV container and the SDK dynamically creates a player in the container for playing the remote video track.

    // If the remote user publishes an audio track.
    if (mediaType === "audio") {
        // Get the RemoteAudioTrack object in the AgoraRTCRemoteUser object.
        const remoteAudioTrack = user.audioTrack;
        // Play the remote audio track. No need to pass any DOM element.

    // Listen for the "user-unpublished" event
    rtc.client.on("user-unpublished", (user) => {
        // Get the dynamically created DIV container.
        const remotePlayerContainer = document.getElementById(user.uid);
        // Destroy the container.

window.onload = function () {
    document.getElementById("join").onclick = async function () {
        // Join an RTC channel.
        const uid = await rtc.client.join(
        // Create a local audio track from the audio sampled by a microphone.
        rtc.localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
        // Create a local video track from the video captured by a camera.
        rtc.localVideoTrack = await AgoraRTC.createCameraVideoTrack();
        // Publish the local audio and video tracks to the RTC channel.
        await rtc.client.publish([rtc.localAudioTrack, rtc.localVideoTrack]);
        // Dynamically create a container in the form of a DIV element for playing the local video track.
        const localPlayerContainer = document.createElement("div");
        // Specify the ID of the DIV container. You can use the uid of the local user.
        localPlayerContainer.id = uid;
        localPlayerContainer.textContent = "Local user " + uid;
        localPlayerContainer.style.width = "640px";
        localPlayerContainer.style.height = "480px";

        // Play the local video track.
        // Pass the DIV container and the SDK dynamically creates a player in the container for playing the local video track.
        console.log("publish success!");


In 100ms, the concept of “tracks” is abstracted to provide the best developer experience and avoid mistakes that pop up when handling them explicitly. The hmsStore.subscribe is automatically called on every peer join/leave event. This means that you don’t have to worry about every single edge case for a peer joining and leaving the room.

// Callback for handling peer rendering - called on peer join/leave events
hmsStore.subscribe(renderPeers, selectPeers);

// Display a tile for each peer in the peer list
function renderPeers() {
    document.getElementById("peers-container").innerHTML = "";
    const peers = hmsStore.getState(selectPeers);

    peers.forEach((peer) => {
        if (peer.videoTrack) {

// Render a single peer
function renderPeer(peer) {
    const peertile = document.createElement("peer-tile");
    peertile.id = peer.id;
    peertile.innerText = peer.name;

    const videoElement = document.createElement("peer-video");
    videoElement.className = "video";
    videoElement.autoplay = true;
    videoElement.muted = true;
    videoElement.playsinline = true;

    hmsActions.attachVideo(peer.videoTrack, videoElement);

    return peerTile;

Mute and Unmute Audio/Video

<div id="controls" class="control-bar">
    <button id="mute-aud" class="btn-control">Mute</button>
    <button id="mute-vid" class="btn-control">Hide</button>


// Global mute states
let audioEnabled = true;
let videoEnabled = true;

// Mute and unmute audio
document.getElementById("mute-aud").onclick = () => {
		audioEnabled = !audioEnabled;
    document.getElementById("mute-aud").textContent = audioEnabled ? "Mute" : "Unmute";

// Mute and unmute video
document.getElementById("mute-vid").onclick = () => {
		videoEnabled = !videoEnabled;
    document.getElementById("mute-vid").textContent = videoEnabled ? "Hide" : "Unhide";


100ms offers simple yet consistent methods for performing common operations like muting/unmuting audio and switching video on/off.

// Mute and unmute audio
document.getElementById("mute-aud").onclick = () => {
    const audioEnabled = !hmsStore.getState(selectIsLocalAudioEnabled);
    document.getElementById("mute-aud").textContent = audioEnabled ? "Mute" : "Unmute";

// Mute and unmute video
document.getElementById("mute-vid").onclick = () => {
    const videoEnabled = !hmsStore.getState(selectIsLocalVideoEnabled);
    document.getElementById("mute-vid").textContent = videoEnabled ? "Hide" : "Unhide";

Leaving the room

<div id="controls" class="control-bar">
    <button id="leave-btn" class="btn-control">Leave</button>


document.getElementById("leave-btn").onclick = async function () {
    // Destroy the local audio and video tracks.

    // Traverse all remote users.
    rtc.client.remoteUsers.forEach((user) => {
        // Destroy the dynamically created DIV containers.
        const playerContainer = document.getElementById(user.uid);
        playerContainer && playerContainer.remove();

    // Leave the channel.
    await rtc.client.leave();


Similar to Agora, a peer can leave the room with the use of a single method hmsActions.leave in 100ms.

document.getElementById("leave-btn").onclick = function () {
    hmsActions.leave().then(() => console.log("User left the room"));

User roles and managing permissions

In Agora, the type of user role determines whether the user in the channel has streaming permissions. It is either host or audience.

  • host - The host refers to a user who has streaming permissions in the channel.
  • audience - The audience are users who do not have streaming permissions in the channel. The audience can only subscribe to the remote audio and video streams, but cannot publish the audio and video streams.

In 100ms, it is easier to manage user permissions and create custom roles to fit your use case better. Understand roles and how they can help better here.

  • When creating a project, you can choose to create a ‘Custom App’.
  • Click on ‘Create a custom role’ to get started defining custom roles for your app. Creating custom role
  • Edit the name of the new role and modify the ‘Publish Strategy’. Whether the peer with the role should be able to share audio/video/screen or not. Publish Strategies
  • Define the permissions for the role. Should the users with the new role be allowed to mute participants, remove them from meetings, start/end sessions, etc. Roles and permission


You can explore our Javascript Quickstart example below, implemented with a similar source code.

Migrating to 100ms SDK is a simple task and, as displayed by the comparison above, well worth the effort in the long run.

Next Steps

If you’re interested in building production-grade apps from scratch with 100ms, here are some resources you might be interested in:

Try the 100ms SDK for yourself, and you’ll know exactly how it can empower your video app to perform better with less developer effort.



Related articles

See all articles