How to Capture High-Resolution Photos in the Browser with JavaScript getUserMedia

As the web platform develops, we can build rich kinds of applications on it. We can build a web app like a barcode scanner or a document scanner using the getUserMedia API to get camera frames for live analysis.

While a barcode scanner may not require high resolution, a document scanner often requires taking a high-resolution photo.

In this article, we are going to build a demo web app to illustrate ways to take a high-resolution photo in the browser. It mainly uses the following ways:

  1. Use WebRTC’s getUserMedia to start the camera preview in a video element and capture a frame with a canvas element. There is also an Image Capture API which allows taking a photo that has a higher resolution than the camera preview’s. But the API has limited browser support.
  2. Use an input element to call the HTML Media Capture API, i.e. <input type="file" name="image" accept="image/*" capture>, which calls the system’s camera app to take a photo.

What you’ll build: A browser-based web app that captures high-resolution photos (up to 4K) using JavaScript getUserMedia, the Image Capture API, and HTML Media Capture input.

Key Takeaways

  • The getUserMedia API lets you open a camera stream and capture frames up to 3840×2160 (4K) by setting width/height constraints.
  • The Image Capture API can take a photo at a higher resolution than the live camera preview, but browser support is limited to Chromium-based browsers.
  • For the simplest high-resolution capture without live preview, use <input type="file" accept="image/*" capture> to invoke the device’s native camera app.
  • High-resolution browser capture is essential for document scanning, barcode reading, and OCR workflows where image detail directly affects accuracy.

Common Developer Questions

How do I capture a high-resolution photo from a webcam in JavaScript?

Open the camera with navigator.mediaDevices.getUserMedia(), attach the stream to a video element, and request a specific width and height such as 3840x2160 through the video constraints before the stream starts. Once the preview is live, either draw the current frame onto a canvas with drawImage() or, when supported, call ImageCapture.takePhoto() to save a higher-resolution still image from the active video track.

What is the maximum camera resolution I can get with getUserMedia in the browser?

getUserMedia does not guarantee a fixed maximum because the final size depends on the physical camera and the browser’s constraint negotiation, but the demo shows 3840×2160 as a practical 4K target. After the stream starts, check video.videoWidth and video.videoHeight to see the actual resolution the browser granted, because unsupported requests silently fall back to the nearest supported mode.

How do I use the Image Capture API to take a 4K photo in Chrome?

In Chrome, get the first video track from localStream, create new ImageCapture(track), and call await imageCapture.takePhoto() inside the photo button handler. The returned Blob can be previewed with URL.createObjectURL(blob), and if ImageCapture is unavailable or throws, the sample falls back to captureFrame() so Firefox and Safari still produce an image through the canvas path.

Prerequisites

  • A modern browser (Chrome, Edge, Firefox, or Safari) with camera access.
  • A device with a camera that supports the desired resolution (e.g., 1080p or 4K).
  • HTTPS or localhost — getUserMedia requires a secure context.
  • If you want to extend this demo with document boundary detection or barcode scanning, get a 30-day free trial license for Dynamsoft Capture Vision.

Step 1: Create the HTML Boilerplate

Create a new HTML file with the following content:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Take a High Resolution Photo</title>
  <style>
  .home {
    display: flex;
    align-items: center;
    flex-direction: column;
  }
  </style>
</head>
<body>
  <div class="home">
    <h2>Take a Photo</h2>
  </div>
  <script>
  </script>
</body>
</html>

