Vanilla JavaScript Projects

Accessing the Camera

Anjana Vakil

Anjana Vakil

Software Engineer & Educator
Vanilla JavaScript Projects

Check out a free preview of the full Vanilla JavaScript Projects course

The "Accessing the Camera" Lesson is part of the full, Vanilla JavaScript Projects course featured in this preview video. Here's what you'd learn in this lesson:

Anjana begins building the selfie cam functionality. The existing application is refactored to use the getUserMedia API and add the video to a canvas element. The markup rendered by the main.js file is moved into the index.html file.


Transcript from the "Accessing the Camera" Lesson

>> So, we're going to now get some video involved, and here begins the fun and the road to selfie magic. So we also have a method on the context object that allows us to draw an image and we can then pass in a few different things. So let's go see what we pass in to the draw image, CanvasRenderingContext 2D, draw image method.

Another very long page, okay with some hippos on it, that's fine. And yeah, there's a whole bunch of overloads for this method signature, so there's all kinds of things we can pass in. We're just gonna look at this first one to get us started. We will pass in an image element to that we wanna draw onto the Canvas and this could be all kinds of image g type things.

We could give it an image, an image element, we could give it a SVG, we could give it a video just what we're gonna do. We could give it bitmaps, we can do all kinds of stuff. But basically we give it something that the browser knows how to display as an image on the screen, and then we give it.

Essentially, this is now telling us about all these fancy things, but the d stands for sort of destination x-coordinate. So what's the top-left corner? Where is that gonna go in the destination canvas? And the y coordinate is, well, these two together are the top left corner, you feel me?

So the idea then is that we can also do stuff, if we want to only draw a subset of the image or only draw it to a certain width from the original image. There's all kinds of different settings we can do, but right now we're just gonna draw a whole image.

So we're gonna use this easy, simplest overload here and you can read the whole rest of this long page with hippos later. Okay, so how are we gonna do that? So yeah, if we draw, if we call drawImage with one of these imagey type objects, imagey type elements, Canvas will figure it out for us.

Now, anybody know what's gonna happen when I do this?
>> It doesn't know what video is.
>> Video is not defined. You know what? You don't even need to meet and run it, we can believe that this is not gonna work. Okay, all right, and we scroll down in the vite logs.

So vite has very helpfully been telling me each time it reloads the page, like index changed, source canvas changed, source canvas changed twice. Something messed up because you don't know how to write a path but all of these things vite is sort of helpfully telling us as it goes.

So it's constantly running, watching for our changes. Okay, so what if we get a video element? How's that sound? Should we do that? Okay, so, and this is where, again, it starts to become a choose your own adventure of like, how does it make sense to organize our code?

So maybe I want to have, this is a file that kind of, draw some JavaScript logos, which isn't necessarily what I ultimately wanna do. So maybe I want to actually start working towards my end goal of having a camera that we can then draw data from. So maybe we want to put this in a file called, camera or video, or selfie, JS, whatever we want.

And does everything need to start with a C in our source directory? No, it does not, this is just weird coincidence. But okay, this was an example of some of the things we can do with Canvas. Now we're gonna try to do the video thing that we wanna do.

So, that means I'm gonna have to go back and change my index.html, aha, remembered this time. So once I do that, no more JS logo because nothing's happening in camera.js, but so we can now start grabbing some videos stuff. How's that sound? Okay, so let's go back up.

So one thing we could do is add a video element to our page, that's not the only way we could play this. We could also, for example, create a video element, I don't know. And does this need to be on the top level of this module? Not necessarily, could be in, let's say a handy dandy, like get video function, for example and I can call this, my goodness function, okay, get video, I'll satisfy syntax.

Okay [LAUGH] move all this up, option up to do all of that. No, I'm gonna have to await some stuff in this function, one of the other things I'm gonna have to do is actually get the user media. So I'm gonna want an asynchronous function here. Cool? Now, what's gonna happen when I run [LAUGH] This?

Anybody see any issues? What's AVStream?
>> What's AVStream? Well, I don't know. Did I save it, no I did not, okay, no, yes I did, all right. No, actually this didn't do anything because this isn't a function and I never called it. So, [LAUGH] all right, so if I do a wait, which, by the way, I can only do in a JavaScript module.

