skip to content
Home

Custom Twitch Overlays and Interactions

with Jason Lengstorf

In this episode, join Jason for a working session on the Learn With Jason Twitch overlays. This will be a casual Q&A working session, so stop by to learn about streaming and building custom overlays with web technologies!

Transcript

In this episode, join Jason for a working session on the Learn With Jason Twitch overlays. This will be a casual Q&A working session, so stop by to learn about streaming and building custom overlays with web technologies!

What I was thinking could be fun instead is just kind of playing with some ideas around different things we could do on the stream. As you all may be aware, or you might be excited and/or disappointed to learn that there is a tiny Jason that you can grow a beard on in the app here. Or you can shave. This is a CSS only little Jason that Cassidy helped me make. It is one of my favorite things. We can also make it flap, right. So, this is like a little mini game. Wait, where's my flap? Hello? Did we break it? No, we broke it. How did we immediately -- like, this hasn't broken in months. Why today? Now that I want to talk about overlays, you're going to break on me? Let's see. Let's just refresh this whole page. Okay. Now you gonna flap? There it is. There's the flap.

So, this is something that Cassidy and I built together. It's a CSS only overlay. Another thing that you can do on the show is you can drop a boop from the ceiling, and it'll bounce around the screen. You can also -- if you do a party Corgi, it'll count those. I think it also counts Chrises. Yeah, it does count those as well. Or does it? Did it just count that? Yes, it did. Okay. So, it counts, you know, party Corgis from anyone who has one around the ecosystem. And I was thinking, like, what other fun things could we do along that line today? I saw Luke in the chat today. Has some really fun stuff, like pets and a lava lamp that I thought was really cool. So, I was thinking, what could we do? So, Luke, it is not p5. I'm using Matter JS. Actually, how about this.

Let's switch over and look at the desktop. I can show you some of the stuff that we're doing here. So, I'm going to go to this view. Maybe. This one. Good, good. And before I start talking, before I forget, do a quick shout out to the sponsors. We've got Rachel with us today doing live captioning as always. Thank you so much. She's here from White Coat Captioning. That is made possible through the support of our sponsors. We've got Netlify, Fauna, Auth0, and Hasura all kicking in to make this show more accessible to more people, which means a lot to me. So, I thank you for that. Thank you, all these companies, for pitching in. Thank you, White Coat Captioning and Rachel, for being here. And with that being said, let's take a look at Matter.js. So, this is what I'm using for the boops. And this is like a pretty simple 2D physics library. Simple was important for me because I don't know anything about -- like, I don't know the math that goes into these kinds of physics. I don't really know a lot about the kind of visual boundary detection, collision detection stuff that goes into this type of physics. So, for me, this is all very out of my comfort zone.

But what I like about it is they've got about a million demos. So, you can start looking at like what each of these things does, and you can kind of smash things around and play. By taking these and finding one that did what I wanted or that was close to what I wanted, I was kind of able to -- that's cool. I don't even know what a lot of these are. This is like if you were going to do a game and you wanted to have like shooting, you can see what we would be hitting by drawing this line. But it's just got a lot of cool stuff like this. I don't know how most of it works, but it is exciting. Anyway, let me drop a link to this. So that y'all can go play with matter.js because it is very fun. So, I know, the count reset with the scene switch. Maybe some day I'll figure out how to make that persistent. I thus far have decided that it's fun but not that fun. I don't know. It's fun but not fun enough that I've decided to do the work to fix it. (Laughter) How about that? That's where I'm at right now. But yeah, so what I thought -- like, a couple things I've been thinking about, and y'all can tell me what you think would be fun, I've had this idea for a long time about having some kind of a chat-controlled vehicle that you can drive around the screen. So, my thinking on that -- here's kind of where I'm at.

I love the idea of there being a little submarine or something. Let me see if I can find this. Let's see if we can find this thing that Dom made. Is that Dom's? No, where is it? Somewhere in here. He did this Photoshop and it's amazing. Photos? Okay, here we go. This is the one. So, like here's this little submarine where we're going to go learn. I was thinking, how fun would it be to be able to let people drive this submarine around? Like, this would be fun, I think. Is Ben -- Ben, come on now, trying to bury me? Can't be buried. Famous last words, I think.

So, yeah, here's kind of what I'm thinking. How do we -- (Laughter). How do we make something fun that we can all control together? So, the boops fall. The flappy Jason flaps and beard grows and everything. But how fun would it be if y'all could run a command like, you know, up, down, left, right, and move something around the screen? So, that's kind of what I was thinking today. How can we make that work? So, to do that, I'm going to -- (Laughter). Here we go.

To do that, I want to open up code here. We're going to get into the subscriber stuff later with prints. For now, what I want to do is I want to just kind of look at where we are. So, at the moment, we've got our scenes, and all of the scenes -- like, here's how this works. If you're looking at my files over here, this is a toast site. So, under the hood, the way the scenes work is they're a website. They're run on Toast, which is the front-end framework. It's powered by Rust, super, super fast. What it gives me the ability to do is to put up some pretty kind of compressed files. The output of this is pretty teeny tiny. That's good for me because I don't really want to deal with huge things. I don't want to deal with a lot of complexity. You can kind of see what's coming out here is modules. Like, ES modules. So the generated code is actually pretty friendly to work with. I'm a fan of such things.

