JavaScript-30-Day-19
Tags: javascript, webdev, github, programming
Published: 2021-06-21 18:04:41

## <center> Unreal Webcam Fun </center>

## [demo](https://cenacrharsh.github.io/JS-30-DAY-19/)

![ss](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9up6dasslwmsn539fdb.png)

So what are we going to be building today?

Today we are going to make a photo booth with JavaScript.

First of all, we've got our video being piped in from our webcam, and then we've got our video being piped into a canvas element. Then, once it's in a canvas element, we can start to do all kinds of cool things with it. Like we can take our photo, and then you can go ahead and download them. They'll download to your
computer, and you can see them as real photos. Then, in the scripts, once we have it, we can start to add effects, like different filters where things should be red. Or what we can do is make this really cool RGB split, where you can see my face.

Before we get started today, there is one
thing that we do need to get up and running, and that is a server. So far, in this course we've just been working off of
the file. However, because of security restrictions with
getting a user's webcam, it must be tied to what's called a "secure origin". 

>A secure origin is a website that
is HTTPS, or in our case localhost is also
a secure origin.

>`index.html` needs to be fed through some sort of server.

Wes included a `package.JSON` file. If we open that up, you'll see one dependency, which is this called "browser-sync". It allows you to open up your website and start a little server, and it also gives you live reloading and a whole bunch of other stuff.

First we type `npm install` when that has finished what you can type is `npm start`.

This is the html we start with:

```html
<div class="photobooth">
      <div class="controls">
        <button onClick="takePhoto()">Take Photo</button>
      </div>

      <canvas class="photo"></canvas>
      <video class="player"></video>
      <div class="strip"></div>
    </div>
```

We quickly make a couple of selectors:

```javascript
const video = document.querySelector('.player');
const canvas = document.querySelector('.photo');
const ctx = canvas.getContext('2d');
const strip = document.querySelector('.strip');
const snap = document.querySelector('.snap');
```

The first thing we want to do is get the video
being piped into that video element.

```javascript
navigator.mediaDevices.getUserMedia({ video: true, audio: false })
```

Now that thing this going to return a promise so we call a `.then` on it.

```javascript
navigator.mediaDevices.getUserMedia({ video: true, audio: false })
    .then(localMediaStream => {
      console.log(localMediaStream);
```

Now what we need to do is to take our video, and we need to set the source to be that `localMediaStream`

Now, that's not going to work automatically, because `localMediaStream` is an object. In order for our video to work, it actually needs to be converted into some sort of URL.

```javascript
video.srcObject = localMediaStream;
      video.play();
    })
    .catch(err => {
      console.error(`OH NO!!!`, err);
    });
```

>`.createObjectURL`, That's going to convert that media stream into something that this video player can understand.

Now what we will see is one or two frames not a continuous video stream.

Why is that?

That's because we set the video to be this media stream. But it's not going to update unless we actually go ahead and play it.

Therefore, underneath that we'll call `video.play`, which is going to play it.

>We need to do a catch here, just in case someone doesn't allow you to access their webcam. We need to handle that error.

Here is the complete `getVideo` function:

```javascript
function getVideo() {
  navigator.mediaDevices
    .getUserMedia({ video: true, audio: false })
    .then((localMediaStream) => {
      console.log(localMediaStream);

      video.srcObject = localMediaStream;
      video.play();
    })
    .catch((err) => {
      console.error(`OH NO!!!`, err);
    });
}

getVideo();
```

The next thing that we need to do is to take a frame from this video, and to paint it onto the actual canvas on the screen.

We'll first re-size our canvas according to height and width of actual video.

Now, we need to make sure that the canvas is the
exact same size before we paint to it. That's really important because if the canvas is not the same size as the video
or if your video webcam has a different width and height to it, then we need to change that.

```javascript
const width = video.videoWidth;
  const height = video.videoHeight;
  canvas.width = width;
  canvas.height = height;
```

Now, what we want to do is to every 16 milliseconds (random choice), we are going to take an image from the webcam
and put it into the canvas.

```javascript
return setInterval(() => {
    ctx.drawImage(video, 0, 0, width, height);
```

Here is the complete `paintToCanvas` function:

```javascript
function paintToCanvas() {
  const width = video.videoWidth;
  const height = video.videoHeight;
  canvas.width = width;
  canvas.height = height;

  return setInterval(() => {
    ctx.drawImage(video, 0, 0, width, height);
}
```

The way that `dramImage` works is that you pass it an image, or a video element, and it will paint it right to it.

We're going to start at 00. Start at the top left-hand corner of the canvas, and then paint the width and the height. That's exactly why we resized our canvas.

We return that interval here, because if you ever need to stop this from painting, you can have access to that interval and
you can call `clearInterval` on it.

It's kind of a pain to have to manually run this `paintToCanvas`. So what we're going to do is, We're going to listen for an event
on the video element called `canplay`

**`canplay`** - That's an event that the video will emit.

```javascript
video.addEventListener("canplay", paintToCanvas);
```

