Search code examples
javascripthtmlscreenshot

How to take a screenshot of a webpage using html5, without rendering it on a canvas via JS


The important part is to take the screenshot

  • exactly as the user sees the browser window. This rules out canvas-based questions and answers, which emulate the browser but cannot exactly replicate how it renders html and css
  • without any non-standard plugins outside of plain, standards-compliant html5 and JS. This rules out Java applets, Flash, ActiveX, and kindly asking the user to press the "print-screen" button to then paste the image into a form.

I came across several questions on stackoverflow addressing this (see above), but did not find any answer that fulfilled both conditions. Instead of trying to add a partial answer for existing questions with slightly different requirements, I am asking this in a separate question instead.

I have so-far found a great explanation on how to acquire and stream pixels from a any window into a video element, using the Screen Capture API. However, I am missing the next step, where those pixels that are being displayed in a video element are converted into a jpg or png file that can be uploaded to a server.


Solution

  • This is simpler than it seemed. The missing part, saving a still shot of the video to png, can be achieved with code from this answer. The complete code would be as follows:

    const v = document.getElementById("v");
    const b = document.getElementById("b");
    const i = document.getElementById("i");
    
    navigator.mediaDevices.getDisplayMedia({
      audio: false
    }).then((r) => {
      console.log(r);
      v.srcObject = r;
    }).catch((e) => {
      console.log("this must be run in a secure context. Stack snippets (rightfully) refuse to run this.");
    });
    
    b.onclick = () => {
      // take screenshot
      // from https://stackoverflow.com/a/44325898/15472
      let scale = 1;
    
      const canvas = document.createElement("canvas");
      canvas.width = v.clientWidth * scale;
      canvas.height = v.clientHeight * scale;
      canvas.getContext('2d').drawImage(v, 0, 0,
        canvas.width, canvas.height);
    
      i.src = canvas.toDataURL();
      // you can send i.src here to a server
    
      // stop video
      let tracks = v.srcObject.getTracks();
      tracks.forEach(track => track.stop());
      v.srcObject = null;
    }
    #v,
    #i {
      width: 400;
      height: 300;
    }
    
    #v {
      border: 1px solid blue;
    }
    
    #i {
      border: 1px solid green;
    }
    <div>
      <video id="v" autoplay></video>
      <button id="b">take screenshot</button>
      <img id="i" src="//:0" />
    </div>

    Note that StackOverflow does not allow asking for screen-grabbing permissions, and therefore this code does not run here.