So, I -- can I persist a page between scenes? I could. I have not done it. So, my thinking on this is like, I can make this into a full app, and there are a lot of ways I could make that work, but there are things I haven't done yet, like right now the request for Twitch data that comes in -- so if you look down at the bottom of the screen here, it says the custom Twitch overlays and interactions. That's the title pulled in from Twitch. Or I guess it's pulled in -- is it from Twitch, or is it from -- it's from the Learn with Jason API. Then like the sponsors, those images, those are pulled in from the Learn with Jason API. The chat is pulled in from Twitch. All of those requests are done asynchronously. But I have it set up, you'll notice it takes a minute for everything to pop in because I made those requests blocking instead of not doing that, which was, you know, a mistake on my part. But it makes the page loading a little janky. But I could absolutely do -- I could set this up in such a way that the page itself was static and I'd just move between the different scenes, like with routes, which would mean I could do stuff with -- what's the -- framer motion, or like one of those animation libraries that would let me do some really cool like animation and interactive stuff that I think would be super exciting. I have not done that yet. I mean, that could be another route to go down. I don't know. What do you think, chat? What's more interesting to you? Do you want to see like a little -- probably won't get all the way through it, but a crack at a submarine game? Do you want to turn the scenes into a single-page app? What do you think? What's going to make you happy? Anyone? Submarine. Yeah, that's what I was thinking too.

Now, the interactive thing, though, is super interesting. I'm going to look into that because I do think it would be super fun. But I do think -- I think the submarine is a more interesting problem to try to solve right now. So anyway, looking at all this, the way this actually works is I have all of my scenes set up here. Then I've got pages. My pages go -- I get this index, which pulls in some of the stuff I need to figure out where I am. Socket Studio is how -- it's the service. Some day this is going to be a business that y'all are going to be able to use. I'll get it launched eventually. This is how I talk to Twitch. It gives me subscription events and chats and commands and all those things. Then these pull in my content, lower thirds, and overlays. So, I get all of my overlays here. I get my Socket Studio client. I wrap that around my main content, so I have my big overlay. Then there's a top bar. So, that's the gradient bar at the top. This is one of those really subtle things that I did that I don't know if anybody has ever noticed, but those gradients are animated. So, if you watch them, they'll change throughout the show. They'll just kind of like pulse between the different colors. I spent a long time on it. I don't know if anybody notices. I like them a lot.

Then we've got this fragment here where if we're in the browser, we set up a router and we get the content. Then we add the overlays. So, the overlays are the invisible part. If you -- right now, you don't see the overlays, but if somebody runs like one of the commands, like adult supervision, that little box you're seeing over there, that's the overlay. So, that just kind of sits on top of things, and things are positioned. Then the bottom bar is this gradient right here, right there. And the lower third is a whole other section in components. Then I can look at the lower third here. And this pulls in -- like, that's the bar at the bottom that's got the logo at the -- or sorry, the chat bar here. Then if we go over this way, we have the title. Then all the way over is the logo. So, here's the logo. Details has got the episode and that rotating banner underneath with the details and sponsors and everything. Then this chat box.

So, the chat runs using Socket Studio stuff. It just pulls in the Twitch chat. I have to figure out what color somebody should be based on their role. Then use the Twitch chat based on my Twitch channel to then build out chat. So, this is under the hood powered by some pretty cool stuff. But here all we have to do is just get that chat. So, we just loop through the chat and build it out. Why am I logged -- I shouldn't be logging that. That's a waste of things to do. So, we get out our message. If there's not an HTML message, because I do some sanitization, then we do more sanitization. This is why you can use the marquee tag, but you can't do like H1s anymore. I remember when we first figured out that you could do HTML injection on my overlays. We did that -- like, somebody was putting CSS in here. It was a nightmare. Yeah, it was a mess. But yeah, so now we clean it up. So you can play with images and marquees. The images only allow the source tag and the alt tag. That keeps y'all from -- like, you can wreak what I would consider to be an appropriate amount of havoc on the chat box. Then, that gets built into a list item.

So, this is one of the things I'm really excited about with this. It just becomes code. We're not trying to figure out how to deal with the IRC part of Twitch chat or getting into third-party libraries. It's -- well, I guess Socket Studio is a third-party library, but it abstracts it all the way into a hook. I get the chat out, and I'm happy with the chat. Then we do kind of the same thing with a bunch of this other stuff. So, the details, this is a hook that I wrote for my own API, the Learn with Jason API so I can get out the episode details and whether or not it's loading. This ends up right now doing weird blocking stuff. So, I should fix that at some point. Not going to stress about it today, though. Then we also get the overlays. So, the overlays are -- like, there's a bunch of them. There's the beard game, the boop drop. There's the effects, which are like when you run a sound effect or something. Then there's the emote counter. The emote counter is like up in the top, up there. Then you've got the effects over in the far corner there. The boop drop is actually like the whole top part, up to the lower third. I love that. Y'all are just going all in on the marquees here. So, Alex, Socket Studio, like, I'm building it. It will eventually be a software as a service platform that anyone can use to power their stream. At the moment, it is a branded thing that I have built that is only available for me. But all the code is out there, if you want to look at it. It's on -- Socket Studio. This is the front end for it. So, this is how all this stuff works under the hood. If you look at the hooks, then we're pulling in like all of these subscriptions from a back end -- that's the Socket Studio back end that does all sorts of stuff and is kind of a pain.

Yeah, the whole overlay you're looking at is made out of HTML and CSS. So what I'm trying to do here is make all this stuff more fun. So, I want to take and be able to get my commands here. I'm going to go into the effects, which are here. So, the effects -- let's see. We're going to go into use sound effects. So, use sound effect. This one is a state machine. So, I'm going to -- I've done content on state machines, and we have more coming up. We have several episodes on state machines coming up, so I'm not going to go too deeply into this. This is going to be a great episode for that, getting into more state machine stuff with David. Then I'm also doing some -- really excited to see what this is because honestly have no idea what Abi is talking about. But that's going to be a lot of fun. And remember, you can add the calendar and you'll get a list on your calendar of what's coming up so you can keep up to date and come hang out with us.

