How to Implement Camera Zoom Control in a JavaScript Web App

Using the getUserMedia API, we can open cameras on the web with JavaScript. Starting from Chrome 87, it is also possible to control the camera’s pan, tilt, and zoom, giving more possibilities for making a camera web app.

In this article, we are going to talk about how to control camera zoom on the web. We will create a demo directly using getUserMedia and another demo using Dynamsoft Camera Enhancer.

The zoom control feature is only available on Android’s Chrome or browsers with a Chrome kernel. Dynamsoft Camera Enhancer enables zoom on browsers that do not have this feature using a simulated method.

What you’ll build: A web page that opens a camera stream and lets users control zoom level via JavaScript — using both the native MediaStream API and the Dynamsoft Camera Enhancer SDK for cross-browser support.

Key Takeaways

  • The getUserMedia API combined with MediaStreamTrack.applyConstraints() enables native camera zoom control in Chromium-based browsers.
  • Camera zoom support must be checked at both the browser level (getSupportedConstraints) and the device level (getCapabilities) before use.
  • Dynamsoft Camera Enhancer provides a cross-browser zoom fallback using CSS and WebGL when native zoom is unavailable.
  • Native camera zoom is currently limited to Android Chrome and Chromium-based browsers; iOS Safari and Firefox do not support it.

Common Developer Questions

  • How do I control camera zoom with JavaScript in a web application?
  • Why does camera zoom not work on iOS Safari or Firefox, and what is the workaround?
  • How do I use Dynamsoft Camera Enhancer to add cross-browser camera zoom support?

Demo video:

This article is Part 1 in a 1-Part Series.

Prerequisites

  • A modern Chromium-based browser (Chrome 87+ on Android for native zoom support).
  • A device with a camera that supports zoom (check via getCapabilities()).
  • Get a 30-day free trial license if you want to use Dynamsoft Camera Enhancer.

Step 1: Open the Camera Stream with getUserMedia

  1. Create a new HTML file with the following template:

    <!DOCTYPE html>
    <html>
    <head>
      <title>Camera Zoom Demo</title>
      <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />
      <style>
        video {
          max-width: 100%;
        }
      </style>
    </head>
    <body>
      <h2>Camera Zoom Demo</h2>
      <label>
        Camera:
        <select id="select-camera"></select>
      </label>
      <label>
        Resolution:
        <select id="select-resolution">
          <option value="640x480">640x480</option>
          <option value="1280x720">1280x720</option>
          <option value="1920x1080" selected>1920x1080</option>
          <option value="3840x2160">3840x2160</option>
        </select>
      </label>
      <button onclick="startCamera();">Start Camera</button>
      <br/>
      <video id="video" muted autoplay="autoplay" playsinline="playsinline" webkit-playsinline></video>
      <script type="text/javascript">
      </script>
    </body>
    </html>
    
  2. Request camera permission.

    async function requestCameraPermission() {
      try {
        const constraints = {video: true, audio: false};
        const stream = await navigator.mediaDevices.getUserMedia(constraints);
        closeStream(stream);
      } catch (error) {
        console.log(error);
        throw error;
      }
    }
    
    function closeStream(stream){
      if (stream) {
        const tracks = stream.getTracks();
        for (let i=0;i<tracks.length;i++) {
          const track = tracks[i];
          track.stop();  // stop the opened tracks
        }
      }
    }
    
  3. List cameras.

    let cameras = []
    async function listCameras(){
      let cameraSelect = document.getElementById("select-camera");
      let allDevices = await navigator.mediaDevices.enumerateDevices();
      for (let i = 0; i < allDevices.length; i++){
        let device = allDevices[i];
        if (device.kind == 'videoinput'){
          cameras.push(device);
          cameraSelect.appendChild(new Option(device.label,device.deviceId));
        }
      }
    }
    
  4. Start the selected camera with the desired resolution.

    async function startCamera(){
      let selectedCamera = cameras[document.getElementById("select-camera").selectedIndex];
      closeStream(document.getElementById("video").srcObject);
      let selectedResolution = document.getElementById("select-resolution").selectedOptions[0].value;
      let width = parseInt(selectedResolution.split("x")[0]);
      let height = parseInt(selectedResolution.split("x")[1]);
         
      const videoConstraints = {
        video: {width:width, height:height, deviceId: selectedCamera.deviceId}, 
        audio: false
      };
       const cameraStream = await navigator.mediaDevices.getUserMedia(videoConstraints);
       document.getElementById("video").srcObject = cameraStream;
    }
    

