Suggestions

close search

Add Messaging, Voice, and Authentication to your apps with Vonage Communications APIs

Visit the Vonage API Developer Portal

Customizing the UI — Web

These are the adjustments you can make to customize the user interface of OpenTok videos:

Setting the initial position and dimensions of a video

When you publish a video, you can specify the DOM element (or its ID) that the publisher will replace. You can also specify the initial width and height of the publisher:

// Replace the first parameter with ID of the target DOM element:
const publisher = OT.initPublisher('myPublisherElementId',
                                 {width:400, height:300});
session.publish(publisher);

You can also set the insertMode property of the options you pass into the OT.initPublisher() method to 'append' to have the publisher appended as a child of the DOM element (container) you specify:

const publisherOptions = {
  insertMode: 'append',
  width: 400,
  height: 300
};
const publisher = OT.initPublisher('publisherContainerElementId', publisherOptions);
session.publish(publisher);

Similarly, when you subscribe to a stream, you can specify the target DOM element (or its ID). You can also specify the initial width and height of the subscriber:

const options = {width: 400, height: 300, insertMode: 'append'}
const subscriber = session.subscribe(stream, 'containerElementId', options);

You can also specify the initial width and height of as a percentage of the size of the parent DOM element:

const publisherOptions = {
  insertMode: 'append',
  width: '100%',
  height: '100%'
};
const publisher = OT.initPublisher(publisherContainerElement, publisherOptions);
session.publish(publisher);

If you do not specify a replace element ID (or set it to null), the application appends a new DOM element to the HTML body. The default width is 264 pixels, and the default height is 198 pixels.

If you want to apply multiple CSS rules, apply it to the parent (container) DOM element:

<style>
	#publisherContainer.large
	{ width: 640px; height: 480px; }
	#publisherContainer.small
	{ width:100px; height: 100px; }