So, what this does under the hood is it has a machine that checks whether or not we've got a command and whether or not one's active. It does a queue so it shows the effects one after the other. So, if y'all were to like spam a particular command, it would run again and again kind of in a loop, which we've seen sometimes. Like sometimes multiple people have the same idea to use the same command, and it'll play it two or three times. So that's a cleanup thing I should eventually do, where we'll go in and make this queue. If it's already playing, it would skip that one, and so on and so forth. But for the time being, nah. But yeah, so here we go.

We've got the sound effect. This is the part I'm interested in. So, we get this current command. The current command is what's going to let us determine whether or not this is a thing that we want to respond to. So, I'm going to create a new component, and let's call this component submarine. And in here, I need to look and see how I'm actually building this stuff. So, let's look at one of the simple ones. Here's the boop drop. Let's just grab that part out. So, I'm going to start there with our submarine, and I'm going to export function submarine. That's going to take no props, I don't think. And for now, we probably just want to return some kind of a submarine. So, what I'm going to do for now, I think, is I'm going to see if I can find one that's free. Eventually, I will probably draw one of these. But if I can find one that's royalty free, then I don't want to -- I just don't want to get this pulled down or anything on YouTube for using somebody's images without permission. Anybody got a submarine I can borrow? Don't think I'm going to find one here. Nope, okay. You want to just make a submarine real quick? Let's do a quick and dirty submarine.

So, I'm going to create a new thingy in here. We'll do one of these. Then we'll get a -- like an oval. Like that, kind of. Then I want my submarine to be a little bit like front heavy like so. Then we'll give it -- like, take one of these and just go boop. Feels kind of submarine-y, right? Then I want to give it a periscope up top, so we'll do one of these. This is a bad submarine. It's okay. It's all right. The goal isn't to be good at this stuff. The goal is to have a good time. So, let's do this. Get rid of this. We're going to open up this fill and make it the same. Good, I'm happy. Then I'm going to merge -- oh, wait. Not yet. I want to copy this bit, and I'm going to move it down. No. Copy, move into a new -- no, what is that? Give me this. That's what I want. I'm copying you, and I'm coming out here and pasting. I want to flip this -- how do I -- if I just want to flip it horizontally, is that not a thing that I can do anymore? Didn't this used to just work? I don't know. Let's do it this way. Good-bye. Then I can bring this straight down. Now we've got a really, like, jankarific submarine that looks kind of submarine-ish. I would say close enough for horseshoes and hand grenades. So, I'm going to take these bits and merge them all into one thing. Now I'm happy. Then maybe we'll give it like a porthole so we can see out of it. And that's going to be a subtraction so that we get a hole. Hey, look at our submarine.

So, I'm probably not winning any design awards today, but I've at least got a submarine I can use. Now I'm going to crop this out, make it a little bigger. Actually, can I just export this directly? Yes, I can. I'm going to do it as a PNG. I don't know what that is, so I'm going to get rid of it. Let's just export our submarine, and let's make it a color that I like. Let's make it -- oh, you know what we should do? Let's take one of the colors from our setup. I've got in here styles, globals, and let's make it a yellow submarine, a yellow submarine. Then we can drop that in here. There it is. Then I'm going to give it, because that's a little jarring without a border, I'm going to get the black color, and we'll give it a -- am I hurting you right now, Luke? (Laughter) So, there's a rough submarine vibe. That's kind of fun. I'm happy. Let's take this, shrink it down a bit. That feels like an acceptable submarine. So, I'm going to -- I need to turn these outlines -- good. Now I can export this as a PNG. I think I'll do it with some space. So, let's do it like this. Submarine, got a little bit of space, gonna make it a PNG, export it, and now I have a submarine we can use.

So, in my static stuff, I'm going to drop in the submarine. Oh, no. You kept the background. I need to turn off the background. Okay. So, you're going to go away. Bloop. Then this should now not have a background anymore. It doesn't. Okay. Let's make it tiny as well. We'll just get rid of a whole bunch of junk on it. I love this tool. So, yeah, now it's under 4 kilobytes. That makes me happy. Then I can go over to here. Oh, wait. Did I just turn that into a JPEG? I sure did. You know what, not going to worry about it. It was 37%, and that's going to eat up a bunch of time, so I'm not even going to stress about it. What is this link? This is a risky click. I guess it's Wikipedia, right? So it's only medium -- oh, hey, look at that. That is -- yep, that's why the periscope is there, so you know it's a submarine.

Yeah, so let's see here. Now that I've got our submarine, I'm going to get rid of this one. We're going to put the actual PNG in there instead. Replace. Okay. So, now we've got static. We've got our submarine, and yeah, okay. I'm happy. So, then what I can do is I can, in here, just have an image source, and because this is toast, the way this works is anything in static just ends up at the root of the site. So, I'm going to do submarine.png. Eventually I'm going to move this out. I'll call it submarine, and we're going to start there. So, then I'm going to go out to my overlays. We're going to pull in submarine from submarine. Again, this -- the reason the .js is here is we're using ES modules pretty close to the way they work natively. So, you have to include the extension. That's a feature of Toast that I really like. It's not doing anything magic. So, then we can go in here, and we can do the submarine. If I run this, which, let's see if it's going to do the thing. Let's go with -- it might complain because of this state machine stuff. So, I think I might need to turn this off. But let's -- hmmm. Let's see if it works. If it explodes, we'll figure out how to back that out. I feel like when we worked on this last time, it just didn't do what we wanted it to do. So, I'm going to run npm run build, and it doesn't like -- I think this is the state machines thing we ran into. Yeah, it doesn't like the state machine. So, I'm actually just going to back this out because we ended up doing it in a different repo. So, no more state machines. Okay.