Step 2: Apply Camera Zoom via the MediaStream API

  1. First, check whether the browser supports the zoom feature.

    function checkBrowserCapabilities(){
      if (navigator.mediaDevices.getSupportedConstraints().zoom) {
        console.log("Browser supports zoom");
      }else{
        alert("The browser does not support zoom.");
      }
    }
    
  2. Check whether the selected camera supports zoom. If it is supported, load the range of zoom.

    function checkCameraCapabilities(){
      const video = document.querySelector('video');
      const videoTracks = video.srcObject.getVideoTracks();
      track = videoTracks[0];
      let capabilities = track.getCapabilities();
      console.log(capabilities);
      if ('zoom' in capabilities) {
        let min = capabilities["zoom"]["min"];
        let max = capabilities["zoom"]["max"];
        document.getElementById("zoomInput").setAttribute("min",min);
        document.getElementById("zoomInput").setAttribute("max",max);
        document.getElementById("zoomInput").value  = 1;
      }else{
        alert("This camera does not support zoom");
      }
    }
    

    An input is added to set the zoom of the camera.

    <label>
      Zoom Factor:
      <input type="number" min="0" max="2" id="zoomInput" value="0" onchange="setZoom();">
    </label>
    
  3. Set the zoom if the factor is updated.

    async function setZoom(){
      let expectedZoom = document.getElementById("zoomInput").value;
      const constraints = {advanced: [{zoom: expectedZoom}]};
      await track.applyConstraints(constraints);
    }
    

Step 3: Simplify Camera Zoom with Dynamsoft Camera Enhancer

Dynamsoft Camera Enhancer is a library which makes it easier to control the camera and has features to enhance the camera. It is based on getUserMedia internally.

Here are the equivalent functions using Dynamsoft Camera Enhancer v4:

  1. List the cameras.

    let cameras = await cameraEnhancer.getAllCameras();
    
  2. Open the selected camera with the desired resolution.

    let selectedCamera = cameras[document.getElementById("select-camera").selectedIndex];
    let selectedResolution = document.getElementById("select-resolution").selectedOptions[0].value;
    let width = parseInt(selectedResolution.split("x")[0]);
    let height = parseInt(selectedResolution.split("x")[1]);
    await cameraEnhancer.selectCamera(selectedCamera);
    await cameraEnhancer.setResolution({width:width, height:height});
    await cameraEnhancer.open();
    
  3. Get zoom range.

    let zoomRange = cameraEnhancer.getAutoZoomRange();
    
  4. Set zoom.

    let expectedZoom = document.getElementById("zoomInput").value;
    cameraEnhancer.setZoom({factor: parseFloat(expectedZoom)});
    

An advantage of using Dynamsoft Camera Enhancer is that its zoom function supports all browsers. If the browser does not support zoom natively, it will simulate the zoom behavior using CSS and WebGL.

Common Issues and Edge Cases

  • Zoom not working on iOS Safari or Firefox: The native zoom constraint is only supported in Chromium-based browsers. Use Dynamsoft Camera Enhancer’s simulated zoom (CSS/WebGL) as a cross-browser fallback.
  • getCapabilities() returns no zoom property: The connected camera hardware does not support optical or digital zoom. Front-facing laptop cameras often lack zoom. Test with a USB or rear-facing mobile camera instead.
  • Zoom range resets after switching cameras: Each camera device reports its own min/max zoom capabilities. Always re-read getCapabilities() after calling getUserMedia with a different deviceId.

Source Code

Get the source code of the demos to have a try:

https://github.com/tony-xlh/web-camera-zoom