Taking webcam photos

  • Revision slug: WebRTC/Taking_webcam_photos
  • Revision title: Taking webcam photos
  • Revision id: 312699
  • Created:
  • Creator: espressive
  • 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.

Step 1: The HTML markup

The first thing you need to access a webcam using WebRTC is a video and a canvas element in the page. The video will receive the stream from WebRTC and canvas 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">

Step 2: 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);

})();

Step 3: The bit by bit explanation

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

(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 globals. 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 and Opera and Chrome 640x400 resolution. This will change in the future, but resizing with ratio as we will do later makes sure you don't get nasty surprises.

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 of the video element to play it, other browsers need to get the src attribute set. Whilst Firefox can use the stream directly Webkit and Opera need to create an object URL from it. This all will play out to a standard in the nearer future. Next we need to resize the video to the desired size.

  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 avaiable until the event was 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. 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 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 and take a still 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 to set a flag in the configuration:

  • Type "about:config" 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 imgur 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="Step_1.3A_The_HTML_markup">Step 1: The HTML markup</h2>
<p>The first thing you need to access a webcam using WebRTC is a <a href="/en-US/docs/DOM/HTMLVideoElement" title="/en-US/docs/DOM/HTMLVideoElement">video</a> and a <a href="/en-US/docs/HTML/Canvas" title="/en-US/docs/HTML/Canvas">canvas</a> element in the page. The video will receive the stream from WebRTC and canvas 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="Step_2.3A_The_full_script">Step 2: 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="Step_3.3A_The_bit_by_bit_explanation">Step 3: The bit by bit explanation</h2>
<p>So what is going on here. Let's go through it bit by bit:</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;</pre>
<p>We start by wrapping the whole script in an anonymous function to avoid globals. 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 and Opera and Chrome 640x400 resolution. This will change in the future, but resizing with ratio as we will do later makes sure you don't get nasty surprises.</p>
</div>
<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 mozSrcObject of the video element to play it, other browsers need to get the src attribute set. Whilst Firefox can use the stream directly Webkit and Opera need to create an object URL from it. This all will play out to a standard in the nearer future. Next we need to resize the video to the desired size.</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 canplay event of the video and read its dimensions using videoHeight and videoWidth. These are not reliably avaiable until the event was fired. We set streaming to true to do this check only once as the canplay event keeps firing.</p>
<p>This is all that is needed to get the video to play. 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.</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 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 and take a still 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 to set a flag in the configuration:</p>
<ul>
  <li>Type "about:config" 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