Then that means that -- where was I using that, though? Nowhere. It's gone. It's out. That was just a page. That's right. We kind of -- oh, wait. I need to remove the stuff that I'm not using anymore, so we'll get rid of -- am I using this anymore? Let's look. Xstate. That one is using it. Which one is it using? Xstate React. So, let's get rid of the fsm. Then we're going to run an npm install. Then we're going to npm run build. What are you mad about? Cannot read property place of undefine. That seems like something else. Socket Studio. Am I missing -- oh. I have environment variables. I need to run Netlify dev. There they are. So, these are coming in from my .env file. Because I have local settings that are different, they get overrided. So, now here we go. Hey, look at that. Look at that submarine.

So, the next thing I want to do is we can go to one of these. We'll go to monologue. So, there's no video here. These are actually -- because I know exactly what resolution I'm using these at, I have them like hard configured to be a certain size. So, that is a little bit jarring if you're looking at this. Like, why, why does this look so janky? It's because these are expected to be at exactly 1280x720, I think. Yeah. Yeah, yeah, yeah. So, what do we do next? Here's what I want to do next. I want to get commands coming in, and I want to see -- I've never actually tried this. I want to see if commands that we run are just visible or if I need to do something else. Look at that. There's the chat coming in. See?

So, here what I want to do is I want to go into the submarine, and I'm going to import -- where is it? I want to get the effects, and the effects are in the hooks. I just want the current command. So, I want use Twitch chat. Oh, that's all I wanted anyways. So, I'm going to pull in from Socket Studio, and then I put in that. Then what I'm going to do is I'm going to console log the command. What I'm going is getting current command and aliasing it to command so we can see what's going on. But let's try this. So, I'm going to stop and restart. Then -- yeah, let's see. Sorry. I'm trying to catch up on the chat. I did just auto mod something. Keep it clean in the chat, y'all, with the terminology.. okay. So I am -- this is running. Now what I want to see is I think what should happen if I reload the page is we should be able to say any command. So, I'm going to do up. We got a command, and our command is up. So, let's try another one. Down. Right? And these are all just coming in the way that I want. Okay. Hey, this is exciting.

So, check this out, y'all. We are -- hacking was not the word in that, that got it moderated. So, we are now able to kind of control this thing. So, here's what I want to do. I want to set up some basic config here. So, we're going to do a div with a class name of submarine. Then I want this submarine to be on the screen. So then I'm going to give this -- or I guess we should probably call this like wrapper. Then we can give this one a class name of submarine for real. So then down here we can say -- in our styles, we've got lots of things we could do, and I'm going to say -- we can stick it at the bottom of this one for now.

So, what I'm going to do is let's do submarine wrapper, and I want to make this width 100%, height 100%, and let's put a border of 1 -- actually, let's go more 10px, solid red, so we can see what's going on. Then I also want to make this position relative because then what I want to do with the submarine is I want to make this one position absolute. Let's give it a width -- let's make it like 100 pixels with a height auto. Then I want to set the bottom -- let's do it this way. Let's do a top of 75%, and we'll do a left of like 5%. And I'm using percentages for a reason because I want to -- yeah, I know my dimensions, right? So, I think what I can do is I can do -- I can just bounce this thing around the screen based on command. So, let's see if we can make this work. I'm going to save this. I'm going to save this. Then I just want to make sure that when I'm looking at this, that the styles got picked up and that things are sitting where I want them to sit.

So, if I reload, okay. We can see now we've got the submarine wrapper. Our submarine is down here. I'm going to tweak these values a little bit because I don't want them to be quite so -- what is happening? Something weird is happening. Where is my -- beard game submarine wrapper. There's my submarine. So, I want this to be -- let's call it 80 pixels. Then we're going to make the top value 90%. Left is going to be -- actually, I like that. Let's set it down in the corner. Okay. So, 90% and zero. I spelled height auto wrong, so I guess I didn't need that at all.

So, let's just go clean that up. We can go down here, say we wanted the top to be 90%, and we want the left to be zero. So, then, knowing those details, now what I can do is -- oh, wait. Wait, wait, wait. How can I -- I can make this easier. I can set CSS properties -- what's easier to do here? I guess I can just inline style this, and that'll be fine. So, if we have commands, right, I can then use effect, and that means I need to pull it in. So, let's import use effect, and I think I also don't want to rerender the page. So I'm going to use ref from preact hooks. Then in here, I can say ref equals use ref. And in here, I can set up a command that's only going to change when the current command changes. Inside of it, I will then make the ref, ref. Now I can get at this dom element without rerendering the entire React component. So, that means I can do my submarine -- oh, we've got echoes because this one is also active. So, let's mute this one so we don't get doubles. But yeah, that also works in here. So if I run one of these commands, it'll show up on both screens. Oh, that actually brings up a good point. I should probably -- what do you think, should the alerts take precedence? They should go over the top of the submarine. And that's okay. The submarine can stay back. What about the boops? Let's see. The boops are currently in front of the submarine. That seems correct. Yeah, because then you can dive the submarine into the boops and that's funny. Funny to me at least. That seems fine. Wait, the sound effects aren't making sound? Oh, you know what I bet it is? I bet it's this. Somebody run a sound effect. Let's try it.