So only in the type module do I get top-level await, another fun little nugget [LAUGH] of modules. So I can do await, getVideo, and maybe I capture that value as, a video, for example, and semicolons is a debate we can have another time. All right, so now I get the expected error somewhere, please move, okay, that I don't have an AVI stream.

What are you talking about? So let's make sure we grab that user media, like we said we were gonna do going further back. Okay, so we've figured out that we want to use this navigator thing. And that this is another reason why this getvideo is gonna have to be an AY function because this is an asynchronous operation, getting the user media.

And you know what I'm gonna do at this point is actually just stop doing the main js stuff for now, just so we can focus. Okay, still an error, here we are. Okay, so now I am basically just pasting this code in, letting VS Code format it for me.

We actually don't need the audio if we just want to take a selfie, so let's not even worry about that. Let's only ask for the minimum permissions that we need and not be like so many projects out there that just want access to basically everything your computer can do.

So let's just restrict it to the minimum viable permissions, which is gonna be video, And, something happened, a little box popped up being like allow this website to use your camera. Well, I don't know, maybe I'll allow it, okay, now we have a new book. A new error, which is that video is null because I never put a video element in my HTML.

So when I tried to select for this video element, no dice. So what if I do like create element instead and make one programmatically with the phenomenal cosmic powers of my dumb API. Let's see if that is more helpful,okay. And you you'll notice that, like, at this point,since vite is kind of automatically updating what's happening in the page here, you don't have to reapprove.

But if I were to, let's say open this again in a new tab, every different run of this script in the client will be asking the user for these permissions. So unless I, as the user, click that remember, where the browser is just gonna stop asking me this, every different user that sees this and even two different tabs in my device will be able to make different decisions.

So, question, what do you think happens if I don't allow it? Let's find out, [LAUGH] Okay, so if I block, heck no, I don't want local host to have access to my camera. We get every an exception on this getuser media function call, that says, nope you do not have permission, you tried to get that user media the user did not want you to or the platform or the browser something didn't want you to.

And so that's the kind of thing that we would probably in our app want to handle, let's say with a try catch, maybe some, like, please, please, please give me permission module, no, [LAUGH]. But so just keep in mind that this will error if it's not able to get that video because of like a permissions problem, okay.

And just perhaps useful for development is you can always kind of, in your different browsers, there'll be different ways to sort of reset your permissions for a given site. Anyway, okay, so back to our this one, where we have a whole beautiful page of nothing happening, amazing, let's go fix that, shall we [LAUGH]?

Okay, so first of all, what we said before is that we would all feel a lot more comfortable if, instead of main js injecting this massive string template into the inner HTML of our app. We just had this in our HTML, so let's just go ahead and do that for ourselves.

And now we can go to index and we're gonna have to make some changes to this but okay, fine, we can leave this outer div. Now there's some things that are probably not gonna work here, for example JavaScript's, template literal $curlies. We're not in JavaScript anymore, where we were inside of a template literal.

So, what we can do, though, thanks to vite, is in this HTML file I can reference things that are public assets or other kind of recognized assets that it'll capture for us if we're importing them in our modules and things. For now, I don't know, let's just pick one of these logos, let's go with the V logo, okay?

So since vites's logo is in our public directory, that means it's just a statically served file from our server at root. So the public kinda goes away, that's just for our organizational purposes. So what I'll do is, I'll just, let's forget the JavaScript logo for now, since we all know that that's what's happening.

And what we can do is just use source, vites.svg, and is there anything else that won't work?
>> Do you really want the dot in front of that?
>> Good question, let's find out, it worked. I'm not getting the styles though because those were being imported in my main.

So why don't we go ahead and bring back main and we could either we could choose to import the Vite logo in here or whatever but why don't we blow away this, counter related code and just import our style for now. And then in my index, I wanna go back and re-uncomment this name JS.

Right now I'm loading two different modules, which may or may not be what we want, so, we can look at that in a second. But at least now I've got some fancy styles happening and appearances are half the battle, right? And okay, my counter button is quite sad because I deleted all the code that filled it in with useful stuff, but that's okay because we're not super interested in a counter button.

We're perhaps more interested in, let's say, Take Selfie button, for example. So we're just assuming success and being the button we want to see in the world, and you know what, I'm gonna do this for fun, [LAUGH]. So now we're starting to get towards our selfie app, where it's gonna say hello, you [LAUGH] and eventually, hopefully we'll make this button do someday.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now