Home
/ Blog /
Comparing the Code: Migrating from Agora to 100ms [Web Edition]October 10, 202216 min read
Share
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.
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).
This 100ms product walkthrough takes you through create a project from the 100ms dashboard. Create the project yourself by following the steps:
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.
<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" />
</div>
// Username (required for 100ms)
<div class="input-container">
<input id="uname" type="text" name="username" placeholder="Username" />
</div>
// Auth token (required for both Agora and 100ms)
<div class="input-container">
<input id="token" type="text" name="token" placeholder="Auth token" />
</div>
<button type="button" class="btn-primary" id="join-btn">Join</button>
</form>
const client = AgoraRTC.createClient({
codec: "vp8",
mode: "rtc",
});
// Join room on button click
document.getElementById("join-btn").onclick = () => {
client.join({
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();
hmsManager.triggerOnSubscribe();
const hmsStore = hmsManager.getStore();
const hmsActions = hmsManager.getHMSActions();
// Join room on button click
document.getElementById("join-btn").onclick = () => {
hmsActions.join({
userName: document.getElementById("uname").value,
authToken: document.getElementById("token").value,
...otherConfig
});
};
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.
// 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";
document.body.append(remotePlayerContainer);
// 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.
remoteVideoTrack.play(remotePlayerContainer);
}
// 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.
remoteAudioTrack.play();
}
// 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.
remotePlayerContainer.remove();
});
});
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";
document.body.append(localPlayerContainer);
// 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.
rtc.localVideoTrack.play(localPlayerContainer);
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) {
document.getElementById("peers-container").append(renderPeer(peer));
}
});
}
// 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);
peerTileDiv.append(videoElement);
return peerTile;
}
<div id="controls" class="control-bar">
<button id="mute-aud" class="btn-control">Mute</button>
<button id="mute-vid" class="btn-control">Hide</button>
...
</div>
// Global mute states
let audioEnabled = true;
let videoEnabled = true;
// Mute and unmute audio
document.getElementById("mute-aud").onclick = () => {
audioEnabled = !audioEnabled;
localTracks.audioTrack.setEnabled(audioEnabled);
document.getElementById("mute-aud").textContent = audioEnabled ? "Mute" : "Unmute";
};
// Mute and unmute video
document.getElementById("mute-vid").onclick = () => {
videoEnabled = !videoEnabled;
localTracks.videoTrack.setEnabled(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);
hmsActions.setLocalAudioEnabled(audioEnabled);
document.getElementById("mute-aud").textContent = audioEnabled ? "Mute" : "Unmute";
};
// Mute and unmute video
document.getElementById("mute-vid").onclick = () => {
const videoEnabled = !hmsStore.getState(selectIsLocalVideoEnabled);
hmsActions.setLocalVideoEnabled(videoEnabled);
document.getElementById("mute-vid").textContent = videoEnabled ? "Hide" : "Unhide";
};
<div id="controls" class="control-bar">
...
<button id="leave-btn" class="btn-control">Leave</button>
</div>
document.getElementById("leave-btn").onclick = async function () {
// Destroy the local audio and video tracks.
rtc.localAudioTrack.close();
rtc.localVideoTrack.close();
// 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"));
};
In Agora, the type of user role determines whether the user in the channel has streaming permissions. It is either host or audience.
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.
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.
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.
Engineering
Share
Related articles
See all articles