Ahem.

You hear that one?

Oh, so we would need to add code.

Okay, perfect. Yes, I forgot to enable that thing. Cool. So, all right. Now we have our submarine. How we doing on time? Actually, we've got a while. We're doing okay. So, now that we've got this submarine, I want to move the submarine to the left or right based on commands. So, I think the way that we want that to work is we're going to do ref current to give us our submarine. Then I want to do submarine styles. Let's do this. Actually, we should check here. If the command -- and what is our command name? Are you going? Here's my command. The command has a command. So, if I -- that's silly, but yes, that's fine. So if the command command is going to have to be one of the ones we allow, so we'll say up, down, left, right. Then we get to remember if it's includes -- I think it's includes because it just auto completed for me. So, if it's one of those commands, we want to do something. Since I don't want to wrap this whole thing in an "if" statement, we'll just early return if it's not set up. And that's because I got this curly brace. Okay. So then once we get down here -- let me close this up so we can see what's going on.

So, these are the commands we're going to listen for. And we're going to have our submarine. That's our ref current. Then what we can do is we can switch -- do we even need to switch -- can I just -- it's probably easier. Let's just switch on the command command. And we'll do a case up, and I want the submarine styles -- I don't like that. That feels messy. Can I -- I want to like access the -- let's just run some console commands out here. So, if I get -- let's let sub equals document query selector submarine. Is this even legible for y'all? This is kind of teeny tiny. So, I have my submarine. If I want to get the sub style, it pulls all those things in. Then if I do sub style left, it's nothing. That's not right. So, inline style I don't want. Let's see. Zell has been on the show. He knows things. Element style font size? But that's what I did, and it wasn't set. And it is set. Because if we go and look at it over here on the submarine, it's got this value set of 90%, right? And so why wouldn't that show up? Right? Like, am I missing something, y'all? Get computed style? Wait, where is get computed style? What's the API for this? Why don't I know? I've used this before. Get computed style, and then I pass in an element. Then what? Get computed style. Here's the computed styles. Okay. I can do that.

So, we're going to here. Get computed style of sub. Am I in here? Then we can do styles, get property value, left. Okay, that's kind of correct. Computed in not percentages is kind of a bummer, but I can live with it, I guess. I'm going to -- I guess we can just kind of figure out if it's less than zero, we would be able to -- so, we can do a const style equals window get computed style. We don't even need to do the shortcut here. Or, yes, we do, because I want to change it later. So, we have our styles. We have our submarine. Then I want to -- if the command is up, I want the current left is going to be styles, get property value, left. And I want to parse that as an integer? So that we get like a zero, right. That way zero px will become zero. Yes. Then I can say new left is going to be current left is greater than zero, and we'll say -- actually, let's do it at ten. Then we'll say the new left is -- if it's going to move left, then it would be current left minus ten. Otherwise, it would be zero. That way you can only go to the very left edge of the screen. That seems okay. I think that's fine.

So, then we can do left equals new left -- do I have to set it as pixels? Probably. This is up. Let's try left. How about that? So, I'm going to start it at left. Then I'm going to move the left to like 100 pixels so that we can just test this. Let me save that as well so we stop seeing the chat in the console. So then I'm going to -- let's just give this a little test. A little test. So, here's our submarine. If this works, y'all should be able to control it right now by running the left command. Command is undefined in submarine js14. Oh. So, at first there is no current command. That would give us an undefined. We can just check for that. So then, try that one more time. No errors. Let's see it, chat. Run a lefty. No, you're unhappy again. Submarine styles is undefined. Yeah, I screwed this up, didn't I? Is it just regular style? Okay. So, let's try that one more time. If this one doesn't work, I'm going to start doing it in the console at first. Okay, so let's reload. Give me a left. Hey, look at it go! Look at it go! (Laughter)

Okay, this is great. This makes me really happy. So here's what I want to do next. I want to see if I can transition, ease in out, and we'll make it a little slow. So maybe like 300 milliseconds. And that, I think, fingers crossed, we should get an animation when it moves. So, let's try that again. All right, everybody. Move it left. Look at it go! Okay. So, this is fun. This is super fun, right? So, now what I want to do is I want to just kind of duplicate this so we can get it for the other direction. So, let's go with --

Holy buckets, did that just work?

It sure did. (Laughter)

So then we can just return here so that we don't end up hitting any of the other cases. So, I want to write -- we can do a const current -- you know what I'm going to do? I think I'm going to -- nah, it doesn't matter. We can do current parse int of styles, get property value. And we're going to say left. I feel like I can just do -- mmm. This is a little repeat yourself-ish. So, I can -- I'll have to figure this out. Let's go current is less than -- I'm just going to hard code this for now. Let's go in and figure out what the submarine's furthest possible left can be. So, let's set the left up here. Boop, boop, boop, boop. Probably like 1100 maybe. Nope, that's too far. What about maybe 700. But is that going to be -- how wide are you? You are 100 pixels wide. So, I could do window -- no, that won't work because then for -- I need to get like the inner width of the -- of this thing. Which I can do with -- it's like, what, document, query selector, submarine wrapper. Then we can do get inner width. Where is it? Anybody remember this off the top of their head? Get bounding client rect. Did you see Cassie codes has -- this made me really happy. She has a new sticker coming out. Let's go to her media. Look at this. That's going to be a sticker. And it makes me extremely happy. And you should definitely go get on that waiting list because that's going to be really fun. But yeah, okay, so we've got our bounding client rect. Then if I do a width, we know how wide it is. Okay. So, here's what I can do then. Then what I can do is set -- I can set this to be the wrapper, and the submarine is going to be wrapper.queryselector.submarine. Now we have the wrapper on the outside, the submarine in the middle, and now we can kind of touch all that stuff. So I'm going to move this ref up to the div instead. And now what I'm able to do is change the submarine style, but I can also get the -- go max left will be -- it was already -- so, it was going to be wrapper get -- already forgot. Bounding client rect minus 100 because that's our -- that's the size of our submarine, right? So the furthest left we want to two is where the submarine would be against the right side, not where it's 100% off the screen.

