TinyTheBrontosaurus
TinyTheBrontosaurus

Reputation: 4810

Is it possible to control the camera light on a phone via a website?

Is it possible to control the camera's flashlight on a phone via a website? Say through Chrome or Firefox. I know it's possible using an Android or iOS app, which is implemented by all the flashlight apps out there. And I know one can control the cameras via the getUserMedia family of functions. If not, does anyone know when will it become available?

Upvotes: 45

Views: 50979

Answers (5)

Marlon Englemam
Marlon Englemam

Reputation: 386

I had to implement a similar feature at work and these answers helped me a lot, the thing is I needed to implement it in a React Js web app so I used some of the code provided in older answers to make a react hook to handle the usage of the device's torch (if it is available in the device). I made it into a simple react project in the code sandbox and in case anyone needs something similar in the future this might be useful

https://codesandbox.io/s/dazzling-blackburn-yj5dyy

Upvotes: 0

Bikram Kumar
Bikram Kumar

Reputation: 443

Here, it is a static class for handling flashlight. You can call flashlightHandler.accessFlashlight() when the window loads. And then use the flashlightHandler.setFlashlightStatus() method, passing it true or false, as you want the flashlight status.

class flashlightHandler {

    static track; //the video track which is used to turn on/off the flashlight

    static accessFlashlight() {
        //Test browser support
        if (!('mediaDevices' in window.navigator)) {
            alert("Media Devices not available. Use HTTPS!");
            return;
        };

        //Get the environment camera (usually the second one)
        window.navigator.mediaDevices.enumerateDevices().then((devices) => {

            const cameras = devices.filter((device) => device.kind === 'videoinput');
            if (cameras.length === 0) {
                alert("No camera found. If your device has camera available, check permissions.");
                return;
            };
            
            const camera = cameras[cameras.length - 1];
            
            window.navigator.mediaDevices.getUserMedia({
                video: {
                    deviceId: camera.deviceId
                }
            }).then((stream) => {
                this.track = stream.getVideoTracks()[0];
                
                if (!(this.track.getCapabilities().torch)) {
                    alert("No torch available.");
                }; 
            });
        });
    }

    static setFlashlightStatus(status) {
        this.track.applyConstraints({
            advanced: [{
                torch: status
            }]
        });
    }
}

Upvotes: 5

Nikolay Makhalin
Nikolay Makhalin

Reputation: 319

I fixed Daniel's answer and now button works properly on Android phones. IOS is still unsupported.

https://jsfiddle.net/nzw5tv1q/

//have a console on mobile
const consoleOutput = document.getElementById("console");
const log = function (msg) {
  consoleOutput.innerText = `${consoleOutput.innerText}\n${msg}`;
  console.log(msg);
}

//Test browser support
const SUPPORTS_MEDIA_DEVICES = 'mediaDevices' in navigator;

if (SUPPORTS_MEDIA_DEVICES) {
  //Get the environment camera (usually the second one)
  navigator.mediaDevices.enumerateDevices().then(devices => {

    const cameras = devices.filter((device) => device.kind === 'videoinput');

    if (cameras.length === 0) {
      log('No camera found on this device.');
    }
    // Create stream and get video track
    navigator.mediaDevices.getUserMedia({
      video: {
        facingMode: 'environment',
      }
    }).then(stream => {
      const track = stream.getVideoTracks()[0];

      //Create image capture object and get camera capabilities
      const imageCapture = new ImageCapture(track)
      imageCapture.getPhotoCapabilities().then(capabilities => {
        //let there be light!
        const btn = document.querySelector('.switch');
        const torchSupported = !!capabilities.torch || (
          'fillLightMode' in capabilities &&
          capabilities.fillLightMode.length != 0 &&
          capabilities.fillLightMode != 'none'
        );

        if (torchSupported) {
          let torch = false;
          btn.addEventListener('click', function (e) {
            try {
              track.applyConstraints({
                advanced: [{
                  torch: (torch = !torch)
                }]
              });
            } catch (err) {
              log(err);
            }
          });
        } else {
          log("No torch found");
        }
      }).catch(log);
    }).catch(log);
  }).catch(log);

  //The light will be on as long the track exists
}
<button class="switch">On / Off</button>
<h2>
Console output
</h2>
<div id="console">

</div>

Upvotes: 3

Daniel Budick
Daniel Budick

Reputation: 1850

Here is a little "torch-app" for a website:

Edit 1: I also made a jsfiddle

//Test browser support
const SUPPORTS_MEDIA_DEVICES = 'mediaDevices' in navigator;

