Many browsers now have the ability to access video and audio input from the user. However, depending on the browser it might be a full dynamic and inline experience, or it could be delegated to another app on the user's device.
Start simple and progressively
The easiest thing to do is simply ask the user for a pre-recorded
file. Do this by creating a simple file input element and adding
an accept filter that indicates we can only accept image files and ideally we
will get them directly from the camera.
<input type="file" accept="image/*" capture>
This method works on all platforms. On desktop it will prompt the user to upload an image file from the file system. In Safari on iOS this method will open up the camera app, allowing you to capture an image and then send it back to the web page; on Android this method will give the user a choice of which app to use to capture the image before sending it back to the web page.
The data can then be attached to a <form> or manipulated with JavaScript by
listening for an onchange event on the input element and then reading
the files property of the event target.
Capture a single frame
Getting access to the image file is simple.
<input type="file" accept="image/*" capture="camera" id="camera">
<img id="frame">
<script>
var camera = document.getElementById('camera');
var frame = document.getElementById('frame');
camera.addEventListener('change', function(e) {
var file = e.target.files[0];
// Do something with the image file.
frame.src = URL.createObjectURL(file);
});
</script>
Once you have access to the file you can do anything you want with it. For example, you can:
- Attach it directly to a
<canvas>element so that you can manipulate it - Download it to the user's device
- Upload it to a server by attaching to an
XMLHttpRequest
Whilst using the input element method of getting access to images is ubiquitous, it is the least appealing option because it is not integrated directly into the web page, and on desktop can't access the user's webcam.
Access the camera interactively
Modern browsers can get direct acccess to cameras, allowing us to build experiences that are fully integrated with the web page, so the user need never leave the browser.
Acquire access to the camera
We can directly access a camera and microphone by using an API in the WebRTC
specification called getUserMedia(). This will prompt the user for
access to their connected microphones and cameras.
If successful the API will return a MediaStream that contains data from
the camera, and we can then either attach it to a <video> element and play it
to show a real time preview, or attach it to a <canvas> to get a
snapshot.
To get data from the camera we just set video: true in the constraints
object that is passed to the getUserMedia() API.
<video id="player" controls autoplay></video>
<script>
var player = document.getElementById('player');
var handleSuccess = function(stream) {
player.srcObject = stream;
};
navigator.mediaDevices.getUserMedia({video: true})
.then(handleSuccess);
</script>
By itself, this isn't that useful. All we can do is take the video data and play it back.
Take a snapshot from the camera
To access the raw data from the camera we have to take the stream created by
getUserMedia() and process the data. Unlike Web Audio, there isn't a
dedicated stream processing API for video on the web so we have to resort to
a tiny bit of hackery to capture a snapshot from the user's camera.
The process is as follows:
- Create a canvas object that will hold the frame from the camera
- Get access to the camera stream
- Attach it to a video element
- When you want to capture a precise frame, add the data from the video element
to a canvas object using
drawImage().
Done.
<video id="player" controls autoplay></video>
<button id="capture">Capture</button>
<canvas id="snapshot" width=320 height=240></canvas>
<script>
var player = document.getElementById('player');
var snapshotCanvas = document.getElementById('snapshot');
var captureButton = document.getElementById('capture');
var handleSuccess = function(stream) {
// Attach the video stream to the video element and autoplay.
player.srcObject = stream;
};
captureButton.addEventListener('click', function() {
var context = snapshot.getContext('2d');
// Draw the video frame to the canvas.
context.drawImage(player, 0, 0, snapshotCanvas.width,
snapshotCanvas.height);
});
navigator.mediaDevices.getUserMedia({video: true})
.then(handleSuccess);
</script>
Once you have data from the camera stored in the canvas you can do many things with it. You could:
- Upload it straight to the server
- Store it locally
- Apply funky effects to the image
Stop streaming from the camera when not needed
It is good practice to stop using the camera when you no longer need it. Not only will this save battery and processing power, it will also give users confidence in your application.
To stop access to the camera you can simply call stop() on each video track
for the stream returned by getUserMedia().
<video id="player" controls autoplay></video>
<button id="capture">Capture</button>
<canvas id="snapshot" width=320 height=240></canvas>
<script>
var player = document.getElementById('player');
var snapshotCanvas = document.getElementById('snapshot');
var captureButton = document.getElementById('capture');
var videoTracks;
var handleSuccess = function(stream) {
// Attach the video stream to the video element and autoplay.
player.srcObject = stream;
videoTracks = stream.getVideoTracks();
};
captureButton.addEventListener('click', function() {
var context = snapshot.getContext('2d');
context.drawImage(player, 0, 0, snapshotCanvas.width, snapshotCanvas.height);
// Stop all video streams.
videoTracks.forEach(function(track) {track.stop()});
});
navigator.mediaDevices.getUserMedia({video: true})
.then(handleSuccess);
</script>
Ask permission to use camera responsibly
If the user has not previously granted your site access to the camera then
the instant that you call getUserMedia the browser will prompt the user to
grant your site permission to the camera.
Users hate getting prompted for access to powerful devices on their machine and they will frequently block the request, or they will ignore it if they don't understand the context for which the prompt has been created. It is best practice to only ask to access the camera when first needed. Once the user has granted access they won't be asked again. However, if the user rejects access, you can't get access again, unless they manually change camera permission settings.
Compatibility
More information about mobile and desktop browser implementation: srcObject navigator.mediaDevices.getUserMedia()
We also recommend using the adapter.js shim to protect apps from WebRTC spec changes and prefix differences.