This guide provides you with an overview of the key objects you'll use in the Programmable Video API to build your video application with the Twilio Programmable Video JavaScript SDK.
Note: If you haven't already done so, take a look at the open source video collaboration app and quickstart apps. Then come back to this guide for more detail on how to add video to your own app.
If you've worked with WebRTC in the past, you'll find that Programmable Video provides a simple wrapper around WebRTC's lower-level APIs to make it easy to build rich audio and video applications. You can still access lower-level primitives, but that's not required to get started.
Additionally, Programmable Video provides the missing pieces required to use WebRTC to build sophisticated applications: Global STUN/TURN relays, media services for large-scale group conferences and recording, and signaling infrastructure are all included.
Let's start with an overview of the Programmable Video API:
Room
represents a real-time audio, data, video, and/or screen-share session, and is the basic building block for a Programmable Video application.Peer-to-peer Room
, media flows directly between participants. Supports up to 10 participants in a mesh topology.Group Room
, media is routed through Twilio's Media Servers.
Supports up to 50 participants.Participants
represent client applications that are connected to a Room and sharing audio, data, and/or video media with one another.Tracks
represent the individual audio, data, and video media streams that are shared within a Room.LocalTracks
represent the audio, data, and video captured from the local client's media sources (for example, microphone and camera).RemoteTracks
represent the audio, data, and video tracks from other participants connected to the Room.The following code samples illustrate common tasks that you as a developer may wish to perform related to a Room and its Participants.
To start using the JavaScript Programmable Video SDK in your apps, you need to perform a few basic tasks first.
You can install the JavaScript Video library using NPM.
npm install --save twilio-video@2.8.0
You can also include it in your application using our CDN.
Releases of twilio-video.js are hosted on a CDN, and you can include these directly in your web app using a <script>
tag.
<script src="//sdk.twilio.com/js/video/releases/2.15.3/twilio-video.min.js"></script>
Using this method, twilio-video.js
will set a browser global:
const Video = Twilio.Video;
Please refer to this table for information regarding supported browsers and operating systems.
API Keys represent credentials to access the Twilio API. They are used for two purposes:
For the purposes of this guide, we will create our API Key from the Twilio Console.
To execute the code samples below, you'll need to generate an Access Token. An Access Token is a short-lived credential used to authenticate your client-side application to Twilio.
You can generate an Access Token using either the Twilio CLI or a Twilio helper library. For application testing purposes, the Twilio CLI provides a quick way to generate Access Tokens that you can then copy/paste into your application.
To use the CLI, you will need to install the Twilio CLI and log in to your Twilio account from the command line; see the CLI Quickstart for instructions. Then, you can install the Token CLI plugin with the following command:
twilio plugins:install @twilio-labs/plugin-token
To generate an Access Token, run the following command. --identity
is a required argument and should be a string that represents the user identity for this Access Token.
twilio token:video --identity=<identity>
In a production application, your back-end server will need to generate an Access Token for every user in your application. Visit the User Identity and Access Token guide to learn more. You can find examples of how to generate an Access Token for a participant using Twilio's helper libraries in the User Identity and Access Token guide.
Call connect
to connect to a Room from your web application. Once connected, you can send and receive audio and video streams with other Participants who are connected to the Room.
1const { connect } = require('twilio-video');23connect('$TOKEN', { name:'my-new-room' }).then(room => {4console.log(`Successfully joined a Room: ${room}`);5room.on('participantConnected', participant => {6console.log(`A remote Participant connected: ${participant}`);7});8}, error => {9console.error(`Unable to connect to Room: ${error.message}`);10});
You must pass the Access Token when connecting to a Room. You may also optionally pass the following:
The name of the Room specifies which Room you wish to join. If client-side Room creation is enabled, and if a Room by that name does not already exist, it will be created upon connection. If a Room by that name is already active, you'll be connected to the Room and receive notifications from any other Participants also connected to the same Room. Room names must be unique within an account.
You can also create a Room using the Rooms REST API. Look at the REST API Rooms resource docs for more details.
1curl -XPOST 'https://video.twilio.com/v1/Rooms' \2-u '{API Key SID}:{API Secret}' \3-d 'UniqueName=DailyStandup'
Note: If you don't specify a Type
attribute, then Twilio will create a Room based on the default Type
in the Room Settings.
You can also set the Room Type
from the Room Settings page in the Twilio Video Console. Twilio will use the Room Type
set on Room Settings page, when you create a Room from the client-side or the REST API.
Note: Twilio will set the Room Type
as "Group" by default on the Room Settings page.
Once a Room is created, Twilio will fire a Room-created
webhook event, if the StatusCallback URL is set. You can set the StatusCallback URL on the Room Settings page, if you want create a Room from the client-side.
If you create a Room using the REST API, then you need to provide a StatusCallback URL while creating the Room.
1curl -XPOST 'https://video.twilio.com/v1/Rooms' \2-u '{API Key SID}:{API Secret}' \3-d 'UniqueName=DailyStandup' \4-d 'StatusCallback=https://hooks.yoursite.com/room-events' \5-d 'StatusCallbackMethod=POST' \6-d 'Type=group'
Recordings can be enabled only on Group Rooms. Set Recordings to Enabled
to record Participants when they connect to a Group Room.
Recordings can also be enabled on Group Rooms through via the Rest API at Room creation time by setting the RecordParticipantsOnConnect
property to true
.
1curl -XPOST 'https://video.twilio.com/v1/Rooms' \2-u 'SKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:your_api_key_secret' \3-d 'UniqueName=DailyStandup' \4-d 'Type=group' \5-d 'RecordParticipantsOnConnect=true' \6-d 'StatusCallback=http://example.org'
If you'd like to join a Room you know already exists, you handle that exactly the same way as creating a Room: just pass the Room name to the connect
method.
Once in a Room, you'll receive a "participantConnected" event for each Participant that successfully joins. Querying the participants
getter will return any existing Participants who have already joined the Room.
1const { connect } = require('twilio-video');23connect('$TOKEN', { name: 'existing-room' }).then(room => {4console.log(`Successfully joined a Room: ${room}`);5room.on('participantConnected', participant => {6console.log(`A remote Participant connected: ${participant}`);7});8}, error => {9console.error(`Unable to connect to Room: ${error.message}`);10});
You can capture local media from your device's microphone, camera or screen-share on different platforms in the following ways:
1const { connect, createLocalTracks } = require('twilio-video');23// Option 14createLocalTracks({5audio: true,6video: { width: 640 }7}).then(localTracks => {8return connect('$TOKEN', {9name: 'my-room-name',10tracks: localTracks11});12}).then(room => {13console.log(`Connected to Room: ${room.name}`);14});1516// Option 217connect('$TOKEN', {18audio: true,19name: 'my-room-name',20video: { width: 640 }21}).then(room => {22console.log(`Connected to Room: ${room.name}`);23});
Use Twilio's createLocalTracks API to gain access to the user's microphone and camera. Note that some browsers, such as Google Chrome, will only let your application access local media when your site is served from localhost
or over HTTPS.
When you join a Room, Participants may already be present. You can check for existing Participants by using the Room's participants
collection:
1// Log your Client's LocalParticipant in the Room2const localParticipant = room.localParticipant;3console.log(`Connected to the Room as LocalParticipant "${localParticipant.identity}"`);45// Log any Participants already connected to the Room6room.participants.forEach(participant => {7console.log(`Participant "${participant.identity}" is connected to the Room`);8});910// Log new Participants as they connect to the Room11room.once('participantConnected', participant => {12console.log(`Participant "${participant.identity}" has connected to the Room`);13});1415// Log Participants as they disconnect from the Room16room.once('participantDisconnected', participant => {17console.log(`Participant "${participant.identity}" has disconnected from the Room`);18});
When Participants connect to or disconnect from a Room that you're connected to, you'll be notified via Participant connection events:
1room.on('participantConnected', participant => {2console.log(`Participant connected: ${participant.identity}`);3});45room.on('participantDisconnected', participant => {6console.log(`Participant disconnected: ${participant.identity}`);7});
To see the Video Tracks being sent by remote Participants, we need to render them to the screen. Because Participants can publish Tracks at any time, we'll want to handle both
We can handle the former by iterating over tracks
and we can handle the latter by attaching an event listener for "trackSubscribed":
1// Attach the Participant's Media to a <div> element.2room.on('participantConnected', participant => {3console.log(`Participant "${participant.identity}" connected`);45participant.tracks.forEach(publication => {6if (publication.isSubscribed) {7const track = publication.track;8document.getElementById('remote-media-div').appendChild(track.attach());9}10});1112participant.on('trackSubscribed', track => {13document.getElementById('remote-media-div').appendChild(track.attach());14});15});
For RemoteParticipants that are already in the Room, we can attach their RemoteTracks by iterating over the Room's participants
:
1room.participants.forEach(participant => {2participant.tracks.forEach(publication => {3if (publication.track) {4document.getElementById('remote-media-div').appendChild(publication.track.attach());5}6});78participant.on('trackSubscribed', track => {9document.getElementById('remote-media-div').appendChild(track.attach());10});11});
You can mute your LocalAudioTracks (microphone) and LocalVideoTracks (camera) by calling the disable method as shown below:
1room.localParticipant.audioTracks.forEach(publication => {2publication.track.disable();3});45room.localParticipant.videoTracks.forEach(publication => {6publication.track.disable();7});
NOTE: Although disabling a LocalVideoTrack whose source is a camera stops sending media, the camera is still reserved by the LocalVideoTrack and hence its light still stays on. In some use cases, the desired behavior might be that the light should turn off when users mutes their camera. Although this method is not recommend, this can be achieved by calling stop on the LocalVideoTrack and unpublishing it from the Room:
1room.localParticipant.videoTracks.forEach(publication => {2publication.track.stop();3publication.unpublish();4});
When RemoteParticipants mute their media, you will be notified through a "disabled" event on the corresponding RemoteAudioTrack and/or RemoteVideoTrack. You can listen to this event as shown below:
1function handleTrackDisabled(track) {2track.on('disabled', () => {3/* Hide the associated <video> element and show an avatar image. */4});5}67room.participants.forEach(participant => {8participant.tracks.forEach(publication => {9if (publication.isSubscribed) {10handleTrackDisabled(publication.track);11}12publication.on('subscribed', handleTrackDisabled);13});14});
NOTE: If RemoteParticipants mute their video by unpublishing their LocalVideoTracks, then you can listen to the "unsubscribed" event on the RemoteTrackPublication instead:
1room.participants.forEach(participant => {2participant.tracks.forEach(publication => {3publication.on('unsubscribed', () => {4/* Hide the associated <video> element and show an avatar image. */5});6});7});
You can unmute your LocalAudioTracks and LocalVideoTracks by calling the enable method as shown below:
1room.localParticipant.audioTracks.forEach(publication => {2publication.track.enable();3});45room.localParticipant.videoTracks.forEach(publication => {6publication.track.enable();7});
NOTE: If you muted your camera by stopping and unpublishing the associated LocalVideoTrack, then you can unmute your local video by creating a new LocalVideoTrack and publishing it to the Room:
1const { createLocalVideoTrack } = require('twilio-video');23createLocalVideoTrack().then(localVideoTrack => {4return room.localParticipant.publishTrack(localVideoTrack);5}).then(publication => {6console.log('Successfully unmuted your video:', publication);7});
When RemoteParticipants unmute their media, you will be notified through an "enabled" event on the corresponding RemoteAudioTrack and/or RemoteVideoTrack. You can listen to this event as shown below:
1function handleTrackEnabled(track) {2track.on('enabled', () => {3/* Hide the avatar image and show the associated <video> element. */4});5}67room.participants.forEach(participant => {8participant.tracks.forEach(publication => {9if (publication.isSubscribed) {10handleTrackEnabled(publication.track);11}12publication.on('subscribed', handleTrackEnabled);13});14});
NOTE: If RemoteParticipants unmute their video by publishing new LocalVideoTracks, then you can listen to the "subscribed" event on the RemoteTrackPublication instead:
1room.participants.forEach(participant => {2participant.tracks.forEach(publication => {3publication.on('subscribed', () => {4/* Hide the avatar image and show the associated <video> element. */5});6});7});
Sometimes you need to make sure you're looking fantastic before entering a Room. We get it. Each SDK provides a means to render a local camera preview outside the context of an active Room:
1const { createLocalVideoTrack } = require('twilio-video');23createLocalVideoTrack().then(track => {4const localMediaContainer = document.getElementById('local-media');5localMediaContainer.appendChild(track.attach());6});
You can disconnect from a Room you're currently participating in. Other Participants will receive a "participantDisconnected" event:
1room.on('disconnected', room => {2// Detach the local media elements3room.localParticipant.tracks.forEach(publication => {4const attachedElements = publication.track.detach();5attachedElements.forEach(element => element.remove());6});7});89// To disconnect from a Room10room.disconnect();
The Programmable Video REST API allows you to control your video applications from your back-end server via HTTP requests.