JAVASCRIPT • VIDEO CALLING • STEP-BY-STEP

How to add video calling to a JavaScript app

Build a two-person video call in your web app with RTCExpress. This guide shows the real integration flow: use a server-issued token, join an authorized room, handle an incoming video invitation, and render local and remote media.

1. Create a project and authorize both users

Video calling is available on the RTCExpress Pro plan. Create a project in the customer portal, then use your backend to authenticate each signed-in person and issue a short-lived token for the room they may enter.

Keep the App Secret on your server. It is used only when your backend requests the scoped token; browsers must never receive it.

2. Initialize the SDK and join the room

Set up event listeners before joining. Your application can then show clear status while the room membership and call state change.

import { RTCExpress } from "@rtc/sdk";

const rtc = new RTCExpress();

rtc.on("roomJoined", ({ roomId }) => {
  setCallStatus("Ready in " + roomId);
});

rtc.on("callState", ({ state }) => {
  setCallStatus(state);
});

await rtc.init({ serverUrl, appId, userId, token });
rtc.joinRoom("consultation-42");

The public SDK package is not published to npm yet. Use the local SDK package supplied with your RTCExpress integration, or contact support for video integration access.

3. Start or accept a video call

Only enable the call button after the user is in the authorized room. The caller targets another user ID; the invited user chooses whether to accept.

callButton.addEventListener("click", async () => {
  await rtc.videoCallUser("user_b");
});

rtc.on("callInvite", ({ callType, fromUserId }) => {
  if (callType !== "video") return;
  showIncomingVideoCall(fromUserId);
});

acceptButton.addEventListener("click", () => rtc.acceptCall());
declineButton.addEventListener("click", () => rtc.rejectCall());
hangUpButton.addEventListener("click", () => rtc.endCall());

Request camera and microphone permission only when the person starts or accepts the call. Explain why the permission is needed before the browser prompt appears.

4. Render the local and remote video

Attach the local media when it is available and add each received remote track to the other participant's video element. Your UI should also handle a missing camera, mute states, and a call that ends before media starts.

rtc.on("localStream", ({ stream }) => {
  localVideo.srcObject = stream;
  localVideo.muted = true;
});

rtc.on("remoteTrack", ({ stream }) => {
  remoteVideo.srcObject = stream;
});

cameraButton.addEventListener("click", () => {
  rtc.muteCamera(cameraIsMuted);
  cameraIsMuted = !cameraIsMuted;
});

Use playsinline on mobile video elements and provide a labelled hang-up control. Your interface should not assume that a camera is always present or permitted.

5. Test the complete call flow

  • Use two different user IDs with access to the same project and room.
  • Test on two real devices, not two tabs on the same computer only.
  • Reject camera or microphone permission and confirm the app gives a useful recovery message.
  • Switch networks during a call and test reconnect and hang-up behaviour.
  • Try a token for another room and confirm your backend prevents unauthorized access.

Add chat or voice alongside video

For a better product experience, let people start with in-app chat, then move to voice or video when needed. The voice, video, and chat API guide explains when each option fits.

Start your video integration

Create a project, issue room-scoped tokens from your backend, and test a real two-device video call.

Create a projectExplore video calling