So, then we can say if the current is less than the max left minus -- we need like a movement size here, so a movement distance. We can set that to ten for now. Then what I want to do is just kind of make these a little -- movement distance, that way, when I decide inevitably later that I want that to be something different, I'll make that better. So, then what I can do is do current left plus movement distance, or we can make it max left. Okay. So, if you try to move too far to the right, it'll stick at the right. If you move to far to the left, it'll stick at the left. That makes me happy. Then we can do a submarine style left equals new left pixels. So, this is not as dry as I want it to be, but it does the job. So let's see if we can get left-to-right movement working here. So, we'll give that a shot.

Wait, what don't you like? I must have missed something. New left has already been declared. Okay. So, I need to do this a little bit differently. Want to see something gross? What if I do it like this? He-he-he. I mostly just want to see if this works. This is going to need refactoring to not be very upsetting. But I'm pretty okay with it for now. So, let's give that a shot. All right, y'all. Let's go left and right. So, let's go right. No, it doesn't like it. Can't access lexical definition before initialization. Boo. Screwed it up. Okay, one more time. This is a mess! We will go and clean up that mess, probably. Okay. Let's try right again. Here we go. Right. Is it doing it? It still doesn't like it. Oh, my god, I did it again. I am very -- like, my automatic pluralized styles, that part of my brain is fighting hard. Right. Look at it go.

Why do all the right things say left? That is a really good question. It's because we're doing absolute positioning. So, when we're moving the submarine around, what it looks like is happening is that it's moving right, but what we're actually doing is changing the -- where is it? Content monologue, overlays, submarine wrapper. We're changing the left position to be higher or lower, right. So, when you run -- if you want to run the right command, we'll actually see that value update to either go -- yeah, ten pixels higher, which moves it further away from the left, or it'll decrease by ten pixels. Now, I want to do one more thing. I want this thing to turn. So, that is going to require me to do a transformation. So, that transformation is going to be transform -- we're going to do -- can I just like -- I can flip this thing. The way that you do that is you scale X negative one maybe. There it is. Heyo!

So, the other thing I'm going to do is set the transform on both of these. Submarine style transform is going to be scale -- what was it? Scale X negative one for this one. And we'll take that same thing and put it up here and we'll make it one on this side. So, now what we should see when we move it around is that when you move right, it should flip around and turn right. And when you move left, it should flip it back around and turn left again. So, let's try that again. Let's go right. Oh, oh, it's fighting. Then we go back left. And it flips left. This is fun. Ok ay. So, now we have a submarine y'all can control. This is nice and fun. I am very into that. If I used absolute right instead of left, I could have it positioned relative to the camera source. That's true. Yeah, I mean, it's definitely -- I could put it wherever, right. Then you could deliberately avoid your face. Well, so the way it's set up right now is that I think -- let me double check. Let's go to interview. Nope, not that. Let's do paraprogramming. So, right now it would be able to get right on top of our faces, but I think how this will work is the content -- there's a way. How do I do it? I've done this before. Bottom bar, lower third. I don't know. I have some logic I can set if I need to that will change out like where this box lands so that it would skip like the videos on the right if we wanted. But I think for now, just for fun, we can let this kind of go wherever. If the chat really wants to be mean, they can just sit that submarine right on my face like jerks.

But yeah, so I think this will be really fun. And maybe we can make the distance a little greater. Let's go with, I don't know, 30. So, it'll move a little farther each time. So, now it should move a little further wherever you -- yeah, a little bit further each time. Okay. That seems fun. I like that. Yeah, I'm not looking forward to -- this is the problem. I say things, and then I realize what I've just said and realize the chat is going to take it as a challenge, and then I have regrets.

So, the next thing I want to do is I want to figure out how to do this on the up-and-down axis. So, maybe, maybe what we can do is -- you know, I feel like I need to clean this up because it's just so messy right now. I wonder if what we could do -- what's the right solution here? Maybe we -- I don't care. I'm not going to deal with it right now because I feel like I'm going to get myself in a rabbit hole, and then we won't get the rest of this done. So, instead, let's do up. In the up case, we want to basically do all the -- actually, maybe we can write this better by doing these both at once, right. So, I want to do -- how do I want to do this? We want to go with direction equals command command up, and if the -- so, here's my thinking. We need to go up and down. So, I can say that if we're going up, we want to remove a value. I need to actually write this as valid code, I suppose. And if we want to go down, we need to add a value. So, we can say distance will be movement distance times direction. So, now we can either go 30 pixels increased in the value or 30 pixels decreased in the value. Then what we can do is we can do the same thing that we were doing before where we get the current top, and that current top is going to be parse int styles get property value top, and the max top is going to be wrapper get bounding client rect, height minus -- how tall is this thing? Let's find out. It is 58 pixels tall. So let's just call that an even 60. Actually, let's let it dive a little off the screen. That'll be kind of fun. So, it can dive halfway off the screen. Then we want to calculate new top is going to be current top is -- oh, no. We want to make this into a statement. We're going to let new top, and then if current top is less than max top, minus new distance or movement distance, then we want new top to be current top plus distance. Oh, wait, no. That's actually not even true. We don't need to do that anymore. Because what we can do is we can do movement -- current top plus distance, and that, if it's less than max top and current top is greater than zero plus distance, because now this could be a negative value, right? So, if it's greater than zero, then we would do new top equals distance. Otherwise, it's going to just stay the same. It just won't move again after that point.

