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

32,757

Solution 1

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.

Solution 2

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:

Solution 3

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>

Solution 4

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
            }]
        });
    }
}
Share:
32,757
TinyTheBrontosaurus
Author by

TinyTheBrontosaurus

Professional software engineer, manager, and general engineer. Specialize in embedded C++, robotics, and autonomous vehicles. More recently have been learning web technologies.

Updated on April 06, 2022

Comments

  • TinyTheBrontosaurus
    TinyTheBrontosaurus about 2 years

    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?

    • Eric Gurney
      Eric Gurney over 7 years
      Did you ever figure out whether you can do this, and if so how?
  • martin
    martin about 6 years
    Does this work on iOS? I have a working app accessing the camera with getUserMedia in Safari 11 on iOS but the snipped is not working for me. Has anyone tested this?
  • Daniel Budick
    Daniel Budick about 5 years
    It can only work in Chrome, because ImageCapture is only supported in Chrome and Opera (I do not have Opera installed, so I cannot test it) for now. It does not work in chrome on Iphone anymore, I am currently looking into it.
  • Daniel Budick
    Daniel Budick about 5 years
    Chrome cannot access the camera on iphone (stackoverflow.com/questions/51501642/…), so this won't work either... I will test it in chrome on android, I need to find an android phone first.
  • Daniel Budick
    Daniel Budick about 5 years
    I tried it on my old Galaxy S5, it does not find a camera with torch. Currently I don't have more time to spend on that problem, I hope someone else will find a solution.
  • Stuart
    Stuart over 4 years
    Could someone update this to add toggle functionality to the torch? As is once the torch is turned on, there's no turning it off
  • devnullpointer
    devnullpointer almost 4 years
    @DanielBudick Chrome can and does access both cameras on iPhone, you need to use the correct html5 input tag <input type="file" accept="image/*" capture="environment"> "environment" for rear facing and "user" for front facing.
  • Daniel Chin
    Daniel Chin almost 3 years
    Four years passed, it is 2021, and this feature seems to be gone. Understandable. ImageCapture supports more operations than the underlying track, e.g. turning the torch off and on without asking for permission again. Maybe it's simply impossible to support all devices, so they had to prune it.
  • Bikram Kumar
    Bikram Kumar about 2 years
    FYI, your code has many redundant lines and this even doesn't provide a way to detect torch or turn it off. You can check my answer for better version.
  • Bikram Kumar
    Bikram Kumar about 2 years
    You should check my answer for a better version.