How to switch from front camera to rear camera (facing back) with JavaScript (HTML5) in the Browser

How to switch from front camera to rear camera (facing back) with JavaScript (HTML5) in the Browser

When you work with WebRTC, you will need to do a lot of stuff that offers the most basic functionality to the users to interact with your app. One of those features is the possibility to select a different video or audio stream source manually. For example, viewing the demo on this website, on the Samsung Galaxy S10+, you will see the following options available (The Galaxy S10 has three cameras on the back: a main 12-megapixel with an aperture that shifts between f/1.5 and f/2.4 depending on the light. An ultra-wide 16-megapixel unit, and a telephoto 12-megapixel for zooming):

List Front and Back Camera Mobile Device JavaScript

Although the example looks that it may require complicated logic to make it work, it really isn't. You need to understand the basics of the interaction with the MediaStream API in the browser and you will be up and running in a few minutes. This article will explain how to easily list all the media devices on your device to use their stream.

Requirements

The browser where you will work needs to support the NavigatorUserMedia API. You can verify this with a simple conditional in your code:

if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
    console.log("enumerateDevices is not supported.");
}

If it's supported, you may continue.

1. Listing Media Devices Without User's Permission

Note: this approach only works in Chrome and Firefox. It's meant to work only to verify if the user has a microphone or a camera connected as it won't list the details about the device like the label, deviceId, and so on.

The first thing that you need to learn is how to list all the available video and audio input devices of the computer. In this example, we will use a very simple approach of listing all of them in 2 selects, one of them will contain the list of the audioinput devices (microphones) and the one will list all the videoinput devices (cameras). The markup will look like this:

<label>Select Video Source (Camera)</label>

<select id="video-source"></select>

<label>Select Audio Source (Microphone)</label>

<select id="audio-source"></select>

And the JavaScript that will be executed to list the devices will be the following one:

navigator.mediaDevices.enumerateDevices().then((devices) => {
    let videoSourcesSelect = document.getElementById("video-source");
    let audioSourcesSelect = document.getElementById("audio-source");

    // Iterate over all the list of devices (InputDeviceInfo and MediaDeviceInfo)
    devices.forEach((device) => {
        let option = new Option();
        option.value = device.deviceId;

        // According to the type of media device
        switch(device.kind){
            // Append device to list of Cameras
            case "videoinput":
                option.text = device.label || `Camera ${videoSourcesSelect.length + 1}`;
                videoSourcesSelect.appendChild(option);
                break;
            // Append device to list of Microphone
            case "audioinput":
                option.text = device.label || `Microphone ${videoSourcesSelect.length + 1}`;
                audioSourcesSelect.appendChild(option);
                break;
        }

        console.log(device);
    });
}).catch(function (e) {
    console.log(e.name + ": " + e.message);
});

Using the Promises API obtained from the navigator.mediaDevices.enumerateDevices, we will obtain an array that contains the InputDeviceInfo and MediaDeviceInfo instances of each of them. We will iterate over them and will append a new option element to the defined selects, where each of them will have a simple label if it's available as text or the number of the device and as value the deviceId property, which contains the ID of the device that you can use to switch from source later.

The problem with this approach is that you won't get real information about the devices and even if you have multiple devices connected, you will always obtain a single item for every category (audioinput, videoinput, audiooutput):

List Cameras and Microphone JavaScript

2. Listing Media Devices With User's Permission

If you want detailed information about all the available devices on the user's computer/mobile, you will need to ask for the user's permissions:

User's Permissions for Access the Microphone and Camera

This can be requested through the method MediaDevices.getUserMedia() that will prompt for user's permission to use a media input which produces a MediaStream with tracks containing the requested types of media. In this example, we are going to create a helper that allows us to execute everything with an understandable workflow. As markup for this example, we will have 2 selects and a video element that will play the stream:

<label>Select Video Source (Camera)</label>

<select id="video-source"></select>

<label>Select Audio Source (Microphone)</label>

<select id="audio-source"></select>

<label>Live Preview:</label>
<!-- Important to set autoplay to true, otherwise the video won't play anything at the beginning -->
<video autoplay="true" id="player" controls></video>

The helper that we will use to request permissions will be the following one:

let videoSourcesSelect = document.getElementById("video-source");
let audioSourcesSelect = document.getElementById("audio-source");
let videoPlayer = document.getElementById("player");

