Taking webcam photos

  • Revision slug: WebRTC/Taking_webcam_photos
  • Revision title: Taking webcam photos
  • Revision id: 312721
  • Created:
  • Creator: jswisher
  • Is current revision? No
  • Comment

Revision Content

Intro and demo

This is a quick tutorial how to access the camera on your laptop and take a photo with it. You can see the final code in action in this JSFiddle. There is also a more advanced version uploading photos to imgur in JavaScript available as code on GitHub or as a demo.

The HTML markup

The first thing you need to access a webcam using WebRTC is a {{HTMLElement("video")}} element and a {{HTMLElement("canvas")}} element in the page. The video element receives the stream from WebRTC and the canvas element is needed to grab the image from the video. We also add a placeholder image to be replaced with the webcam shot later on.

<video id="video"></video>
<button id="startbutton">Take photo</button>
<canvas id="canvas"></canvas>
<img src="http://placekitten.com/g/320/261" id="photo" alt="photo">

The full script

Here is the full JavaScript in one chunk. We'll explain each section in detail below.

(function() {

  var streaming = false,
      video        = document.querySelector('#video'),
      cover        = document.querySelector('#cover'),
      canvas       = document.querySelector('#canvas'),
      photo        = document.querySelector('#photo'),
      startbutton  = document.querySelector('#startbutton'),
      width = 320,
      height = 0;

  navigator.getMedia = ( navigator.getUserMedia ||
                         navigator.webkitGetUserMedia ||
                         navigator.mozGetUserMedia ||
                         navigator.msGetUserMedia);

  navigator.getMedia(
    {
      video: true,
      audio: false
    },
    function(stream) {
      if (navigator.mozGetUserMedia) {
        video.mozSrcObject = stream;
      } else {
        var vendorURL = window.URL || window.webkitURL;
        video.src = vendorURL.createObjectURL(stream);
      }
      video.play();
    },
    function(err) {
      console.log("An error occured! " + err);
    }
  );

  video.addEventListener('canplay', function(ev){
    if (!streaming) {
      height = video.videoHeight / (video.videoWidth/width);
      video.setAttribute('width', width);
      video.setAttribute('height', height);
      canvas.setAttribute('width', width);
      canvas.setAttribute('height', height);
      streaming = true;
    }
  }, false);

  function takepicture() {
    canvas.width = width;
    canvas.height = height;
    canvas.getContext('2d').drawImage(video, 0, 0, width, height);
    var data = canvas.toDataURL('image/png');
    photo.setAttribute('src', data);
  }

  startbutton.addEventListener('click', function(ev){
      takepicture();
    ev.preventDefault();
  }, false);

})();

The bit by bit explanation

So what is going on here. Let's go through it bit by bit.

Anonymous function

(function() {

  var streaming = false,
      video        = document.querySelector('#video'),
      cover        = document.querySelector('#cover'),
      canvas       = document.querySelector('#canvas'),
      photo        = document.querySelector('#photo'),
      startbutton  = document.querySelector('#startbutton'),
      width = 320,
      height = 0;

We start by wrapping the whole script in an anonymous function to avoid global variables. We grab the elements from the HTML that we need and define the width of the video to be 320 and the height to be 0, as we need to calculate the appropriate height later on.

Right now there is a difference in the sizes of video provided by getUserMedia. Firefox Nightly uses 352x288 resolution and Opera and Chrome use 640x400 resolution. This will change in the future, but resizing with ratio as we will do below makes sure you don't get nasty surprises.

Get the video

Now we need to get the video from the webcam. This is currently prefixed for different browsers, so we need to test which one is supported:

  navigator.getMedia = ( navigator.getUserMedia ||
                         navigator.webkitGetUserMedia ||
                         navigator.mozGetUserMedia ||
                         navigator.msGetUserMedia);

We ask the browser to give us a video without audio and get a stream in the callback function:

  navigator.getMedia(
    {
      video: true,
      audio: false
    },
    function(stream) {
      if (navigator.mozGetUserMedia) {
        video.mozSrcObject = stream;
      } else {
        var vendorURL = window.URL || window.webkitURL;
        video.src = vendorURL.createObjectURL(stream);
      }
      video.play();
    },
    function(err) {
      console.log("An error occured! " + err);
    }
  );

Firefox Nightly right now needs you to set the mozSrcObject property of the video element in order to play it; for other browsers, set the src attribute. Whilst Firefox can use the stream directly, Webkit and Opera need to create an object URL from it. This all will become standardized in the near future.

Resize the video

Next we need to resize the video to the desired dimensions.

  video.addEventListener('canplay', function(ev){
    if (!streaming) {
      height = video.videoHeight / (video.videoWidth/width);
      video.setAttribute('width', width);
      video.setAttribute('height', height);
      canvas.setAttribute('width', width);
      canvas.setAttribute('height', height);
      streaming = true;
    }
  }, false);

We subscribe to the canplay event of the video and read its dimensions using videoHeight and videoWidth. These are not reliably available until the event is fired. We set streaming to true to do this check only once, as the canplay event keeps firing.

This is all that is needed to get the video to play.

Take a picture

Now we need to grab a picture of it using canvas. We assign an event handler to the start button to call the takepicture function.

  startbutton.addEventListener('click', function(ev){
      takepicture();
    ev.preventDefault();
  }, false);

In this function, we re-set the canvas size to the dimensions of the video, which also wipes it, and we get a frame of the video, which we copy onto the canvas. We then need to turn the canvas data into a data URL with a PNG header, and set the src of the photo to this url.

  function takepicture() {
    canvas.width = width;
    canvas.height = height;
    canvas.getContext('2d').drawImage(video, 0, 0, width, height);
    var data = canvas.toDataURL('image/png');
    photo.setAttribute('src', data);
  }

})();