</style>
<div id="publisherContainer"></div>
<script>
	const publisher = OT.initPublisher('publisherContainer',
	{width: '100%', height: '100%', insertMode: 'append'}
</script>

To apply a different size to a video on a mobile device, use CSS media queries:

<style>
	#publisherContainer
	{ width: 100px; height: 100px; }
	@media screen and (max-width: 650px) {
	#publisherContainer
	{ width: 89px; height: 50px; }
	}
</style>
<div id="publisherContainer"></div>
<script>
	const publisher = OT.initPublisher('publisherContainer',
	{width: '100%', height: '100%', insertMode: 'append'}
</script>

If you will want to dynamically resize the video, set the insertMode to 'append' and set the height and width to '100%'.

See the next section for information on resizing or repositioning a publisher or subscriber video.

Resizing or repositioning a video

The element property of the Publisher or Subscriber object is its HTML DOM element. You can reposition this in the HTML DOM and resize the element by editing its style.width and style.height properties, just as you would any other DOM element:

document.getElementById("target").appendChild(publisher.element);
publisher.element.style.width = "100px";
publisher.element.style.height = "75px";

If you specify the initial width and height of the Publisher or Subscriber object as a percentage (such as "100%:), you can resize it by resizing one of its parent elements. The following example includes a function that resizes a Publisher:

<script type="text/javascript">
	const publisherOptions = {
		insertMode: "append",
		height: "100%",
		width: "100%"
	}
	const publisher = OT.initPublisher("publisherContainer", publisherOptions);
	session.publish(publisher);

	function resizePublisher() {
		const publisherContainer = document.getElementById("publisherContainer");
		publisherContainer.style.width = "1000px";
		publisherContainer.style.height = "750px";
	}
</script>

<div id="container">
	<div id="publisherContainer"></div>
	<a href="javascript:resizePublisher()">resize</a>
</div>

See the previous section, Setting the initial dimensions of a video for information on setting the initial position and dimensions of a publisher or subscriber.

Important: If you disable the default user interface of the publisher or subscriber by setting insertDefaultUI to false when instantiating the Publisher or Subscriber object, the element property of the Publisher or Subscriber will be undefined. Listen for the videoElementCreated event and use the element property of the event object to access the HTML DOM element for the Publisher or Subscriber. See Directly accessing the video element for a Publisher or Subscriber.

Adding a name for a published stream

When you create a Publisher, you can (optionally) specify a name to be displayed in the video:

// Replace the first parameter with the target element ID:
const publisher = OT.initPublisher("myPublisher",
                                 {name: "John"})
session.publish(publisher);

You can use this name to identify the client.

Note that you can also add metadata about the client when you create a token. This name is not automatically displayed in the video. However, by adding the data when you create a token, you can add information more securely (since tokens are created on the server, not on the client. For more information, see Creating a token.

Clients can choose to hide the name in a Publisher or Subscriber view. See the next section

Displaying or hiding the name in a video

When you publish a stream, you can specify a name to be displayed in the video (see the previous section).

When you create a Publisher, you can specify whether the name is displayed in the Publisher video, by setting the style.nameDisplayMode property of the options you pass into the OT.initPublisher() method:

// Replace the first parameter with the target element ID:
const publisher = OT.initPublisher("myPublisher",
  {
    name: "John",
    style: { nameDisplayMode: "off" }
  });
session.publish(publisher);

The style.nameDisplayMode property can be set to one of three values:

Once you have created the Publisher, you can change the name display mode, by calling the setStyle() method of the Publisher object. (See the documentation for the Publisher.setStyle() method.)

When you subscribe to a stream, you can specify whether the name is displayed in the Subscriber video, by setting the style.nameDisplayMode property of the options you pass into the Session.subscribe() method:

// Replace the first two parameters with the stream and target element ID:
const subscriber = session.subscribe(stream,
  "mySubscriber",
  {
    style: { nameDisplayMode: "off" }
  });

Once you have created the Subscriber, you can change the name display mode, by calling the setStyle() method of the Subscriber object. (See the documentation for the Subscriber.setStyle() method.)

Displaying or hiding the mute audio button

By default, the user interface for a Publisher or a Subscriber includes a mute audio button. For a Publisher, the user can click to toggle the mic on and off. For a Subscriber, the user can click to toggle the speaker on and off.

When you publish a stream, you can specify whether the mute button is displayed by passing a style.buttonDisplayMode property into the OT.initPublisher() method:

const publisher = OT.initPublisher(
  'publisher-element-id', // Replace with the replacement element ID
  {
     name: 'John',
     style: {buttonDisplayMode: 'on'}
  }
);
session.publish(publisher);

The style.buttonDisplayMode property can be set to one of three values:

Similarly, when you subscribe to a stream, you can specify whether the mute speaker is displayed by passing a style.buttonDisplayMode property into the Session.subscribe() method:

const subscriber = session.subscribe(stream,
  'subscriber-element-id', // Replace with the replacement element ID
  {
     style: {buttonDisplayMode: 'on'}
  }
);

Once you have created the Publisher or Subscriber, you can change the mute button display mode, by calling the setStyle() method of the Publisher object or Subscriber object. (See the documentation for Publisher.setStyle() and Subscriber.setStyle().)

Adjusting video cropping and letterboxing

You can specify cropping or letterboxing of a Publisher or Subscriber's video by setting the fitMode property of the options you pass into OT.initPublisher() or Session.subscribe(). Pass in one of the following two strings:

For example, the following code initializes a publisher with the video element letterboxed:

const publisher = OT.initPublisher("publisher-element-id",
  {fitMode: "contain"});

The following code subscribes to a stream with the video element cropped:

const subscriber = session.subscribe(stream,
  "subscriber-element-id",
  {fitMode: "cover"});

Getting a snapshot image of a video

The following code captures and displays a static image of the Publisher video:

const imgData = publisher.getImgData();
const img = document.createElement("img");
img.setAttribute("src", "data:image/png;base64," + imgData);

// Replace with the parent DIV for the img
document.getElementById("containerId").appendChild(img);

The following code captures and displays a static image of a Subscriber video:

const imgData = subscriber.getImgData();
const img = document.createElement("img");
img.setAttribute("src", "data:image/png;base64," + imgData);

// Replace with the parent DIV for the img
document.getElementById("containerId").appendChild(img);

Setting an image to display in audio-only mode

You can use the backgroundImageURI style of a Subscriber to set the image to be displayed when there is no video. The value you set can be the URL of an image on the web. It can also be a data: URL, such as one that you obtain using the getImgData() method of the Subscriber object (see the previous section).

The following code sets the background image of the Subscriber. When the call to Session.subscribe() completes successfully, the background image is set. If there is a video stream, the background is set to a static image captured from the subscriber video; otherwise it is set to an image loaded from a web URL:

const subscriber = session.subscribe(event.stream, 'subscriberElement', function(error) {
  if (error) {
    console.log(error.message)'
    return;
  }
  if (subscriber.stream.hasVideo) {
    const imgData = subscriber.getImgData();
    subscriber.setStyle('backgroundImageURI', imgData);
  } else {
    subscriber.setStyle('backgroundImageURI',
      'https://tokbox.com/img/styleguide/tb-colors-cream.png'
    );
  }
});

If you do not set a background image for a Subscriber, when there is no video, it will display the initials for the stream, if initials were set when the stream's publisher initialized the stream (see Initializing a Publisher).

Adjusting user interface based on audio levels

Publisher and Subscriber objects dispatch audioLevelUpdated events periodically to report the audio level. You can use these events to display an audio level indicator. You can also use these events to detect active speakers in a session.

The following example adjusts the value of a meter element that shows volume of a subscriber. The code sets the audioLevelDisplayMode style to 'off', which disables the default audio level meter displayed in the Subscriber. Note that the audio level is adjusted logarithmically and a moving average is applied:

subscriber.setStyle('audioLevelDisplayMode', 'off');
const movingAvg = null;
subscriber.on('audioLevelUpdated', function(event) {
  if (movingAvg === null || movingAvg <= event.audioLevel) {
    movingAvg = event.audioLevel;
  } else {
    movingAvg = 0.7 * movingAvg + 0.3 * event.audioLevel;
  }

  // 1.5 scaling to map the -30 - 0 dBm range to [0,1]
  const logLevel = (Math.log(movingAvg) / Math.LN10) / 1.5 + 1;
  logLevel = Math.min(Math.max(logLevel, 0), 1);
  document.getElementById('subscriberMeter').value = logLevel;
});

The example assumes that there is an HTML meter element with the ID "subscriberMeter".

Note that in audio-only mode, a Publisher or Subscriber DOM element displays a volume indicator by default (in the upper-righthand corner of the element). You can disable this default user interface element and display your own volume meter. See the next topic, Adjusting user interface when video is enabled or disabled.

You can also use the audioLevelUpdated event to determine when a publisher or subscriber's audio is loud enough for long enough to label the participant as having started talking. Or, if the audio has been quiet for long enough, you can identify the participant as having stopped talking:

const subscriber = session.subscribe(event.stream);

SpeakerDetection(subscriber, function() {
  console.log('started talking');
}, function() {
  console.log('stopped talking');
});

const SpeakerDetection = function(subscriber, startTalking, stopTalking) {
  const activity = null;
  subscriber.on('audioLevelUpdated', function(event) {
    const now = Date.now();
    if (event.audioLevel > 0.2) {
      if (!activity) {
        activity = {timestamp: now, talking: false};
      } else if (activity.talking) {
        activity.timestamp = now;
      } else if (now- activity.timestamp > 1000) {
        // detected audio activity for more than 1s
        // for the first time.
        activity.talking = true;
        if (typeof(startTalking) === 'function') {
          startTalking();
        }
      }
    } else if (activity && now - activity.timestamp > 3000) {
      // detected low audio activity for more than 3s
      if (activity.talking) {
        if (typeof(stopTalking) === 'function') {
          stopTalking();
        }
      }
      activity = null;
    }
  });
};

(Instead of logging to the console, your app could adjust a user interface element when the user starts and stops talking.)

Displaying a custom UI element when Subscriber audio is blocked

Some browsers automatically block audio playback, requiring a click event before audio playback starts for subscribers. These browsers include Safari, Firefox 66+, and Chrome 71+.

The Subscriber object displays an audio playback button if audio playback is blocked. You can disable the Subscriber's default audio playback button and display your own UI element that the user will click to start audio playback.

To disable the display of the default audio playback button, set the style.audioBlockedDisplayMode property of the options parameter of the Session.subscribe() method):

const subscriberOptions = {
    style: { audioBlockedDisplayMode: "off" }
  };
const subscriber = session.subscribe(stream,
  'subscriber-element-id', // Replace with the replacement element ID
  subscriberOptions
);

Add event listeners for the audioBlocked and audioUnblocked events dispatched by the Subscriber to display and hide your custom UI element (telling the use to click to play back audio):

subscriber.on({
  audioBlocked: function(event) {
    // display custom UI
  },
  audioUnblocked: function(event) {
    // hide custom UI
  }
});

When the user clicks your custom UI element, call the OT.unblockAudio() method:

customElement.addEventListener('click', async () => {
  try {
    await OT.unblockAudio();
  } catch (err) {
    console.error('Unblocking audio failed.', err);
    return;
  }
  console.log('Unblocked audio successfully.');
});

Adjusting user interface when video is enabled or disabled

Adjusting the UI based on Subscriber events

A Subscriber object dispatches the following events related to the video being enabled or disabled for the subscriber's stream:

The videoDisableWarning and videoDisableWarningLifted events are only available in sessions that use the OpenTok Media Router (sessions with the media mode set to routed) unless using the publisher audio fallback feature, where the events will be available in routed or relayed sessions.

By default, the Subscriber displays a video disabled warning indicator and a video disabled indicator when the videoDisableWarning and videoDisableWarningLifted events are dispatched. You can disable the default display of the indicator by setting the videoDisabledDisplayMode style setting of the Subscriber object.

The following example uses the videoDisabledDisplayMode style setting to have the video disabled warning indicator and a video disabled indicator blink every one second when the videoDisableWarning and videoDisableWarningLifted events are dispatched:

const indicatorBlinker = new IndicatorBlinker(subscriber);

const IndicatorBlinker = function(subscriber) {
  const timer;
  const indicatorOn = false;
  subscriber.on({
    videoDisabled: function(event) {
      start();
    },
    videoDisableWarning: function(event) {
      start();
    },
    videoDisableWarningLifted: function(event) {
      stop();
    },
    videoEnabled: function(event) {
      stop();
    }
  });
  const start = function() {
    subscriber.setStyle('videoDisabledDisplayMode', 'on');
    if (timer) {
      clearInterval(timer);
    }
    timer = setInterval(function() {
      if (indicatorOn) {
        subscriber.setStyle('videoDisabledDisplayMode', 'off');
      } else {
        subscriber.setStyle('videoDisabledDisplayMode', 'on');
      }
      indicatorOn = !indicatorOn;
    }, 1000);
    indicatorOn = true;
  };
  const stop = function() {
    if (timer) {
      clearInterval(timer);
    }
  };
};

You can also set the videoDisabledDisplayMode style to 'off' and add your own user interface elements based on the videoDisableWarning, videoDisabled, videoDisableWarningLifted, and videoEnabled events.

Adjusting the UI based on Publisher events

When publisher audio fallback is enabled, the Publisher object dispatches these events in response to changing quality conditions:

By default, the Publisher displays icons when the videoDisableWarning and videoDisabled events occur.

The style property of the options parameter for OT.initPublisher() now includes a videoDisabledDisplayMode property. You can set the videoDisabledDisplayMode property to one of the following string values control how the default user interface elements are displayed:

For example the following code disables the default video disabled user interface elements, and handles the related events (so that you can provide your own user interface notifications):

// Enabled
const publisher = OT.initPublisher('target', {
  audioFallback: {
    publisher: true,
  },
  style: {
    videoDisabledDisplayMode: 'off',
  }
});

publisher.on({
  videoDisableWarning: () => {
    // Custom action — for example, add custom UI notification
  },
  videoDisableWarningLifted: () => {
    // Custom action — for example, remove custom UI notification
  },
  videoDisabled: () => {
    // Custom action — for example, add custom UI notification
  },
  videoEnabled: () => {
    // Custom action — for example, remove custom UI notification
  },
});

You can also set the videoDisabledDisplayMode style dynamically by calling the Publisher.setStyle() method:

publisher.setStyle('videoDisabledDisplayMode', 'off');

// Alternately:

publisher.setStyle({
  videoDisabledDisplayMode: 'off',
  // other styles ...
});

Hiding all built-in user interface controls for videos

The Publisher and Subscriber objects include the following built-in user interface controls:

You can disable all of these by setting the showControls property to false in the properties parameter you pass into the OT.initPublisher() method or the Session.subscribe() method.

For example, the following code creates a Publisher object that includes no built-in user interface controls:

const publisherOptions = {
     showControls: false
  };
const publisher = OT.initPublisher(
  'publisher-element-id', // Replace with the replacement element ID
  publisherOptions
);

The following code creates a Subscriber object that includes no built-in user interface controls:

const subscriberOptions = {
     showControls: false
  };
const subscriber = session.subscribe(stream,
  'subscriber-element-id', // Replace with the replacement element ID
  subscriberOptions
);

You can control the display of individual user interface controls by leaving the showControls property set to true (the default); then see these topics:

Directly accessing the Video element for a Publisher or Subscriber

You can disable the default user interface elements for a Publisher or Subscriber and access the HTML Video element directly. When publishing or subscribing to a stream, set the insertDefaultUI property to false when calling the OT.initPublisher() or Session.subscribe() method. If you set this option to false, OpenTok.js does not insert a default UI element in the HTML DOM, and the element property of the Publisher or Subscriber object is undefined. Instead, the Publisher element dispatches a videoElementCreated event when the Video element is created. The element property of the event object is a reference to the Video element. Add it to the HTML DOM to display the video.

The following code initializes a publisher and inserts its Video element into the HTML DOM:

const publisher = OT.initPublisher({insertDefaultUI: false});
publisher.on('videoElementCreated', function(event) {
  document.getElementById('publisher-video-parent-id').appendChild(event.element);
});

The following code subscribes to a stream and inserts its Video element into the HTML DOM:

const subscriber = session.subscribe(stream, {insertDefaultUI: false});
subscriber.on('videoElementCreated', function(event) {
  document.getElementById('subscriber-video-parent-id').appendChild(event.element);
});

If you set the insertDefaultUI property to false, do not set the targetElement parameter when calling OT.initPublisher() or Session.subscribe(). (This results in an error.)

The default user interface element contains user interface controls, a video loading indicator, and automatic video cropping or letter-boxing, in addition to the video. If you leave insertDefaultUI set to true (the default), you can control individual user interface settings using the fitMode, showControls, and style options. See the other topics in this page.

You can also access a Subscriber's MediaStream object (and use it in your own Video element). See the next section.

Accessing MediaStream objects for Subscribers

You can access the MediaStream object used by a Subscriber. The HTMLVideoElement object (in the videoElementCreated event dispatched by the Subscriber, described in the previous section), has a srcObject property. This is the MediaStream object for the Subscriber's audio-video stream. You can use that MediaStream object as the source MediaStream for another Video element (as its srcObject property):

session.on('streamCreated', function(event) {
  const subscriber = session.subscribe(event.stream, { insertDefaultUI: false });
  subscriber.on('videoElementCreated', event => {
    // myVideoElement is a Video element you have created:
    myVideoElement.srcObject = event.element.srcObject;
  });
});

In a routed session that uses Adaptive Media Routing, the MediaStream for a subscriber can change when the session switches from relayed to routed streams (see this Help Center knowlege base article). Add an event listener for the play event for a Subscriber object's Video element to get the updated MediaStream instance:

session.on('streamCreated', function(event) {
  const subscriber = session.subscribe(event.stream, { insertDefaultUI: false });
  subscriber.on('videoElementCreated', event => {
    // myVideoElement is a Video element you have created:
    myVideoElement.srcObject = event.element.srcObject;
    myVideoElement.play()
    event.element.addEventListener('play', () => {
      // The MediaStram has changed
      myVideoElement.srcObject = event.element.srcObject;
      myVideoElement.play();
    });
  });
});