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

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

October 10, 202211 min read


Blog Cover.png


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.

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, we can dive into the process of migrating your app to 100ms.

Migrating to 100ms SDK

Once you’ve explored the basic concepts of the 100ms SDK and how it compares with the Agora Video SDK, you’re all set to start the migration process. The first step is to create a project.

Creating your Project

To help you get started by creating your first 100ms project, we have a product walkthrough by Arjun, where he demonstrates how to create a project from the Dashboard.

Once an app is created on the dashboard, we have to refactor the code in order to migrate the app from the Agora Video SDK to the 100ms SDK.

Here, refactoring involves changing just a few lines of code.

1. 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>

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.


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,


// 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.

  1. Click on the Join room button.

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

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

2. Rendering Audio/Video

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.


    // 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(options.appId, document.getElementById("cname").value, document.getElementById("token").value);
            // 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!");


// 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;

3. 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>

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


// 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";


// 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";

4. 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();


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

5. 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.
  • 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.
  • 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.


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