That's all that is needed to show a webcam stream and take a still picture of it, across Chrome, Opera and Firefox.

Support

Currently using WebRTC for accessing the camera is supported in Chrome, Opera and Firefox Nightly 18. Enabling WebRTC in Firefox Nightly requires you to set a flag in the configuration:

  • Type "about:config" in the address bar and say yes that you want to make changes
  • Find the "media.navigator.enabled" entry and set it to true

Revision Source

<h2 id="Intro_and_demo">Intro and demo</h2>
<p>This is a quick tutorial how to access the camera on your laptop and take a photo with it. You can see the <a href="http://jsfiddle.net/codepo8/agaRe/4/" title="http://jsfiddle.net/codepo8/agaRe/4/">final code in action in this JSFiddle</a>. There is also a more advanced version uploading photos to <strong>imgur</strong> in JavaScript available as <a href="https://github.com/codepo8/interaction-cam/" title="https://github.com/codepo8/interaction-cam/">code on GitHub</a> or <a href="http://codepo8.github.com/interaction-cam/" title="http://codepo8.github.com/interaction-cam/">as a demo</a>.</p>
<h2 id="The_HTML_markup">The HTML markup</h2>
<p>The first thing you need to access a webcam using WebRTC is a {{HTMLElement("video")}} element and a {{HTMLElement("canvas")}} element in the page. The video element receives the stream from WebRTC and the canvas element is needed to grab the image from the video. We also add a placeholder image to be replaced with the webcam shot later on.</p>
<pre class="brush:html;">
&lt;video id="video"&gt;&lt;/video&gt;
&lt;button id="startbutton"&gt;Take photo&lt;/button&gt;
&lt;canvas id="canvas"&gt;&lt;/canvas&gt;
&lt;img src="http://placekitten.com/g/320/261" id="photo" alt="photo"&gt;
</pre>
<h2 id="The_full_script">The full script</h2>
<p>Here is the full JavaScript in one chunk. We'll explain each section in detail below.</p>
<pre class="brush:js;">
(function() {

  var streaming = false,
      video        = document.querySelector('#video'),
      cover        = document.querySelector('#cover'),
      canvas       = document.querySelector('#canvas'),
      photo        = document.querySelector('#photo'),
      startbutton  = document.querySelector('#startbutton'),
      width = 320,
      height = 0;

  navigator.getMedia = ( navigator.getUserMedia ||
                         navigator.webkitGetUserMedia ||
                         navigator.mozGetUserMedia ||
                         navigator.msGetUserMedia);

  navigator.getMedia(
    {
      video: true,
      audio: false
    },
    function(stream) {
      if (navigator.mozGetUserMedia) {
        video.mozSrcObject = stream;
      } else {
        var vendorURL = window.URL || window.webkitURL;
        video.src = vendorURL.createObjectURL(stream);
      }
      video.play();
    },
    function(err) {
      console.log("An error occured! " + err);
    }
  );

  video.addEventListener('canplay', function(ev){
    if (!streaming) {
      height = video.videoHeight / (video.videoWidth/width);
      video.setAttribute('width', width);
      video.setAttribute('height', height);
      canvas.setAttribute('width', width);
      canvas.setAttribute('height', height);
      streaming = true;
    }
  }, false);

  function takepicture() {
    canvas.width = width;
    canvas.height = height;
    canvas.getContext('2d').drawImage(video, 0, 0, width, height);
    var data = canvas.toDataURL('image/png');
    photo.setAttribute('src', data);
  }

  startbutton.addEventListener('click', function(ev){
      takepicture();
    ev.preventDefault();
  }, false);

})();</pre>
<h2 id="The_bit_by_bit_explanation">The bit by bit explanation</h2>
<p>So what is going on here. Let's go through it bit by bit.</p>
<h3 id="Anonymous_function">Anonymous function</h3>
<pre class="brush:js;">
(function() {

  var streaming = false,
      video        = document.querySelector('#video'),
      cover        = document.querySelector('#cover'),
      canvas       = document.querySelector('#canvas'),
      photo        = document.querySelector('#photo'),
      startbutton  = document.querySelector('#startbutton'),
      width = 320,
      height = 0;</pre>
<p>We start by wrapping the whole script in an anonymous function to avoid global variables. We grab the elements from the HTML that we need and define the width of the video to be 320 and the height to be 0, as we need to calculate the appropriate height later on.</p>
<div class="warning">
  <p>Right now there is a difference in the sizes of video provided by getUserMedia. Firefox Nightly uses 352x288 resolution and Opera and Chrome use 640x400 resolution. This will change in the future, but resizing with ratio as we will do below makes sure you don't get nasty surprises.</p>
</div>
<h3 id="Get_the_video">Get the video</h3>
<p>Now we need to get the video from the webcam. This is currently prefixed for different browsers, so we need to test which one is supported:</p>
<pre class="brush:js;">
  navigator.getMedia = ( navigator.getUserMedia ||
                         navigator.webkitGetUserMedia ||
                         navigator.mozGetUserMedia ||
                         navigator.msGetUserMedia);</pre>
<p>We ask the browser to give us a video without audio and get a stream in the callback function:</p>
<pre class="brush:js;">
  navigator.getMedia(
    {
      video: true,
      audio: false
    },
    function(stream) {
      if (navigator.mozGetUserMedia) {
        video.mozSrcObject = stream;
      } else {
        var vendorURL = window.URL || window.webkitURL;
        video.src = vendorURL.createObjectURL(stream);
      }
      video.play();
    },
    function(err) {
      console.log("An error occured! " + err);
    }
  );</pre>
<p>Firefox Nightly right now needs you to set the <code>mozSrcObject</code> property of the video element in order to play it; for other browsers, set the <code>src</code> attribute. Whilst Firefox can use the stream directly, Webkit and Opera need to create an object URL from it. This all will become standardized in the near future.</p>
<h3 id="Resize_the_video">Resize the video</h3>
<p>Next we need to resize the video to the desired dimensions.</p>
<pre class="brush:js;">
  video.addEventListener('canplay', function(ev){
    if (!streaming) {
      height = video.videoHeight / (video.videoWidth/width);
      video.setAttribute('width', width);
      video.setAttribute('height', height);
      canvas.setAttribute('width', width);
      canvas.setAttribute('height', height);
      streaming = true;
    }
  }, false);</pre>
<p>We subscribe to the <code>canplay</code> event of the video and read its dimensions using <code>videoHeight</code> and <code>videoWidth</code>. These are not reliably available until the event is fired. We set <code>streaming</code> to true to do this check only once, as the <code>canplay</code> event keeps firing.</p>
<p>This is all that is needed to get the video to play.</p>
<h3 id="Take_a_picture">Take a picture</h3>
<p>Now we need to grab a picture of it using canvas. We assign an event handler to the start button to call the <code>takepicture</code> function.</p>
<pre class="brush:js;">
  startbutton.addEventListener('click', function(ev){
      takepicture();
    ev.preventDefault();
  }, false);</pre>
<p>In this function, we re-set the canvas size to the dimensions of the video, which also wipes it, and we get a frame of the video, which we copy onto the canvas. We then need to turn the canvas data into a data URL with a PNG header, and set the src of the photo to this url.</p>
<pre class="brush:js;">
  function takepicture() {
    canvas.width = width;
    canvas.height = height;
    canvas.getContext('2d').drawImage(video, 0, 0, width, height);
    var data = canvas.toDataURL('image/png');
    photo.setAttribute('src', data);
  }

})();</pre>
<p>That's all that is needed to show a webcam stream and take a still picture of it, across Chrome, Opera and Firefox.</p>
<h2 id="Support">Support</h2>
<p>Currently using WebRTC for accessing the camera is supported in Chrome, Opera and Firefox Nightly 18. Enabling WebRTC in Firefox Nightly requires you to set a flag in the configuration:</p>
<ul>
  <li>Type "about:config" in the address bar and say yes that you want to make changes</li>
  <li>Find the "media.navigator.enabled" entry and set it to true</li>
</ul>
Revert to this revision