Step 2: Capture a Photo with getUserMedia

  1. Request camera permission when the page is loaded.

    window.onload = async function() {
      await requestCameraPermission();
    }
    async function requestCameraPermission() {
      const constraints = {video: true, audio: false};
      const stream = await navigator.mediaDevices.getUserMedia(constraints);
      const tracks = stream.getTracks();
      for (let i=0;i<tracks.length;i++) {
        const track = tracks[i];
        track.stop();  // stop the opened camera
      }
    }
    
  2. List camera devices in a select element.

    HTML:

    <label>
      Camera:
      <select id="cameraSelect"></select>
    </label>
    

    JavaScript:

    const cameraSelect = document.getElementById("cameraSelect");
    window.onload = async function() {
      await loadCameraDevices();
      loadCameraDevicesToSelect();
    }
    async function loadCameraDevices(){
      const constraints = {video: true, audio: false};
      const stream = await navigator.mediaDevices.getUserMedia(constraints);
      const devices = await navigator.mediaDevices.enumerateDevices();
      for (let i=0;i<devices.length;i++){
        let device = devices[i];
        if (device.kind == 'videoinput'){ // filter out audio devices
          cameraDevices.push(device);
        }
      }
      const tracks = stream.getTracks(); // stop the camera to avoid the NotReadableError
      for (let i=0;i<tracks.length;i++) {
        const track = tracks[i];
        track.stop();
      }
    }
    
    function loadCameraDevicesToSelect(){
      for (let i=0;i<cameraDevices.length;i++){
        let device = cameraDevices[i];
        cameraSelect.appendChild(new Option(device.label,device.deviceId))
      }
    }
    
  3. Start the selected camera and display the camera preview in a video element.

    document.getElementById("startCameraBtn").addEventListener('click', (event) => {
      console.log("start camera");
      let options = {};
      if (cameraSelect.selectedIndex != -1) {
        options.deviceId = cameraSelect.selectedOptions[0].value;
      }
      play(options);
    });
    
    
    function play(options) {
      stop(); // close before play
      video.style.display = "block";
      let constraints = {};
      if (options.deviceId){
        constraints = {
          video: {deviceId: options.deviceId},
          audio: false
        }
      }else{
        constraints = {
          video: {width:1280, height:720,facingMode: { exact: "environment" }},
          audio: false
        }
      }
      navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
        localStream = stream;
        // Attach local stream to video element      
        video.srcObject = stream;
      }).catch(function(err) {
        console.error('getUserMediaError', err, err.stack);
      });
    }
    
    function stop() {
      try{
        if (localStream){
          const tracks = localStream.getTracks();
          for (let i=0;i<tracks.length;i++) {
            const track = tracks[i];
            track.stop();
          }
        }
      } catch (e){
        alert(e.message);
      }
    };
    
  4. Use a canvas element to capture a frame.

    Append a canvas element in the HTML and then use it to capture a camera frame and display it in an img element.

    HTML:

    <button id="takePhotoBtn">Take Photo</button>
    <video class="camera" muted autoplay="autoplay" playsinline="playsinline" webkit-playsinline></video>
    <br/>
    <canvas id="hiddenCanvas"></canvas>
    <img id="photoTaken" />
    

    JavaScript:

    document.getElementById("takePhotoBtn").addEventListener('click', async (event) => {
      let src;
      src = captureFrame();
      document.getElementById("photoTaken").src = src;
    });
       
    function captureFrame(){
      let w = video.videoWidth;
      let h = video.videoHeight;
      canvas.width  = w;
      canvas.height = h;
      let ctx = canvas.getContext('2d');
      ctx.drawImage(video, 0, 0, w, h);
      return canvas.toDataURL("image/jpeg")
    }
    
  5. Specify a high resolution so that the frame captured has a high quality.

    We can define several common resolutions in a select and try to use the selected resolution.

    HTML:

    <label>
      Desired Resolution:
      <select id="resolutionSelect">
        <option value="640x480">640x480</option>
        <option value="1280x720">1280x720</option>
        <option value="1920x1080">1920x1080</option>
        <option value="3840x2160">3840x2160</option>
      </select>
    </label>
    

    We need to specify the resolution in the constraints for getUserMadia.

    function play(options){
      if (options.deviceId){
        constraints = {
          video: {deviceId: options.deviceId},
          audio: false
        }
      }else{
        constraints = {
          video: {width:1280, height:720,facingMode: { exact: "environment" }},
          audio: false
        }
      }
      if (resolutionSelect.selectedIndex != -1) {
        let width = parseInt(resolutionSelect.selectedOptions[0].value.split("x")[0]);
        let height = parseInt(resolutionSelect.selectedOptions[0].value.split("x")[1]);
        constraints["video"]["width"] = width;
        constraints["video"]["height"] = height;
      }
      //...
    }
    
  6. Take a photo using the Image Capture API

    If the browser supports the Image Capture API, we can use it to take a photo instead of capturing a frame with canvas.

    document.getElementById("takePhotoBtn").addEventListener('click', async (event) => {
      let src;
      if ("ImageCapture" in window) {
        try {
          const track = localStream.getVideoTracks()[0];
          let imageCapture = new ImageCapture(track);
          let blob = await imageCapture.takePhoto();
          src = URL.createObjectURL(blob);
        }catch(e) {
          src = captureFrame();
        }
      }else{
        src = captureFrame();
      }
      document.getElementById("photoTaken").src = src;
    });
    
  7. Display the size of the image taken.

    document.getElementById("photoTaken").onload = function(){
      let img = document.getElementById("photoTaken");
      document.getElementById("info").innerText = "Image Width: " + img.naturalWidth +"\nImage Height: " + img.naturalHeight;
    }
    

Step 3: Take a Photo with HTML Media Capture

  1. Add an input element for selecting an image file.

    <button id="loadFileBtn">
      Load File
    </button>
    <input type="file" id="file" onchange="loadImageFromFile();" accept=".jpg,.jpeg,.png,.bmp" />
    

    The input element is hidden and triggered with a button.

    document.getElementById("loadFileBtn").addEventListener('click', async (event) => {
      document.getElementById("file").click();
    });
    
  2. When an image file is selected, load it into an img element.

    function loadImageFromFile(){
      let fileInput = document.getElementById("file");
      let files = fileInput.files;
      if (files.length == 0) {
        return;
      }
      let file = files[0];
      fileReader = new FileReader();
      fileReader.onload = function(e){
        document.getElementById("photoTaken").src = e.target.result;
      };
      fileReader.onerror = function () {
        console.warn('oops, something went wrong.');
      };
      fileReader.readAsDataURL(file);
    }
    

Choose the Right Approach for Your Use Case

If you need to do live image processing before taking a photo, you have to use the getUserMedia approach.

If you only need to take a high-resolution photo, you can use the input element.

Common Issues and Edge Cases

  • Camera resolution falls back silently. If you request 3840×2160 but the camera only supports 1920×1080, the browser silently picks the closest supported resolution. Always check video.videoWidth and video.videoHeight after the stream starts to confirm the actual resolution.
  • getUserMedia requires a secure context. The API only works over HTTPS or on localhost. If you test on a plain HTTP URL, navigator.mediaDevices will be undefined.
  • Image Capture API availability. ImageCapture.takePhoto() is only supported in Chromium-based browsers (Chrome, Edge, Opera). Firefox and Safari fall back to the canvas capture method.

Source Code

Get the complete sample project source code on GitHub

There is also a document scanner demo that captures high-resolution photos: Get the complete sample project source code on GitHub. It uses Dynamsoft Document Normalizer to detect document boundaries and correct the document image.