How to Access a Webcam and Record Video with JavaScript
Learn how to access a webcam and record video using JavaScript. Learn about the JavaScript window object, MediaRecord, and navigator.
Table of Contents 📖
- Setting up the Application Structure
- Creating index.html Boiler Plate
- Linking main.css to index.html
- Linking script.js to index.html
- Creating a Container to Hold our Button and Video
- Adding our Video and Button to the Container
- HTML Video Element
- Styling our HTML Elements - Creating a CSS Reset
- Styling our HTML Elements - Creating a Flex Container
- What is a Flex Container?
- Aligning our Video and Button
- Styling our Video Element
- Styling our Button - Making it a Circle
- Styling our Button - Centering the Text
- Styling our Button - Fading away the box-shadow
- Styling our Button on Hover
- Declaring our Video and Button Elements
- Declaring our MediaRecorder
- Initializing our Webcam
- What is an Asynchronous Function?
- Prompting the User to Access their Webcam
- What is the JavaScript navigator Object?
- What is the await Keyword?
- Specifying the Type of Media we Want Access to
- Starting our Webcamera
- What is the srcObject Property?
- Attaching the Webcamera Stream to the Window
- What is the Window Object?
- Calling startWebcamera
- Handling getUserMedia Errors
- Adding Functionality to our Button
- Adding Functionality to our Button - Start or Stop Recording
- Recording Webcamera Output
- Replacing Recorded Video with Webcamera Output
- Creating our MediaRecorder
- What is Mime-type?
- What is a Codec?
- Starting our MediaRecorder
- Handling the dataavailable Event
- Playing our Recorded Video on our Video Element
- What is a Blob?
- Swapping our Webcamera Output with the Recorded Video
- Stopping the MediaRecorder
- Starting our Application
- Summary
Setting up the Application Structure
To begin, lets create a directory called webcam-video and then step inside it.
mkdir webcam-video
cd webcam-video
This application will consist of three files, index.html, main.css, and script.js. Index.html will contain the structure of our application, main.css will style our application, and script.js will provide the functionality.
Creating index.html Boiler Plate
To begin, lets set up our HTML for this application. Add the following code to a file called index.html at the root of the application.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WittCode's Media Downloader</title>
</head>
<body>
</body>
</html>
Linking main.css to index.html
To link main.css to our index.html file we use the HTML link tag. Place the following inside the head tags of index.html
<link rel="stylesheet" href="./main.css">
The link tag provides a link between an HTML document and an external source. The attribute href is the location of the linked element. The attribute rel specifies the relationship with the external document.
Linking script.js to index.html
To link our script.js file to our index.html file, we need to use the script tag. The script tag is used to embed JavaScript. The script tag can point to an external script file through the src attribute. Add the following just above the closing body tag in our index.html file.
<script src="./script.js"></ script>
We want to place the script tag just above our body close tag because we want all our HTML to be loaded before our JavaScript runs.
Creating a Container to Hold our Button and Video
This application consists of a button and a HTML video element. Both of these elements will be housed inside the following div.
<div id="main__main-container"></div>
We also give this div the id of main__main-container which we will use to style it inside of main.css.
Adding our Video and Button to the Container
Lets place our HTML video element and button inside this container. The ids will be used to style and add functionality to the elements.
<video id="main__video" playsinline autoplay loop>Can't load video :(</video>
<div id="main__video-button">Record</div>
HTML Video Element
The HTML video element is used to embed video content into a document. The video displayed can be a movie clip or other video streams. What it will be here is the stream from our webcam.
The autoplay attribute makes the video play as soon as it is ready. The playsinline attribute is used for mobile browsers, making the video play where it is instead of opening up to fullscreen, the default behavior of mobile browsers. The loop attribute is used to play the video in a loop. This will be useful for when we have recorded video.
The text inside our video element (Can't load video :() is displayed in browsers that don't support the HTML video element.
Styling our HTML Elements - Creating a CSS Reset
Before we begin styling our elements lets create a small CSS reset. A CSS reset is a set of styles that load prior to all styles to remove browser built-in styles. Add the following to the top of main.css.
* {
margin: 0;
padding: 0;
font-family: Arial, Helvetica, sans-serif;
}
The * symbol means all elements. Different browsers use different default margins and paddings so here we are setting all the elements in our application to have no margin and padding. We do this so we can add our own custom margins and paddings that will be equal across different browsers.
We also change the font-family for all elements to be Arial, but if it isn't present Helvetica, and ultimately sans-serif.
Styling our HTML Elements - Creating a Flex Container
The first HTML element we are going to style is our container, the one with the id main__main-container. We are going to make this container a flex-container by setting the display to flex.
#main__main-container {
display: flex;
}
What is a Flex Container?
Creating a flex container makes it easier to design the layout of a website. When we create a flex container, all items nested within it (here this is our video element and button) become flex items. We can then align these flex items how we want, change the spacing around them, etc.
Aligning our Video and Button
So lets align our video element and button.
#main__main-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
height: 100vh;
}
Flex-direction: column will align our video element and button in a column, align-items: center will center them, and justify-content: space-around will add space around our video and button.
We also set the height of our container to be 100vh which means the entire height of the screen. We do this because we want our application to span the entire page.
Styling our Video Element
We want our video element to be no wider than 800px. To do this, we will use the CSS property max-width.
#main__video {
max-width: 800px;
}
So now, our video element can be smaller than 800px, but it can't have a width larger than 800px.
Styling our Button - Making it a Circle
To begin styling our button, lets first give our div a background color of gainsboro and a height and width of 200px. We want our height and width to be equal because we want our div to be a circle. We can create a circle with CSS by making a square and then giving it a border radius of 50%. The CSS border-radius property rounds the corners of an element.
#main__video-button {
background-color: gainsboro;
height: 200px;
width: 200px;
border-radius: 50%;
}
Styling our Button - Centering the Text
We then center our text horizontally by using text-align: center and vertically by using line-height: 200px. The line-height property sets the height between lines of text. In CSS, if we want to center text vertically we make the line-height the same size as our height (200px). We also increase the font-size to 2em, which means increase the default font-size by 2 times. Our button styles should now look like this.
#main__video-button {
background-color: gainsboro;
height: 200px;
width: 200px;
border-radius: 50%;
text-align: center;
line-height: 200px;
font-size: 2em;
}
Styling our Button - Fading away the box-shadow
When we move our cursor away from our button, we want a shadow to disappear slowly from behind it. This shadow can be made by using the CSS box-shadow property. If we want the shadow to disappear we need to not set the box-shadow property. However, if we want the box-shadow to fade away slowly we need to add a CSS transition.
CSS transitions allow us to change property values smoothly. We want our box-shadow to transition smoothly so we give our button transition-property: box-shadow and then transition-duration of 0.3s. This will make our box-shadow fade away over a duration of 0.3 seconds.
#main__video-button {
background-color: gainsboro;
height: 200px;
width: 200px;
border-radius: 50%;
text-align: center;
line-height: 200px;
font-size: 2em;
transition-property: box-shadow;
transition-duration: .3s;
}
Styling our Button on Hover
Now lets add that box-shadow to our button when we hover over it. To add styles to an element when we hover over it we use the :hover selector.
#main__video-button:hover {
cursor: pointer;
box-shadow: gray 0 0 10px 10px;
transition-property: box-shadow;
transition-duration: .3s;
}
Now when we hover over our button, the cursor becomes a pointer and a gray shadow appears behind it with a radius and spread of 10px. We make it so the shadow appears over a duration of 0.3 seconds.
Declaring our Video and Button Elements
Now we can start coding! To begin, lets link up our video and button HTML elements by their id. Add the following to the top of script.js.
const videoButton = document.getElementById('main__video-button');
const video = document.getElementById('main__video');
Declaring our MediaRecorder
We need a way to record the video produced from our webcamera. The JavaScript MediaRecorder allows us to record media. We won't set it up now, but we will declare it in an outer scope as we will need to access it in a few functions.
let mediaRecorder;
Initializing our Webcam
We will get our webcamera started inside an asynchronous function called init.
async function init() {
}
What is an Asynchronous Function?
An asynchronous function is declared with the async keyword. Async functions allow us to handle promises in a cleaner way. Async functions always return a promise.
Prompting the User to Access their Webcam
We now want to prompt the user for permission to use a media input that produces a media stream. In other words, we want to ask the user if our application can access their webcamera. To do this, we need to use the JavaScript navigator object.
What is the JavaScript navigator Object?
The navigator object contains information about the browser. A property of the navigator object is mediaDevices.
navigator.mediaDevices
The mediaDevices property is a read-only property that provides access to media input devices, one of these media devices is the webcamera.
To get access to media input devices, we use the method getUserMedia. The method getUserMedia prompts the user for permission to use a media input that produces a media stream.
navigator.mediaDevices.getUserMedia()
The method getUserMedia returns a promise that resolves into a media stream object. If this promise resolves, we want to capture it inside a variable called stream.
const stream = await navigator.mediaDevices.getUserMedia();
The media stream object we return here will be the user's webcamera.
What is the await Keyword?
As getUserMedia() returns a promise, we want to wait for that promsie to resolve. The await operator is a cleaner way to wait for a promise. The await keyword can only be used inside an async function.
Specifying the Type of Media we Want Access to
The getUserMedia method takes an object of the type mediaStreamConstraints that specifies the type of media we are requesting. Not only that, but it also specifies the requirements. For example, we can specify what resolution web camera we are looking for, whether we want access to the webcamera's audio, etc.
The mediaStreamConstraints object has two properties: video and audio that describe the requested media.
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
The above code says we are looking for an input device that has both audio and video available. A webcamera fulfills both of these constraints.
Starting our Webcamera
If the getUserMedia method finds a media input with the specified constraints (promise is resolved) then we can start our webcamera. To do this, we will create a method called startWebcamera.
function startWebcamera(stream) {
}
This function takes the stream returned from getUserMedia. Remember, this stream will be the media stream coming from our webcamera. We then want to set the srcObject on our HTML video element to be this stream.
video.srcObject = stream;
What is the srcObject Property?
The srcObject property sets the source of media on the given HTMLMediaElement. This object can be a MediaSource, Blob, File, or MediaStream. For this application, the source of media is a MediaStream and the HTMLMediaElement is our video element.
Attaching the Webcamera Stream to the Window
Inside our startCamera function we also want to tag the stream produced from our webcamera to our window object.
window.stream = stream;
Attaching the stream to the window object will allow us to access it throughout our program.
What is the Window Object?
The JavaScript window object represents the browser window. Global functions, variables, and objects become members of the window object. In fact, even the document object is part of the window object.
Calling startWebcamera
We will call our startWebcamera method right after our stream variable is called. We will also wrap this in a try block so we can catch any errors thrown.
async function init() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
startWebcam(stream);
}
}
Handling getUserMedia Errors
We need to handle errors that could be thrown from the method getUserMedia. Common reasons for the getUserMedia promise to be rejected are the user denying access to their media (NotAllowedError) or the desired media can't be found (NotFoundError). Add the following catch statement after the try block in our init function.
catch (err) {
console.log('Error retrieving media device.');
console.log(err);
}
Adding Functionality to our Button
When our video button is first clicked, we want to begin recording the video. So, lets first add an onclick to our video button and provide it a function. This function will be called each time the button is clicked.
videoButton.onclick = () => {
}
Adding Functionality to our Button - Start or Stop Recording
When video isn't being recorded, clicking the button will begin recording video. When video is being recorded, clicking the button will stop the video recording. We will handle this with a switch statement on the current text of the button.
switch (videoButton.textContent) {
case 'Record':
videoButton.textContent = 'Stop';
startRecording();
break;
case 'Stop':
videoButton.textContent = 'Record';
stopRecording();
break;
}
This code above says that if the text on the button is currently "Record", then change the text to "Stop" and then begin recording. On the other hand, if the text of the button is "Stop", change the text of the button to "Record" and then stop recording. We will make the startRecording and stopRecording functions next.
Recording Webcamera Output
Recording the webcamera output will be done inside a function called startRecording().
function startRecording() {
}
Replacing Recorded Video with Webcamera Output
Earlier, we learned that we can produce webcamera output through our video element by using the srcObject property.
if (video.srcObject === null) {
video.srcObject = window.stream;
}
If the srcObject property of the video element is null, this means that the recorded video is currently playing on the video element and not the webcamera output. Remember, the recorded video and the webcamera will both be played through the same video element.
This code replaces the recorded video with the webcamera output so we can make a new recorded video.
Creating our MediaRecorder
Earlier we declared our MediaRecorder variable, now we can instantiate it inside our startRecording function.
mediaRecorder = new MediaRecorder(window.stream, {mimeType: 'video/webm;codecs=vp9,opus'});
The MediaRecorder constructor takes a MediaStream to record from. Here, we are saying we want to record the media that is emitted from the webcamera stream. We do this by supplying the webcamera stream that we attached to our window object.
The second argument are specific options we want to set. One of these options is Mime-type.
What is Mime-type?
Mime-type is used to identify a type of data so that software knows how to handle it. We set the Mime-type to video/webm;codecs=vp9,opus. Video/webm is a file format for the HTML video tag. Vp9 is an open source video codec that provides compression for HD and 4K video/streaming. Opus is a codec for high quality audio.
What is a Codec?
A codec is a computer program, or device, that encodes or decodes a data stream. The word codec comes from a combination of the words coder and decoder.
Starting our MediaRecorder
Now our media recorder knows to record a stream coming from the webcam and also the format of the stream, so lets start recording. To record media, we use the method start.
mediaRecorder.start();
We have the option to pass the start method an object with the property timeslice. This timeslice value specifies when a dataavailable event is fired in milliseconds. We use this dataavailable event to work with the recorded data. If we don't specify a timeslice, the dataavailable event will be fired when the media recorder stops.
We will not specify a timeslice for this application as we are fine working with the recorded data in one chunk.
Handling the dataavailable Event
When our media recorder stops recording (the user has clicked the stop button), we will receive a dataavailable event.
mediaRecorder.ondataavailable
What we want to do is call a function whenever the ondataavailable event is fired. Remember, this is when our media recorder stops recording.
mediaRecorder.ondataavailable = recordVideo;
Now, when the media recorder stops, we will call the function recordVideo.
Playing our Recorded Video on our Video Element
When the media recorder stops playing, we want to play the recorded video on our HTML video element. We will perform this action inside the method recordVideo.
function recordVideo(event) {
}
As this function is called when the dataavailable event is triggered, it is passed an event object. This event is of the type BlobEvent.
What is a Blob?
A Blob is a file like object that contains raw data. Blobs are typically associated with media content and can be read as binary data, text, or converted into a readable stream. The class BlobEvent has a property called data that is the associated blob.
if (event.data && event.data.size > 0) {
}
Here, the blob will be our recorded video. We want to check if a video was recorded (the BlobEvent contains a Blob) and that the Blob attached to the event has data inside it.
Swapping our Webcamera Output with the Recorded Video
Remember, we want to play both the recorded video and the webcamera output on the same HTML video element. To do this, we need to first remove the webcamera output from our HTML video element.
video.srcObject = null;
Now, lets convert out blob object into a URL that can be played on our HTML video element using its src attribute.
let videoUrl = URL.createObjectURL(event.data);
URL.createObjectURL() is a static method that creates a DOMString out of the provided object. In other words, it turns our blob object into something that can be added to the src attribute on the HTML video tag.
video.src = videoUrl;
Stopping the MediaRecorder
We will be stopping our media recorder inside the function stopRecording.
function stopRecording() {
mediaRecorder.stop();
}
The MediaRecorder stop method will fire our dataavailable event on our media recorder. This will fire the recordVideo function we made previously, removing our webcamera output from the HTML video element and replacing it with the recorded video.
Starting our Application
Our application is now good to go! All we need to do is call our init function we made in the very beginning.
init();
Summary
But there we have it! We can now access our webcamera and record a video and play it! If this article was helpful, please consider donating at the top of the page, sharing this article, and subscribing to my YouTube channel WittCode!