Take a Selfie with React – Access Web Camera JS

Are you looking for a solution to take pictures from your react application? You are in the right place. In this post, I will teach you how to take a selfie from desktop (web camera js), mobile or iPad with react by using JavaScript getUserMedia
API.
Steps to Create the Selfie App
JavaScript has a built-in API called MediaDevices
to access the input devices like web camera, microphone, or even screen–sharing.
- We will access the web camera JS data using
getUserMedia
method ofMediaDevices
object - Transfer the data from
getUserMedia
to the HTML5video
element. - Take a picture by grabbing the current video frame and draw it in the
canvas
element. - Save the picture using anchor element’s
download
attribute.
Create a component Selfie.js
Declare a state object for the component with a property imageURL
to store the URL of the captured image.
Declare 3 properties videoEle
, canvasEle
and imageEle
by assigning the Refs. Refs are used to accessing the DOM elements or React elements created inside the render method.
To assign a reference, you should use createRef
method of React
object
import React, { Component } from 'react';
class Selfie extends Component {
state = {
imageURL: ''
}
videoEle = React.createRef();
canvasEle = React.createRef();
imageEle = React.createRef();
render() {
return null;
}
}
export default Selfie;
Let’s write the JSX for our selfie component, we are going to add three elements video
, canvas
and image
elements.
render() {
return (<div className="selfie">
<video ref={this.videoEle}></video>
<canvas ref={this.canvasEle} style={{display: 'none'}}>
</canvas>
<img src={this.state.imageURL} ref={this.imageEle} />
</div>)
}
The ref
attribute is used to map the DOM element with our properties in the component. We have created a hidden canvas element, as it’s sole purpose is to capture the video frame using JavaScript.
Start the Web Camera JS or Mobile Camera
Create a method startCamera
to start the camera using navigator.mediaDevices.getUserMedia(constraints)
method. The constraints
parameter is an object used to define what hardware or the values we want to listen. For example, {video: true, audio: true}
will listen for both video and audio stream from the web camera js. In our case, we need to listen to the video stream only.
startCamera = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: true
});
this.videoEle.current.srcObject = stream;
} catch(err) {
console.log(err);
}
}
The getUserMedia
method returns a stream object. We can assign that stream value to the video element’s srcObject
to stream the data from the camera to the Video element.
Add a componentDidMount
lifecycle hook, inside the life cycle method invoke the startCamera
method.
componentDidMount = async () => {
this.startCamera();
}
Now, run the app npm run start
, you can see your face on the web application by accessing web camera JS.
Stop Web Camera / Mobile Camera
We need a method to stop the web camera, add the following method
stopCam = () => {
const stream = this.videoEle.current.srcObject;
const tracks = stream.getTracks();
tracks.forEach(track => {
track.stop();
});
}
We are accessing the tracks of the video and stop it one by one inside the forEach loop.
Take Selfie from Web Camera or Mobile Camera
Create a method takeSelfie
. In this method, we are going to use the drawImage
method in the canvas context to draw the current video frame on the canvas.
takeSelfie = async () => {
// Get the exact size of the video element.
const width = this.videoEle.current.videoWidth;
const height = this.videoEle.current.videoHeight;
// get the context object of hidden canvas
const ctx = this.canvasEle.current.getContext('2d');
// Set the canvas to the same dimensions as the video.
this.canvasEle.current.width = width;
this.canvasEle.current.height = height;
// Draw the current frame from the video on the canvas.
ctx.drawImage(this.videoEle.current, 0, 0, width, height);
// Get an image dataURL from the canvas.
const imageDataURL = this.canvasEle.current.toDataURL('image/png');
// Set the dataURL as source of an image element, showing the captured photo.
this.stopCam();
this.setState({
imageURL: imageDataURL
})
}
We are making a dataURL
using toDataURL
method in canvas. Later, we are updating the state property imageURL
with imageDataURL
, and before that stop the cam by invoking the this.stopCam()
method. Because, of the state change, the component will rerender to show the preview of the image we have taken.
Add Buttons to Take Selfie and Download Selfie
We need to rewrite our JSX to have buttons to take a selfie and download that selfie. We are also going to style our component to look better.
render() {
return (<div className="selfie">
<div className="cam">
<video width="100%" height="100%" className="video-player" autoPlay={true} ref={this.videoEle}></video>
<button className="btn capture-btn" onClick={this.takeSelfie}>
<i class="fa fa-camera" aria-hidden="true"></i>
</button>
</div>
<canvas ref={this.canvasEle} style={{display: 'none'}}></canvas>
<div className="preview">
<img className="preview-img" src={this.state.imageURL} ref={this.imageEle} />
<div className="btn-container">
<button className="btn back-btn" onClick={this.backToCam}>
<i class="fa fa-chevron-left" aria-hidden="true"></i>
</button>
<a href={this.state.imageURL} download="selfie.png"
className="btn download-btn">
<i class="fa fa-download" aria-hidden="true"></i>
</a>
</div>
</div>
</div>)
}
Now, we have separate container div for video element and image elements. Each of the div has some buttons. For the video element, there’s a button to capture the selfie. By clicking on that button, the this.takeSelfie()
method will get invoked to take the picture.
Inside the image container div, there are two buttons, one is to take you back to the cam by invoking this.backToCam
method, another one is the anchor element to download the captured image.
The anchor
element has a download
attribute with the value selfie.png, the href
attribute is updated with the imageURL
property of the state. So that we can download the captured image by clicking this anchor element.
Add Back Button Method
Inside the component, add the following method.
backToCam = () => {
this.setState({
imageURL: ''
}, () => {
this.startCamera();
})
}
We are assigning an empty string to the imageURL
and then start the web camera to take another picture.
Show/Hide Web Camera JS / Mobile Camera
To show/hide the web camera and the preview image, we can use the imageURL
property in the state. If it’s an empty string, then we need to display the video web camera, otherwise, we can show the preview image.
render() {
return (<div className="selfie">
{this.state.imageURL === '' && <div className="cam">
<video width="100%" height="100%" className="video-player" autoPlay={true} ref={this.videoEle}></video>
<button className="btn capture-btn" onClick={this.takeSelfie}>
<i class="fa fa-camera" aria-hidden="true"></i>
</button>
</div>
}
<canvas ref={this.canvasEle} style={{display: 'none'}}></canvas>
{this.state.imageURL !== '' && <div className="preview">
<img className="preview-img" src={this.state.imageURL} ref={this.imageEle} />
<div className="btn-container">
<button className="btn back-btn" onClick={this.backToCam}>
<i class="fa fa-chevron-left" aria-hidden="true"></i>
</button>
<a href={this.state.imageURL} download="selfie.png"
className="btn download-btn">
<i class="fa fa-download" aria-hidden="true"></i>
</a>
</div>
</div>
}
</div>)
}
Our final render method is having a condition to show/hide video player and preview image based on imageURL
property.
Style our component
Add the following font-awesome CDN in your index.css file
@import "https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css";
Create a file Selfie.css, add the following CSS into it.
.selfie {
display: flex;
justify-content: center;
}
.cam, .preview {
position: relative;
}
.preview-img {
display: block;
}
.capture-btn {
position: absolute;
bottom: 9px;
display: block;
left: 50%;
transform: translateX(-50%);
background: none;
color: #fff;
border: none;
font-size: 40px;
}
.video-player {
display: block;
}
.btn-container {
display: flex;
justify-content: space-between;
position: absolute;
bottom: 0;
left: 0;
}
.back-btn, .download-btn {
background: none;
color: #fff;
border: none;
font-size: 40px;
}
.download-btn {
margin-left: 25px;
}
import the Selfie.css
in your Selfie.js
import './Selfie.css';
Complete Code of Our Selfie Component
import React, { Component } from 'react';
import './Selfie.css';
class Selfie extends Component {
state = {
imageURL: '',
}
videoEle = React.createRef();
canvasEle = React.createRef();
imageEle = React.createRef();
componentDidMount = async () => {
this.startCamera();
}
startCamera = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: true
});
this.videoEle.current.srcObject = stream;
} catch(err) {
console.log(err);
}
}
takeSelfie = async () => {
// Get the exact size of the video element.
const width = this.videoEle.current.videoWidth;
const height = this.videoEle.current.videoHeight;
// get the context object of hidden canvas
const ctx = this.canvasEle.current.getContext('2d');
// Set the canvas to the same dimensions as the video.
this.canvasEle.current.width = width;
this.canvasEle.current.height = height;
// Draw the current frame from the video on the canvas.
ctx.drawImage(this.videoEle.current, 0, 0, width, height);
// Get an image dataURL from the canvas.
const imageDataURL = this.canvasEle.current.toDataURL('image/png');
this.stopCam();
this.setState({
imageURL: imageDataURL
})
}
stopCam = () => {
const stream = this.videoEle.current.srcObject;
const tracks = stream.getTracks();
tracks.forEach(track => {
track.stop();
});
}
backToCam = () => {
this.setState({
imageURL: ''
}, () => {
this.startCamera();
})
}
render() {
return (<div className="selfie">
{this.state.imageURL === '' && <div className="cam">
<video width="100%" height="100%" className="video-player" autoPlay={true} ref={this.videoEle}></video>
<button className="btn capture-btn" onClick={this.takeSelfie}>
<i class="fa fa-camera" aria-hidden="true"></i>
</button>
</div>
}
<canvas ref={this.canvasEle} style={{display: 'none'}}></canvas>
{this.state.imageURL !== '' && <div className="preview">
<img className="preview-img" src={this.state.imageURL} ref={this.imageEle} />
<div className="btn-container">
<button className="btn back-btn" onClick={this.backToCam}>
<i class="fa fa-chevron-left" aria-hidden="true"></i>
</button>
<a href={this.state.imageURL} download="selfie.png"
className="btn download-btn">
<i class="fa fa-download" aria-hidden="true"></i>
</a>
</div>
</div>
}
</div>)
}
}
export default Selfie;
Conclusion
We made it, you learned to create a selfie app with React. You have learned how to access the web camera and mobile camera using MediaDevices
API in JavaScript, how to take selfie using canvas
and save it to your local device using anchor tag. If you tried this method, share the repo in the comment. If you have other suggestions, please make a comment below.