So, then down here, we can do submarine style left -- or top -- equals new top pixel. Is that going to work? I don't know. Let's find out. Let's see. Yeah, let's give it a shot. So, running this, assuming I didn't typo anything. Okay, nothing exploded there. Let's see if we get errors in the console. Request blocked. That's fine. That's fine. Let's try some up and downs. Up, up. Down. Ha-ha-ha. Then we can move to the right. Now we can go up, up. So, what happens when we get all the way down? Oh, so it shouldn't go down any further, I don't think. Yeah, it sticks down, but what I want to do is I want to make that overflow hidden so that it like dives under the screen. So let's have the submarine wrapper go overflow hidden. Then let's dive it back down to the bottom and make sure that works. So, let's go down, down, down. I'm abusing my moderator privileges. Okay. So, that's gone. If I get rid of the border, yeah, it'll just sink all the way to the bottom and sit. Okay.

So that -- I'm pretty happy with that, y'all. I feel like we did okay. So, let's get rid of the border. Let's go overflow hidden. And I think we're good. We did it, I think? Let's try that one more time. Let's go play again. Okay. All right, everyone. Dive it. Down, down. Oh, it just sat there. Hmmm. Maybe it needs to start in a different place. Let's see. So, if I go find my submarine, it's defaulting to the top of 90. So, I wonder if I need to like take this thing -- let's turn off that and maybe we can make this one like -- what would it be, 500 pixels? So, that would start it at the bottom. I don't really like that, though. That seems -- what if we calc it? 100% minus 30 pixels. Right? Because then it kind of starts just chilling on the bottom of the screen. Then our left -- maybe our left is actually like 100% minus 100 pixels. Maybe we just start it with a transform of -- that would not be that. So, now we've got like a submarine chilling in the bottom, and you can start flying it around. That would be -- barrel roll (Laughter). That's funny. Okay. So, this seems okay. I think maybe we'll start there because it overrides pretty much immediately once they get there. That puts us at a good position for sticking it on the screen where we want without relying on absolute values, which I'm pretty happy about. Then we can default this transform in here so it's present. Okay. So, now we have transition. We've got this transform. We've got the left and height set. Okay. So, I'm going to try this one more time, assuming it looks and works the way we expect, I'm just going to deploy it. We're going to see what happens if we take this to production right now. All right, everyone. Roll this thing around. Let's go up. Let's go down. Okay. Let's go left. Let's go right. Beautiful. It's beautiful. This is -- okay. Yolo. Let's ship it.

Let's do it in here. I'm going to use the visual thing so y'all don't have to watch me dig through here. Why is that even showing up? Let's ignore ds store. We're just not there. Don't care. So, now we've got the package lock. We want that updated. We've got the chat. That's getting rid of the unnecessary -- so, we can do like a chore, remove console log. This one, we're going to chore, ignore system files. What? Oh. Then this one we got rid of the unused package so we can add these two and say chore remove unused packages. Then down here, now we get into the interesting stuff. So we have the submarine. We have the new component. We've got -- am I still console logging anything in here? No. Okay. I've got my submarine. Got my overlays. So I'm pretty happy with all of that. I do need to refactor that left and right, but I know how I'm going to do it. I just don't know that we need to do it in the few minutes that we have left. So let's call this one a feature, and we're going to say add chat controlled submarine. Okay. So now that we've got that, I can check here. Good. I'm going to push. We're going straight to main, no pull requests today. Now that I've got that, I'm going to open my Netlify account. And it opened in the wrong window. That's fine. Let's go in here.

So, this is the site building. This should all happen pretty dang fast. Oh, yeah. So, the prefixes that I'm using, they're a good habit? A carryover from when I was doing product engineering at IBM. We were generating automatic change logs. So, using that convention where you prefix with chore or feature or fix or there's a few others, what we were able to do is run a script called conventional change log, I think, that would automatically build a change log with what it actually changed since we cut the last release. It's really, really nice.

So, okay. This is deployed, which means if I reload the overlays here -- so, let's refresh. And we have a submarine. Okay, y'all. You want to fly this submarine around the screen a little bit? Great. It's going straight for my face, isn't it? Good. This makes me very happy. Oh, that's interesting that it just redeemed boop. Whoa, does that work with like miscellaneous stuff? So it says I redeemed boop. What if I just redeem something. No. I have built -- I'm super happy I added this submarine now. I immediately have regrets.

Oofda, all right.

You know what could be really fun actually, chat? What if we made it so that you could change the image so that what was on the screen actually could be -- you could change what it was? That could be really, really fun. Probably -- I mean, not something we're going to get done in ten minutes. That could be a really, really fun thing to do and something that could be a lot of fun to play with. Yeah, I can definitely tell this is going to go well for me. But here's the thing, chat. Here's how this is going to work. You get to troll me all you want, but don't you put this submarine on my guests' faces. You can disrespect me all you want, but in this house, we respect our teachers. (Laughter) I'm so -- what was I thinking? Why did I think this was going to be a good idea? What part of me was like, you know what would be great, let's let the chat put stuff on my face? (Laughter) Yeah, it's definitely going to need safe regions. For now, this will be okay. If you all refuse to respect, I will take it away. That's how it's going to go. If you abuse my guests by putting submarines on their faces, I will take away your toys. That's how it's going to work.