Now, what we want to do is let's work on the `takePhoto` function.

First of all we add capture sound to it for effect.

``` javascript
snap.currentTime = 0;
  snap.play();
```

What we now need to do is take the data out of the canvas.
We can do this, `const data = canvas.toDataURL`.

Then, you pass it, "image/jpeg".

The image that we currently have is in a text-based representation so we need to convert in into a link.

```javascript
const link = document.createElement("a");

  link.href = data;
  link.setAttribute("download", "handsome");
```

We can now not only click photos but download them as well.

Now we want the photos to be visible on the screen as well:

```javascript
link.innerHTML = `<img src="${data}" alt="Handsome Man" />`;
strip.insertBefore(link, strip.firstChild);
```

Here is the complete take photo function:

```javascript
function takePhoto() {
  // played the sound
  snap.currentTime = 0;
  snap.play();

  // take the data out of the canvas
  const data = canvas.toDataURL("image/jpeg");
  
  const link = document.createElement("a");

  link.href = data;
  link.setAttribute("download", "handsome");

  link.innerHTML = `<img src="${data}" alt="Handsome Man" />`;

  strip.insertBefore(link, strip.firstChild);
}
```

The last thing that we want to do is do some **filters** on them.
So the way that a filter works is that you get the pixels out of the canvas, and then you mess with them, changing the
RGB values, and put them back in.

So let's go back up to our `paintToCanvas`:

Here are the changes we make:

```javascript
// take the pixels out
let pixels = ctx.getImageData(0, 0, width, height);

// mess with them

pixels = redEffect(pixels); //red filter

// pixels = greenScreen(pixels); //green screen effect

// pixels = rgbSplit(pixels); //rgb split effect

// ctx.globalAlpha = 0.8; //for ghosting effect

// put them back
ctx.putImageData(pixels, 0, 0);
  }, 16);
```

Here is the completed function:

```javascript
function paintToCanvas() {
  const width = video.videoWidth;
  const height = video.videoHeight;
  canvas.width = width;
  canvas.height = height;

  return setInterval(() => {
    ctx.drawImage(video, 0, 0, width, height);

// take the pixels out
let pixels = ctx.getImageData(0, 0, width, height);

// mess with them

pixels = redEffect(pixels); //red filter

// pixels = greenScreen(pixels); //green screen effect

// pixels = rgbSplit(pixels); //rgb split effect

// ctx.globalAlpha = 0.8; //for ghosting effect

// put them back
ctx.putImageData(pixels, 0, 0);
  }, 16);
}
```

and now you create the functions for effects:

```javascript
function redEffect(pixels) {
  for (let i = 0; i < pixels.data.length; i += 4) {
    pixels.data[i + 0] = pixels.data[i + 0] + 200; // RED
    pixels.data[i + 1] = pixels.data[i + 1] - 50; // GREEN
    pixels.data[i + 2] = pixels.data[i + 2] * 0.5; // Blue
  }
  return pixels;
}
```

```javascript
function rgbSplit(pixels) {
  for (let i = 0; i < pixels.data.length; i += 4) {
    pixels.data[i - 150] = pixels.data[i + 0]; // RED
    pixels.data[i + 500] = pixels.data[i + 1]; // GREEN
    pixels.data[i - 550] = pixels.data[i + 2]; // Blue
  }
  return pixels;
}
```

```javascript
function greenScreen(pixels) {
  const levels = {};

  document.querySelectorAll(".rgb input").forEach((input) => {
    levels[input.name] = input.value;
  });

  for (i = 0; i < pixels.data.length; i = i + 4) {
    red = pixels.data[i + 0];
    green = pixels.data[i + 1];
    blue = pixels.data[i + 2];
    alpha = pixels.data[i + 3];

    if (
      red >= levels.rmin &&
      green >= levels.gmin &&
      blue >= levels.bmin &&
      red <= levels.rmax &&
      green <= levels.gmax &&
      blue <= levels.bmax
    ) {
      // take it out!
      pixels.data[i + 3] = 0;
    }
  }

  return pixels;
}
```

**With this we are done with the project.**

GitHub repo:
{% github cenacrharsh/JS-30-DAY-19 no-readme %}

###Blog on Day-18 of javascript30
{% link https://dev.to/cenacr007_harsh/javascript-30-day-18-3h8f %}


###Blog on Day-17 of javascript30
{% link https://dev.to/cenacr007_harsh/javascript-30-day-17-2kg3 %}

###Blog on Day-16 of javascript30
{% link https://dev.to/cenacr007_harsh/javascript-30-day-16-a41 %}

##[Follow me on Twitter](https://twitter.com/cenacr007_harsh)
##[Follow me on Linkedin](https://www.linkedin.com/in/kumarharsh22/)

**DEV Profile**

{% user cenacr007_harsh %}

You can also do the challenge at [javascript30](https://javascript30.com/)

Thanks @wesbos , [WesBos](https://github.com/wesbos) to share this with us! 😊💖

**Please comment and let me know your views**
##Thank You!