if (SUPPORTS_MEDIA_DEVICES) {
  //Get the environment camera (usually the second one)
  navigator.mediaDevices.enumerateDevices().then(devices => {
  
    const cameras = devices.filter((device) => device.kind === 'videoinput');

    if (cameras.length === 0) {
      throw 'No camera found on this device.';
    }
    const camera = cameras[cameras.length - 1];

    // Create stream and get video track
    navigator.mediaDevices.getUserMedia({
      video: {
        deviceId: camera.deviceId,
        facingMode: ['user', 'environment'],
        height: {ideal: 1080},
        width: {ideal: 1920}
      }
    }).then(stream => {
      const track = stream.getVideoTracks()[0];

      //Create image capture object and get camera capabilities
      const imageCapture = new ImageCapture(track)
      const photoCapabilities = imageCapture.getPhotoCapabilities().then(() => {

        //todo: check if camera has a torch

        //let there be light!
        const btn = document.querySelector('.switch');
        btn.addEventListener('click', function(){
          track.applyConstraints({
            advanced: [{torch: true}]
          });
        });
      });
    });
  });
  
  //The light will be on as long the track exists
  
  
}
<button class="switch">On / Off</button>

The code is heavily inspired by this repository, this webseries and this blog-post

Edit 2: This does only works in Chrome (and maybe Opera). It does not work in Chrome on iOS, because Chrome cannot access the camera. I cannot test it on android for now. I created a new jsfiddle, with an output. If you have an android phone and it does not work for you, it will maybe tell why: https://jsfiddle.net/jpa1vwed/

Feel free to debug, comment and edit.

Upvotes: 54

ltlBeBoy
ltlBeBoy

Reputation: 1312

You can use the MediaStream Image Capture API by creating an ImageCapture from a VideoStreamTrack and setting the option "fillLightMode" to "flash" or "torch". Example:

<video autoplay="true"></video>
<img />
<button onclick="takePhoto()">Take Photo</button>
<script type="text/javascript">
    var imageCapture = null;
    var deviceConfig = {
        video: {
            width: 480,
            height: 640,
            facingMode: "environment", /* may not work, see https://bugs.chromium.org/p/chromium/issues/detail?id=290161 */
            deviceId: null
        }
    };

    var imageCaptureConfig = {
        fillLightMode: "torch", /* or "flash" */
        focusMode: "continuous"
    };

    // get the available video input devices and choose the one that represents the backside camera
    navigator.mediaDevices.enumerateDevices()
            /* replacement for not working "facingMode: 'environment'": use filter to get the backside camera with the flash light */
            .then(mediaDeviceInfos => mediaDeviceInfos.filter(mediaDeviceInfo => ((mediaDeviceInfo.kind === 'videoinput')/* && mediaDeviceInfo.label.includes("back")*/)))
            .then(mediaDeviceInfos => {
                console.log("mediaDeviceInfos[0].label: " + mediaDeviceInfos[0].label);

                // get the device ID of the backside camera and use it for media stream initialization
                deviceConfig.video.deviceId = mediaDeviceInfos[0].deviceId;
                navigator.mediaDevices.getUserMedia(deviceConfig)
                        .then(_gotMedia)
                        .catch(err => console.error('getUserMedia() failed: ', err));
            });

    function takePhoto () {
        imageCapture.takePhoto()
                .then(blob => {
                    console.log('Photo taken: ' + blob.type + ', ' + blob.size + 'B');

                    // get URL for blob data and use as source for the image element
                    const image = document.querySelector('img');
                    image.src = URL.createObjectURL(blob);
                })
                .catch(err => console.error('takePhoto() failed: ', err));
    }

    function _gotMedia (mediastream) {
        // use the media stream as source for the video element
        const video = document.querySelector('video');
        video.srcObject = mediastream;

        // create an ImageCapture from the first video track
        const track = mediastream.getVideoTracks()[0];
        imageCapture = new ImageCapture(track);

        // set the image capture options (e.g. flash light, autofocus, ...)
        imageCapture.setOptions(imageCaptureConfig)
                .catch(err => console.error('setOptions(' + JSON.stringify(imageCaptureConfig) + ') failed: ', err));
    }
</script>

Note:

  • As of this writing the API is still under development and may change in the future.
  • For enabling ImageCapture in Chrome the flag "chrome://flags/#enable-experimental-web-platform-features" has to be set to "true"
  • For enabling ImageCapture in Firefox the flag "dom.imagecapture.enabled" in "about:config" has to be set to "true". But "setOptions" is not supported as of this writing!

See also:

Upvotes: 6

Related Questions