Is this on GitHub somewhere? Yes, it is. It is on learnwithjason/scenes. You can check that out there. The code is up now. You can see the commit that we did is all here. So, this is -- you know, this is pretty quick and dirty. I did a lot of kind of messing with things to get them to work. This especially I think is not -- this is code that I would refactor before I would -- like if I was doing this at work. But I do think like down here, this feels usable. So, I think I could figure out how I was going to manage that and kind of refactor. Like, I can even see this is actually pretty usable. So, I could move some of this out into an even, like, slightly more clean abstraction. But here's what I think is fun about this. What we were able to do -- will you get off my face? Come on. Get out of here.

So, what this has done and what I think is really cool is that it's now kind of proven something that I was hoping for, which is that I don't need to build a bunch of really special magic stuff here to make this stuff work. But you know, actually, you know what I think is kind of interesting? All right. We've got about three minutes before I'm going to shut down. I want to try something really, really interesting and simple here. So, here's what I'm going to do. I'm going to go get a drawing -- I'm going to get any image I have available on my computer. I'm going to get this dumpster fire. Let's take the dumpster fire, and this is, I believe -- it is a PNG. So, I'm going to take this dumpster fire, and I'm going to make this a thing that we can put on the page. The way that I'm going to do that quickly is I'm going to take the submarine, and I'm going to hide that, and then I'm going to paste in this dumpster fire. And I'm going to put this right in the center like that. Then drop it in, call it dumpster fire. And we're going to export it.

Here's my hope. My hope is that if I put this dumpster fire right in here -- okay. Then we go into submarine.js and we say -- uh, let's do a use state. There's zero chance I'm going to get this done in the amount of time I want. We're going to call this image and set image. That'll be a use state, and it'll default to submarine. Then I can use state, and if the command -- let's see. So, if we can say if command and command command equals pet -- so, the command we're going to allow is pet. Then I'm going to console log the command because I'm not sure if we're going to get this. But then what we're going to do is we're going to set the image to -- for now, we're going to hard code it to dumpster fire. So, what we can do is we can either set up like a switch -- but I'm curious if we could say pet and a value and just get that back or if I'm going to have to add something. Then I'm going to return. What we can do here is we can say, instead of this, we're going to set it to be a submarine -- oops. It'll be a string that gets the value of image in it. And we'll also set this to be the value of image -- nope, that's not at all what I wanted. But this one -- you know what, no one would ever look at this with a screen reader, but I don't like -- I feel weird not doing it. So, I'm going to do that. Now what I think should happen is if I run Netlify dev and we're going to try this once, if it doesn't work, I'll do it on another day, but what should happen is now if we go and look at this page here and y'all run pet -- and I'm going to say like pet dumpster. It changes to a dumpster fire. So, now you can fly a dumpster fire around. And that's really fun. What I'm really interested in is when we sent in pet, what did it give us? So, it said args. Oh, yeah, see? Sometimes I'm a better programmer than I thought. I thought of this. I was like, oh, yeah, I want the thing to exist here. So we have -- we've got access to these args.

So, what I can do -- and I'm going to take exactly one second to do this and then we're going to shut it down for the day, is in here I can say we've got submarine and dumpster fire. Those are going to be our pets, right. So then we can say if -- or actually, we can do new pet equals pets includes command args zero, it will be command args zero or it'll be submarine. So, then we can set the pet to new pet, okay. So, let's give this one try. So you're going to have to call it dumpster fire to set it, but let's see if this works.

So, I am loading it up here. We've got our submarine. Okay. Somebody try changing the pet to dumpster fire with a hyphen. Let's pull it up a little so we can see. Okay. Now change it back. So, I'm going to change it back to submarine. Then I'm going to try to change it to something else. When I try to change it to not real, it changes back. Oh, that's cool. Eddie, yes, my code is auto formatted by prettier on save. So when I make a big old mess, prettier will get rid of everything.

So, y'all, that's so fun. I'll get this deployed after stream so that next time we'll be able to play around. I'm also probably going to draw a submarine and a few other things. But yeah, this has been a blast. So, make sure you go and check out the schedule. We've got so much fun stuff coming up. We've got -- so many good things. So, please, please, please go check that out. Come hang out. Huge shout out to Rachel, who's been hanging out with us all day doing the live captioning. It's wonderful to have her here. White Coat Captioning has been with the show for a long time now doing live captioning. Right on the home page every week. That is made possible by Netlify, Fauna, Auth0, and Hasura, all of whom are pitching in to make this show more accessible to everybody. It helps me keep the wheels on Learn with Jason. Very, very much appreciate y'all for kicking in. Make sure you go to the home page. You can click these links and check these things out. It is a lot of fun. I love these tools. I think you'll have a lot of fun as well. I think that's it, y'all. Who should we raid together? Let's figure it out. Let's see who is on. I'm going to go to browse, live channels. Who's live right now? Let's look at web dev. See who's doing web dev. Hey, look at that guy. What do you think? What do you think, y'all? I develop things. LL Cool Chris. Portfolio dev. That sounds fun. Let's raid LL Cool Chris. Let's make this happen. We're going to raid. And with that, we'll see you next time, y'all. Thank you so much for hanging out today. See you on, what, Tuesday. Later.