// Create Helper to ask for permission and list devices
let MediaStreamHelper = {
    // Property of the object to store the current stream
    _stream: null,
    // This method will return the promise to list the real devices
    getDevices: function() {
        return navigator.mediaDevices.enumerateDevices();
    },
    // Request user permissions to access the camera and video
    requestStream: function() {
        if (this._stream) {
            this._stream.getTracks().forEach(track => {
                track.stop();
            });
        }

        const audioSource = audioSourcesSelect.value;
        const videoSource = videoSourcesSelect.value;
        const constraints = {
            audio: {
                deviceId: audioSource ? {exact: audioSource} : undefined
            },
            video: {
                deviceId: videoSource ? {exact: videoSource} : undefined
            }
        };
    
        return navigator.mediaDevices.getUserMedia(constraints);
    }
};

With this helper, you should be able to request permission from the user with the method MediaStreamHelper.requestStream like this:

// Request streams (audio and video), ask for permission and display streams in the video element
MediaStreamHelper.requestStream().then(function(stream){
    // Store Current Stream
    MediaStreamHelper._stream = stream;

    // Select the Current Streams in the list of devices
    audioSourcesSelect.selectedIndex = [...audioSourcesSelect.options].findIndex(option => option.text === stream.getAudioTracks()[0].label);
    videoSourcesSelect.selectedIndex = [...videoSourcesSelect.options].findIndex(option => option.text === stream.getVideoTracks()[0].label);

    videoPlayer.srcObject = stream;

    // You can now list the devices using the Helper here 
    // MediaStreamHelper.getDevices().then(...);
}).catch(function(err){
    console.error(err);
}); 

In this example, we will display the Audio and Video stream on a Video element of the document (player). After obtaining the stream, you will have now access to the list of devices with the MediaStreamHelper.getDevices promise:

// Request streams (audio and video), ask for permission and display streams in the video element
MediaStreamHelper.requestStream().then(function(stream){
    console.log(stream);
    // Store Current Stream
    MediaStreamHelper._stream = stream;

    // Select the Current Streams in the list of devices
    audioSourcesSelect.selectedIndex = [...audioSourcesSelect.options].findIndex(option => option.text === stream.getAudioTracks()[0].label);
    videoSourcesSelect.selectedIndex = [...videoSourcesSelect.options].findIndex(option => option.text === stream.getVideoTracks()[0].label);

    // Play the current stream in the Video element
    videoPlayer.srcObject = stream;
    
    // You can now list the devices using the Helper
    MediaStreamHelper.getDevices().then((devices) => {
        // Iterate over all the list of devices (InputDeviceInfo and MediaDeviceInfo)
        devices.forEach((device) => {
            let option = new Option();
            option.value = device.deviceId;

            // According to the type of media device
            switch(device.kind){
                // Append device to list of Cameras
                case "videoinput":
                    option.text = device.label || `Camera ${videoSourcesSelect.length + 1}`;
                    videoSourcesSelect.appendChild(option);
                    break;
                // Append device to list of Microphone
                case "audioinput":
                    option.text = device.label || `Microphone ${videoSourcesSelect.length + 1}`;
                    audioSourcesSelect.appendChild(option);
                    break;
            }

            console.log(device);
        });
    }).catch(function (e) {
        console.log(e.name + ": " + e.message);
    });
}).catch(function(err){
    console.error(err);
}); 

This will list the devices to which we didn't have access previously:

List Microphones and Cameras in JavaScript

3. Handling Audio and Video source change

Till this point, the code allows the user to see the list of available devices, now it's necessary to handle the stream change when the user selects a new video or audio source in the selects. This can be done easily by attaching an event listener to selects. When the change is triggered, the helper should request the stream once again using the currently selected sources and that's it! Be sure to update the stream of the video element as well:

let videoSourcesSelect = document.getElementById("video-source");
let audioSourcesSelect = document.getElementById("audio-source");
let videoPlayer = document.getElementById("player");

videoSourcesSelect.onchange = function(){
    MediaStreamHelper.requestStream().then(function(stream){
        MediaStreamHelper._stream = stream;
        videoPlayer.srcObject = stream;
    });
};

audioSourcesSelect.onchange = function(){
    MediaStreamHelper.requestStream().then(function(stream){
        MediaStreamHelper._stream = stream;
        videoPlayer.srcObject = stream;
    });
};

Happy coding ❤️!

This could interest you

Become a more social person