Build a Custom Twitch Overlay With HTML, CSS, and JS
Did you know you can build Twitch overlays using HTML, CSS, and JavaScript? In this episode, Jason will work on the LWJ overlays to add a new subscriber alert!
Links & Resources
- https://cloudinary.com/product_updates/apply_video_transparency?ap=lwj
- https://www.pixijs.com/
- https://github.com/overlay5/overlay5/blob/master/lib/pixi-chromafilter.js
- https://cloudinary.com/product_updates/apply_video_transparency?ap=lwj
- https://en.wikipedia.org/wiki/Chromium_Embedded_Framework
- https://www.learnwithjason.dev/let-s-learn-typescript
Full Transcript
Click to toggle the visibility of the transcript
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
Hey, everybody. Welcome to another episode of Learn With Jason. Today is gonna be a little bit different because we had a last-minute reschedule. If you are following events in the United States right now, you know that things are a little messy. Oh, I'm muted.
Hey, everybody. Can you hear me now? Okay, now we got audio.
Hello, everyone, and welcome to an episode of Learn With Jason. So today is going to be a little bit different. We had a last-minute reschedule. I know a lot of you are here to see Emma. She has pushed back. We're still going to do the show, we're just not gonna do it today. If you've been following events in the United States, you know it's not that great right now. So today I am coping with stress the only way I know how to cope with stress, which is bury my way all the way inside of a project and just, you know, make the best of it. Do what we can. So my intention for today, my plan for today is to work on the Learn With Jason overlays and we're going to see if we can find our best way to make some jokes and keep our minds off the situation we can directly control. I will say, you know, out to message of support to all the people who are currently watching basically white terrorists walk into a government building with no challenge, whereas, you know, if you're Black in this country, you can't walk to your car. So, a little solidarity there.
Okay. All right. So, that -- ready? We're ready. Okay, so, today I want to work on -- I have a new ridiculous thing that I made. So, this is the most ridiculous thing that I've done yet, and I hope that you all love it. This is what I want my new subscriber animation to look like. So, you can see here I'm walking out and kind of looking around. I got a green screen, and I am just thrilled that this exists, right? So, what I want to happen is whenever somebody subscribes, I want to play this video and I want to show someone's name, whoever subscribed. I want to say what they did, right? And, so, what I need to do for that to work is the -- so the way that this works under the hood is this is controlled by -- it's an MP4 video, and as an MP4 video, it has a green background. So if I actually turn that off, which I will do how? Find the buttons. Here we go. Here's the button.
So I can turn off the chroma key and you can see this is just a green box on the screen. And I can have it look for the color green and then filter that out so that we only get the colors that aren't green. That means that if I want to do this the way that I've been doing most of my overlays, it's not quite gonna work the way that I want it to because right now all of my overlays are part of the same website. Each of the pieces of the website, so, the lower third, which is the logo, the title, the sponsor information and the chat, that's called the lower third, and what that is is actually it's just like a div, and I have a display grid in there. And then the chat comes from a GraphQL subscription from something called Socket Studio that I built. And the way we're gonna handle this is, like, there's a GraphQL subscription that brings me chat events and it also brings me sound effects. If one of y'all decides to play a sound effect right now. I'll turn off that video. So the way that the sound effects works is, yes, my chat does filter out HTML now and it's better than it was.
Like, try to do -- yeah, so you see what just happened there with the hosting? And I saw that Bobby Tables is also hosting as well. Thank you both very, very much.
So the way that this works is that when an event from Twitch happens, it sends an event, and that event gets picked up by this website. This is a react -- I guess preact, actually, overlay that will, like, listen for these events and then activate and display certain components based on what happens. So, that's built using toast is what the website is built on. And, actually, you know what? Why don't we switch over and I'm going to talk about the sponsors for a second and we'll get going here. So, let's go over to solo stream, and I'm going to open a new browser. Kind of took it lackadaisical today because I was kind of trying to get ready for not having a guest and forgot to get ready. So, what we're doing right now, we have the show, new feature, the homepage is all brand-new. Like, this is all brand-new for 2021. I wanted to rebuild the Learn With Jason site. This is a new site. It's built in Toast. Let me refresh this because, yeah, all that was missing. The homepage now shows a little more information about the site, and it shows when I'm live, or it shows the next episode. So, you can now see live captions right on the homepage of learnwithJason.dev. That's being provided by Jordan. Thank you so much. Jordan is from White Coat Captioning and they're able to provide captions for us because of the generous sponsorships, all of whom kicking in to make this somehow accessible to more people, which means a lot to me.
So, what we are going to be able to do here is, like, all of the things that are happening in this site, like, the fact that this site is aware that I'm live, and the overlay that you're seeing is aware when somebody says something, and if I run, like, if I want to run a command here, I can run the ahem command, and what happens is my overlay gets some events. So, I can show you what that looks like if I open up the right thing. So, let's go to -- here are my scenes. Let's open them. And this repo is open source. You can all check it out if you want. It's gonna open up in the wrong window, isn't it? Sure it. Beach ball of death over here. Come on. I don't do enough solo streams. I don't know how to built time. That's what we needed, thank you. Okay, so, here's the repo, if you want to check it out. This is how I run the show. And, the way that the show works is it -- take a look in the components here.
Actually, I'm gonna open this in code so that it's a little easier for me to navigate. So, we've got the components here, and each of these is its own thing. So, we've got, like, the lower third is a Redact component, the logo, the details and the chat. So, the chat is a component that is running this socket studio I built. It's a set of hooks. It's on NPM. It's not very tested. I still have it in, like, a canary release. So far, it only works for me. You're welcome to try it if you want, though. I think it will work. I don't know. We'll find out. So, we use Twitch chat, and then down here I'm able to load the chat, and we just map over it to display our different chat messages. And we also use rehype sanitize, thank you to Chris for pointing that out. And what this does is this will make sure that only image tags and -- I guess I allowed the marquee tags because that made me laugh. You can only do an image tag, only have a source and an alt. That's all. So, that's only to allow the -- that will only allow the emotes, but I guess you could potential try to post a different kind of image. Don't -- I mean, I guess you can't. Don't do that. It's gonna be messy. I'll figure it out eventually, like, lock it down to just the Twitch domain or something. But then each of those gets pulled in and we show the user name and all that kind of stuff. There's the marquee. Got it going.
So, permissible chaos. Excellent. I am a big fan. So, now that we've got -- now that we've got our chat, then we also have support for a few other things, like the beard game, if you want to run !grow, shave or flap, you will see it work. You putting dogs in there? You see in the lower left-hand corner that this little CSS animated Jason that Cassidy Williams made is now kind of up and interacting. This is all done, again, using web technology. This is coming from a GraphQL subscription. And so I can -- like, let's see if I can -- let's see if I can remember the live URL here. But this is the underlying of Socket Studio. It used to be Called Stream Blitz. I had to rebrand it because somebody already owned Stream Blitz. So I had to fix that. What I can do is set up a subscription for my own channel here, and what will happen inside of it, if I look in here and look at, like, the message and the author, username and roles, and then we can also look at, like, I don't know, the emotes or something. Emotes of type must have -- oh, let's skip emotes. We don't need all that information.
So, let's just run this. I'm going to clear this so that we can see the whole thing. One issue that I have with the GraphQL playground is that I can't scroll. So, if somebody makes a subscription, look at that, eh. So now whenever someone makes a comment, it shows up in the list, right? And so then -- if I stop this, I'm gonna refresh, and let's start it again. I'm gonna run a command. And so then you can see that we get, like, here's the command. Here's who ran it. And if I look at, like, I have the ability to say on Twitch -- >> Ahem. >> Command. And then over here I can find out what the command is and what the handler is, and all of my -- all of my Twitch commands are actually serverless functions.
So, if we do this again and we say, like, I don't know, oofda. >> Oofda. All right. That's exciting. >> Whenever that happens, what's getting sent to my overlays is my author name, the username, roles, right? Then if I collapse that, I can see the oofda command was run. The handler includes this message that I want sent to chat. This audio that I want to play and this image that I want to show. And all of that is, you know, you can see I'm using Cloudinary for that. That makes my life easier. I don't have to host that. What this looks like under the hood. Let's go back to the source code here. When I go into effect, the way that I'm able to use this is I can -- let's see, am I passing this in? Yeah, this is right. So, down here I have a use sound effect, right?
So, use sound effect is -- this is a whole state machine. There's a whole episode to have on this where we can talk about how this works, but I'm actually pretty proud of this. Because this is a state machine and it operates as a queue. So if y'all want to just everybody quickly queue a sound effect. This is going to be chaos. Everybody queue a different sound effect right now and we're gonna see how this works. Are you doing it? Come on, y'all. Okay, here we go. I see two, right? So here's the please hold. Here's a few more. Watch, so what's happening right now is X state has put us into the playing -- now, it finished. So it runs ahem. Somebody else ran it. Now it starts playing ahem. I need to do some cleanup to dedupe this stuff. Isn't this fun? It's got the ability to, like, completely handle queueing. Any time that it's running, it has this handle queue helper that will put the command into the queue. So it runs -- so, it goes from idle, it loads the command assets. It starts. It's active. Then when it's stopping, it goes into checking for queued commands, and that just says if there's another command in the queue -- I don't know how many we scheduled, but this is gonna be a nightmare. But, so, now it goes into the queue. >> Oofda. All right. So, that's exciting. >> I think that's it. That's the end. No, we've got one more. All right. So, when it gets to the end, it looks at the content queue length, and if there's a queue, it jumps back to the next one, otherwise it jumps back to idle and waits for somebody to run a command.
Wait, y'all can't see my code? Is this -- wait, John, what are you saying? Am I missing something? Okay. Okay. Yeah, you can see the code. Wait, hold on, am I losing my mind over here? Okay. Oh, because of the sound effects. [ Laughter ] Sorry. I need to make the sound effects smaller or do something. Okay, we can -- how about we pull this over a little bit, and I will also collapse that so that we can see a little bit better.
Okay. So, now we've got -- we've got this whole queue that we're using X state. I pass in the action so that we can do all of that. So, that being said, what happens down here is I, again, am using this useTwitchchat, and I just look for the current command. And whenever I get a current command, I pass that into my machine. And you can see here that I'm just sending an add to queue event. When I add it to queue, it looks for where it is. It jumps out of idle into playing or puts that command into the queue to play after the next command is played. And all of that feeds this effects -- this here. So I get a state. And I can look to see what's going on. I can see that I've got my idle state, and then I use my effect display to show the effect on top of the screen. And this is, you know, this is straightforward as I can make it. I'm using splitting -- I was arguing with it in React, so I'm doing this stringified thing where splitting will give me a string back as opposed to my -- as opposed to, like, splitting this down here, but that lets me set the caption like, you know, who, Prince played whatever.
Yowdayum. I love that username forces you to have an accent. Oh, my God, that was so amazing. Yeah, so that -- I forgot what I was saying. Oh, yeah, yowdayum asked, is this React, yes, it's React. My goal is I wanted to use Toast for this. I wanted to write plain ES modules. It doesn't use Webpack, it doesn't use Babel. It goes straight from my code to the browser. That is definitely, like, in my opinion, awesome. It also results in really, really fast build times, like my scenes built on Netlify from the time I push my code to the time it's live is, like, 30 seconds. It's super, super fast. It's really funny.
So, you need to build something basically because in this case I'm not really building anything. It's more of, like, I have to -- I'm using some node modules and not all node modules are ES modules. So, I have to do a step where toast grabs the node modules and puts them into web modules and makes sure everything is wired up the way it needs to be. I'm not using a lot of the static site generator features in this site. I've used a ton of them in learnwithJason.dev. You can check that source code on your own. The reason I'm going through this, I want to build this subscriber overlay. This is MP4. It needs to be chroma key. If I turn off my screen screen, you can see that this is what the video looks like on its own without applying an chroma key to it. That means I can't make this video part of my overlays, unless I'm going to try to figure out something as he's saying, I'm not going to write my own web GL filter. I'm using Streamlabs OBS. They've got that built in to make it happen. So, what I'm looking at doing here is how do I still use web technology to trigger this, but then allow OBS to handle the, you know, the chromakey part of it. I'm going to make a small window, like, a page that has nothing on it except this video, and then I'm gonna use a browser source to drop that in. So, the way that I want to do that is, let's start by adding a page. So, I'm gonna go in here.
Yowdayum, you're asking, how would I recommend learning React? The way that I learned React is by reverse engineering other people's projects. I kind of dug in. We're gonna build some stuff from scratch today, so you'll get a sense of kind of what it looks like to build a page. I'm going to do, as much as I can, some pretty simple stuff here. I'm gonna try to keep this as uncomplicated as possible.
So, let's call this "subscriber overlay." And in this, I'm using Preact, so I'm going to import h from preact. You don't have to do in the latest versions, but it's still required for preact. Then I'm going to export a function called subscriberoverlay, and this is just the way I like to do it. I like to export a function so that it's named and have it match the name of the file. The file is kebab case. What is this, Pascal case? And then I'm going to return -- let's just return for now a div, and in that div, we're going to say subscriber overlay. The reason for doing that, what I want to be able to do here is prove that it functions before we do anything else. So, the way that I'm gonna do that is let's -- this is just gonna generate a page, right? So if I go out here and I do an Netlify build, this is gonna build my site, and you can see there, 2.8 seconds. And then out in my public folder, I get the subscriber overlay. And if we look at the subscriber overlay, it brings in a little bit of toast stuff, and then -- uh-oh, that one didn't work.
Why didn't that work hi -- I screwed up. We can't name the exports it it has to be the default exports.
So, let's try that one more time. Tim, could I not use a video format with a transparent background? Well, I could, except this video I shot against a green screen. So, it's not transparent. I could -- I don't know if I could, like -- could I just use transparency in the MP4 mile directly rather than including the green screen? I mean, I don't know. Should we look that up? Let's find out. So, let's look at this page before we do that. So, here's -- here's my subscriber overlay, and we can see that it's, like, pulling up the div and it has all this content here. If we look up here, it's got the generated HTML. So, this is kind of what a toast page looks like when it's generated. If we load this in the browser, which I can do with Netlify Dev, this will. I'm using Netlify Dev because I want to load my environment variables, and that just does it for me, so I don't have to do anything else.
So, what I'm gonna do here is copy, and let's head out. Chrome is killing me. I'm gonna have to switch browsers. It's getting to be such a memory hog. Here we go. So, let's head over here. All right. So, now if I go to subscriber-overlay, I get my H1 that says subscriber overlay. So, I do kind of want to look and see if there is a way to do a video with -- yeah, let's take a look to see if -- let's see if we can do the video as transparent. Because that would be pretty cool if it just works.
Posted a link. Let's grab that. Going to copy that over here. And so this is -- okay, so, this is pixie chroma filter. I don't know anything about this. So, let's take a look at what it does. Oh, cool, you built this.
So, my webcam is not part of the overlay. I've been looking into that. Right now, the way that I do it is that I run my web -- my dslr directly into the camera, and then put it into a transparent area of the scenes. But I don't actually care that much. Like, I could -- I have some ideas that are really silly with, like, live facial recognition, where I would be able to let y'all put hats on me or something. But I have to just -- I'll get there. Eventually, I'll get there. So I've got a million silly idea that I'd love to dig into. But so this, then, theoretically speaking, would let us -- where do I even run this? So this is kind of the part where I'm not 100% sure what I'm doing.
So, pixie. Chroma filter. So, what you're saying is that if I do this, what's it looking for, though,? Like, how do I actually apply the chroma filter if it doesn't take arguments or is it just pixie.filter? Hmm. I feel like this is something that I probably want to look into either with an expert or when it's not gonna be a lot of me Google goofily. I would spend so much time ramping up on pixie JS that I probably won't. I don't know that that's gonna make for very interesting anything. But this does make me want to talk to the pixie JS team or pixie JS pro and see if we can do something on the show because this is the third or fourth time I've heard it brought up, and I've never used it, so, it could be a lot of fun. Hey, if that's you -- if you're my pixie JS pro, slide into my DMs.
So, let's take a look at -- but, yeah, so let's do this the hard way here. And, so the way that I'm gonna do this is right now my overlay is living in DropBox. So, let's go look at my DropBox. This is going to be horrifying. Let me actually open a specific folder of the DropBox so it's not -- okay, here's my specific folder of the DropBox. If y'all want to see just some super cringe, yeah, let's do it. Let's watch this together. This was me trying to make this thing happen. So, that's my green screen. This is me attempting -- also, I had to throw all of these away because this shirt has green on it. And I had a hole in my chest when I actually tried to make this work. Yeah, look at this one. This is so bad. I was like, what if I came out and I, like, set the -- set the boop free? These are so terrible. They're also, like, out of focus. Yep, awkward wave. I don't know, I can't watch more of that. So, this took me way too many tries. As you can see, I've got, like, four different takes of me in here trying to make that work.
But, so, anyways, what I want to do with the overlay here, goofy lower third, that's what it's called. And I think this is the one. That's the one. Okay. So, this is what's happening in the video, right? That's the green screen part. And then I used -- I used Adobe After Effects. No, that's the wrong one. The boops actually got picked up by the screen screen. So, they had holes in them and stuff. So, I switched over to this heart instead. And the heart works. And this is the actual video. This is what I created in Adobe after Effects, and every time I use Adobe After Effects I feel like I'm learning After Effects very much for the very first time.
So, yeah, my hope is that we can take this video, and I think what I'm gonna do is just drop it right into Cloudinary. So, I'm going to go to my Cloudinary account. And let's see if I'm still logged in. I'm not. Let's see how long it takes me to log into Cloudinary. Pull this off screen. All right. All right, Cloudinary. And then we got to do the one-time code. I love that that's built into one password now. It's so nice. Okay, so I'm in, I'm in my media library, and we're gonna jump into the Learn With Jason folder. All right.
So, I'm just gonna go ahead and upload this straight up. This is a pretty large video, so I think I need to optimize it a little bit before we set this loose on to the internet, but fortunately I can do that with Cloudinary. So, I'm gonna name this subscription overlay, and then come back here, close this down, and let's copy it. I'm gonna come out here and paste. Okay, so what I want to do is I just want to make this a little bit more manageable. So, the size that we're displaying this at right now, let's look, is -- what was the size? It is transform -- I don't think any of my overlays show. I've got it at, like, 573. So, let's resize this down a little bit. Let's go to width 600, set the quality and format to auto. What that's gonna do -- actually, before I do this, I'm going to save this. I'm going to open my network tab, and let's reload the original one that doesn't have any of that stuff, and let's look at this network tab. That's not the one. Let's try that again. Why isn't it showing the actual video? It's weird that it thinks that it's -- okay, let's look at the actual headers then.
The response headers say that the size of this video is 6.1-ish mega -- we can see in the response headers that this is only now 55 kilobytes Am I looking right past it? Save data. Do you not have my size? It's doing like a -- let's try that again. It's saying 246 transferred. Because it's sending, it might be sending it as streaming and not giving me the size, but I don't know enough about video to know if that's actually the case.
Oh, content length. Here it is. So it's down to 240 kilobytes as opposed to the 6 megabytes that it was before. That's going to save us a ton of bandwidth. So, then in here, what I want to do is I am actually just going to show this video and let's do a -- actually, I think -- can I get away with just doing source in here? Let's find out. Accessibility experts, what's the right way to label this? Do I put it inside as like a video description? Or is there a tag -- you don't put alt text on a video, do you? Title, maybe. Who knows.
Somebody tell me. I'm going to look it up if nobody tells me. But, this will give us is we have a -- our video in here with the source. And if I say -- let's run this again. That's true, Robert. Nobody's actually gonna look at this, but that doesn't mean that we shouldn't do it, right? We still should want to -- that should be our habit, is to always do these things the right way.
See, the text inside should be fine, says Prince. You can do label, title or figure fig caption. I think title feels right to me. So, let's do a title. We'll drop that down and say tiny Jason wanders out on screen and is delighted. Is that the right word? Delighted by lots of floating hearts. And I'm going to leave this empty. Okay. So, now what we've got is we're showing a little video. Cloudinary has video -- no, what? That's gonna be amazing. O
h, that would be so much easier than all of this stuff I was gonna do. Functionality works like a green screen effect, but can be applied to any hex color, but will it work to make, like, everything back transparent? Make a color in your video overlay, apply the make transparent effect and color parameter. Okay. The make transparent parameter supported for videos only with an overlay component. You can't apply this parameter to a video without an overlay. Like, I could screen screen inside of the video, but I couldn't put, like, the -- I couldn't put what's underneath in this bottom corner down here.
Like, that part wouldn't be visible. Can you even see my mouse over here? Is it showing. Let me do this. I can't make this part visible under here because the video isn't aware of it when it's doing that transparency, and it's not, as far as I can tell, providing actual transparency. I know that there are video formats that handle it, I just don't think -- hmm, yeah, I don't think it's gonna do what we need. Chris shared the link to which now? So, yeah, Kesor, I see what your post -- let's take a look at this, though, because I want to see if this will do what we want. Res.cloudinary.com. This is the demo. So, there's a couple of things happening here.
So, we've got a video overlay happening with the blue screen watches and we apply the make transparent effect and what color, and then there's another video behind it, the sunset waves. So this, unfortunately, I believe means that we're not able to use this feature. So, instead, yeah, Webm supports the alpha channel, which would probably work for me. I don't know. But, yeah, I think -- I don't, yeah, basically here's how I'm feeling about this. I know for sure there's a better way to do what I'm about to do. What I don't know is enough to do it inside of the time constraints that we have. Because I want to get this done inside the next 50 minutes.
So, I do know that I can get this web page to work the way that I want, and that would allow me to use the OBS tools. So I think, yeah, what Bobby Table said. I can ship it and then I can fix it later. And I would very much appreciate if anybody does know of a better way. Like, open an issue on my scenes repo and let me know. Not only would I very much appreciate it, but we'll also make sure it all gets credited and all that stuff. We're going to make sure to shout it from the rooftops because anything that makes my life easier and helps me learn stuff is a thing that I deeply appreciate. So, what I'm going to do then is in here, I'm going to take a look at how we're getting -- just going to basically copy/paste some stuff here. So, I'm going to get React helmet. I want a style sheet. The way I'm doing style sheets, is I'm dropping them straight in like so.
So we're going to do a link rel stylesheet and give it an href of -- where did I put the rest of these? Styles. And so we'll call this one styles/subscriber-overlay.css. That means I need to go make a subscriber overlay. So somewhere, out here, static. Here it is. I'm going to create a new file. Call it subscriber-overlay.css. Is the div redundant? Yeah, probably. I'm going to use it as an aspect ratio and that will allow me to set the div to a specific size and that will allow the video to fit. Probably something I don't need to do. Probably something I can get away with because I'm not going super cross-browser compatible. But here's the thing. My intention with all of this is to make this stuff available to people who aren't me. I'm trying to put together a streamer kit where you can design a website that acts as your whole streamer set and you'll only have to use some React hooks to get the events and, like, you can write some CSS to make it look the way you want. I want this to be really easy. It's really similar to stream Elements, but I want it to be completely web paced. Stream Elements, I feel like they give you withins and -- widgets and you have to stick the widgets on the page. My hope is as much as possible all be part of one website. If you want widgets, we're escaping to get special functionality, but it wouldn't be required, it wouldn't be necessary.
So, what I'm going to do is set up a wrapper. Let's give this a more specific name. Subscriber-overlay-wrapper, and we're gonna give this a -- it doesn't need a display block, does it? It just needs a position of relative. We'll give it a width of. I had it at 573. We'll give it an even number. Let's make it 600. Then I want the height to be whatever the video height is. Is it gonna show me over here, like, what the height is? Yeah, 336. Okay. So, give it a height of 336. And then the video is gonna be let's call it position: Absolute, and it's going to get a width of 100%. A height of 100% and a top: 0, left: 0. The fact that I haven't alphabetized these is bothering me. You know what? I'm committed to getting better in 2021. I'm not gonna worry about it. So, now we've got our styles here, and we're pulling that in. Which means that I need to give this a class. And here's something that I really like about preact, you don't have to write class name, you can just write "class." If we were in pure react, we would have to do it like this, but preact doesn't care. It handles it for you. I can do subscriber-overlay-wrapper.
So, now I am effectively writing HTML, which makes me so happy. Wait, that just works? Hold on. Oh, I got to, like, select it all. Here we go. Watch this. Watch this magic. Sort lines descending. Oh, wait, sort lines ascending. Wow, that's so much easier than what I've been doing. I'm always over here like, oh, let's do it like that. Okay, so, then -- see, you're an enabler. [ Laughter ] Okay. So, now I have -- I have the subscriber overlay set up, and what I'm doing with our styles here is actually, ooh, you know what I'm gonna do? Let's make this thing always 100% wide. So, I'm gonna grab my calculator real quick and we will say 336/600 is 56.
So that's our aspect ratio. We can do a very nice little trick here. I'm going to set the padding-bottom to be 56%, and then I'm gonna set the width to be 100%. So, what we've just done is made an aspect ratio container. The height is set to 0, because the height is relative to the height. Padding, whether it's right, left, up down is relative to the width of the container. So if I know that my container is 56% high of whatever the width is, then I can set the padding-bottom to 56% and this box will always be the same aspect ratio regardless of what the width of it is.
So, let's see if I did that right, and we can stop, start again. And now if I go out here, let's take a look and let's see what I've done. There we go. Okay. So, now we have a width, height all set. Now, we've got a couple of problems here. Note this isn't playing. So, I screwed that up. But I can fix that. That will remove the global margin so that this is not bordered like this. Then I'm going to set this thing to auto play. But in HTML in Chrome, you can't autoplay without muting.
So, I'm going to stop and restart. And, like, there's no sound in this video anyway, so it doesn't matter. Okay. So, now that we're reloaded, we've got this thing all set up. It does what we want it perfect, right? And this is exactly what I was after because I'm gonna resize this -- like, this overlay, I'm gonna resize it, but what I want is for the overlay to fit within whatever size I set it. So this is -- this is the thing that I'm after here. And it looks like it only plays once. That is actually perfectly fine with me. Can't autoplay inline without plays inline. Yeah, you can totally drops links, just don't spam links. Ooh, Andy linked to aspect ratio, which is something that I love, but as far as I know, doesn't work yet. Let's take a look.
So aspect-ratio is going to be dope once we get it, right? Because once you have aspect-ratio, we will be able to set -- oh, that's kind of cool. Wait, this is -- that's a media query. Isn't there a CSS attribute, too, for aspect-ratio where you'll just be able to set the aspect ratio of something? Yeah, this is very cool, but it's not quite gonna do what we want. But it is something to be aware of, so I will link to it in the show notes. But what we're going to do here is I'm going to -- let's take this and, you know, I think this might be enough for now. There's another step that I want to do, which is I'm going to pull in the events and show this overlay based on different events. But what I'm gonna do just to start is I want to get this thing showing up on the stream. So, let's publish it.
So, here we go. Get add. Get everything added. I'm going to get commit and we'll say feature add -- this is a work in progress.
Add subscriber overlay without caption. And then we can push. Okay. So, that -- let's open up -- open up Netlify, and we'll just watch this thing build real quick. Here's our scenes. Nicky got time zoned. I know, I know, I know. Kesor, yes, so that's the next step.
So, my first step is to get this in and make sure that the chroma key works and then I'm going to set it up to actually listen for that event and show/hide. I can't point my local host to OBS because I'm on two computers. The streaming happens on my PC, and the work that I'm doing is on my Mac.
So, I'm actually streaming my Dev work on my desktop. I could open up a tunnel and do a lot of things. This builds so fast. We're done in 40 seconds here. I don't feel the additional over-setup isn't worth it to me. I don't think you can see it. I'm going to set up a browser source in OBS, and I'm going to add source, create a new source, and we're gonna call this -- oh, get off my mic. Let's see, Socket Studio Subscription Overlay. And for the link on this, it's going to be -- I need to look this up. Let's see. Https://lwj-scenes-toast.netlify .app/subscriber-overlay/ And then I'm going to set this to be 336 pixels wide. Was that right? Okay. 336. And let's just drop this thing right on in here. We see it. So, I'm going to turn off this other one. That one should have moved. You're in the right place.
So we're going to stick this down and let's make it right up against the bottom. Okay, so, now I have this where I want it to be. It's not playing. That's a whole other thing I'm going to have to figure out, but let's see. Shut down. Use custom frame rate. All of that should be working. I don't know why it's not. Let's see if this chroma key filter works on it. I'm going to add a filter and add my green screen filter, which is in here somewhere. Okay, that looks right. So, let me move to a different screen so it's not quite so confusing.
So, this looks right-ish to me. I think we need the color reduction to go up or down a little bit. I wish it was playing so I could see. Let's call that -- so, next, we need to figure out why it's not playing, and I'm gonna assume it's not playing because something is not doing what I want and I don't know, maybe it doesn't play that type of video or something. Like, maybe I need to force it to be an mp4. Hmm, I don't know. That would require a loop bullion attribute. Oh, yeah. Okay, so, let's try the loop. Was the other one auto play inline? Is that a thing? Hmm, hmm, hmm, hmm, hmm. I don't know. That one doesn't seem to be -- plays inline. Okay. All right. Let's just ship that and see what happens. All right. So, now we're pushing.
Actually, while we're waiting for this to build, you can see -- go into this site here -- is this what we're gonna do. Go to the deploy details, here's my build. While this is building, we've got also a new episode up that aired yesterday. With Orta. We did Typescript I learned a lot. Something that's been communicated as being very dogmatic, but it was actually pretty painless to drop in and just use.
So, this is deployed. So let's reload it. There it goes. It's playing. Look at it go, everybody. All right, so that's a website.
Now we are playing our subscription from the website, and it's gonna loop now because we set that loop on it. And I'm actually okay with that. So, what I think I want to do here is now I want this to only play when someone subscribes. So, what I'm gonna do is I'm gonna get down to -- let's look at chat. And so the way that the chat works is that it uses this use Twitch chat, right? And then I also then am going to need this. So, let's copy both of those, head back into subscriber overlay and do one of those. Okay.
So, now that I have this, I want to use instead events. So, this is operated as a GraphQL subscription. The GraphQL subscription is something I built -- it just listens for TMI.JS which is a Twitch kind of event library and then converts that into a GraphQL subscription event. And I do some normalization on it and things so that it's a little easier for me to parse this information, but what we can do is let's do this. Let's go ahead and for now I'm gonna console.log events. And this is gonna be a little tricky because the only way that we can really test this is by having people subscribe, which is probably not super practical. Since, you know, while I would very much appreciate it if everybody wanted to go and get -- wanted to go and get a subscription, I also know that that's probably not the most practical thing in the world.
But let's do this. Let's run it locally. And maybe what we can do is we can, like, go find a super active gaming channel that got, like, 80,000 people on it and we can subscribe to that one instead as a way to get some details. So, here I've got my console. Is it logging? Here's our events. So, our events now are null. So, what I'm going to do here instead is let's go find someone on twitch who is super active. Going to browse, if I can spell it.
What is that? That's not what I -- directory is what I wanted.
So, let's go to live channels, high to low. Somebody right now, lots of people are going to be subscribing LVNDMARK. I would suspect. Let's drop this in. Let's see what's happening with the event handler. This is brand-new. I wrote this code, like, yesterday, maybe two days ago, and I am reasonably convinced that it works, but not positive. So, let's see. Is it doing anything? I also don't know how often anybody subscribes to, like, other things. I imagine that it would work, but, yeah, you know, that's probably the better -- I should have it say that. Just to make sure I'm not serving like a Cache version or something. Let's rebuild that. Get out here. Open this up. And hard refresh.
Okay, so we've got LVNDMARK's events. That theoretically should get us some information. You know what? I was using games done quick yesterday and it was actually more active. So, let's try games done quick instead and see what happens. I hope the problem is that it's just not active and not that, like, this thing is just actively broken. That would be a bummer.
Anything? Bueller? Let's drop into their channel. And it's muted. Look for a sub and then let's see if it shows up.
I watched a little bit of this yesterday. They were playing Hades. I played Hades for about an hour.
I'm a social gamer. I play Fortnite. I did learn to play Minecraft with Lindsay Levine.
Can I make it do it once? Can I Prime? How do I Prime subscribe? Am I -- man. All right. I'm going to have to remember to deal with this. Oh, God, I'm not entering my info. No, I'm not doing that. I thought it was already saved in here. Whatever. Get out of here.
Okay, so, this is not doing what I want. Gifted one sub, we should have an event and we don't, which means that it's not working. Gifted it to -- yeah, maybe the gifts don't show up the same way. That should still show up the same way, though. One sub. Maybe it's too early in the day for this sort of thing. Okay. So here's what we're gonna do.
I am going to -- let's switch this back over to me. Okay. So, let's do this. I'm going to close -- let's open it again, and I'm gonna do two things.
First, I'm going to open on Twitch event, right, I want the -- oops. Type and let's get the details, right? And then we've got the Twitch command and the Twitch chat and stuff. Let's leave those out for now. So, what I'm going to do is I'm going to start this. Let's reload so that the things clear. Okay. So that's tracing. And then I've got this one over here, which is streaming my events. And now I'm going to -- I think I'm actually logged in. The reason my details aren't in here is that I'm logged in as Socket Studio been, so let's have Socket Studio subscribe and see what happens.
So, bear with me while I enter the details. So, let's subscribe. I'm going to do all of that stuff. I'm going to enter in details. Is this going to work? Come on, do the thing. Do the thing. It's not gonna do the thing. Okay. Come on. Okay. This is -- come on. Why is everything so hard? One of these and one of these and one of those. And that will give me a purchase. Do the thing. I'm completing my purchase. Almost -- okay. All right. So, there's the sub. It should show up over here.
Oh, no. Oh, no. Oh, no. Oh, no. Oh, no, is it too -- is it too far down? No! Foiled. Let's see. We got to be able to get this. Oh, wait. Yeah, it didn't do the thing.
Okay, so something -- something's wrong, right? Like, something's not doing what I want it to do, which is not fun. All right. So, yeah, as someone suggested, let's switch it over, and instead of using events, we'll use currentcommand, and let's double check that so we can stop and restart. Okay. And then we can -- let's go out here. Reload. It says current command is undefined. Let's run this thing. Oh, my God. Okay.
You know why it's not working? It's not working because I didn't add the context provider.
Jeebus. Okay. Let's make this thing actually function.
So, this is React, right? You can't just, like, use a hook, you have to apply the thing -- the thing that does the stuff. This thing that does the other stuff. So, I'm going to -- I'm so upset with myself for not thinking of that.
All right. So, we're gonna drop this out here. We're going to drop this on the other side of it. And then we're gonna move all of this into a component that we'll just stick here for now. So we'll call this function video and return that, and then I need to move this part here to be inside of this component because what we need is if we're doing a context provider, the context needs to be outside of the component calling it. And that -- so it means that I need to get this. Right? So, let's get all of that. And we're gonna drop that in here. And then we need this create here. Okay. And then I don't think I need any of the other stuff. Nope, we're all good there, so let's go down and drop in client. Okay. So, now theoretically speaking, we'll at least get something, right?
So, let's try this again. I'm gonna refresh the page. Refresh the page. Nothing is happening. Did something break?
Show me potato salad.
Showing you nothing right now. What did I -- you want to know what I did, y'all? Ooh, boy. Just really -- just really giving in to the panic of live code, right? Come on. Okay. Look at it go. There we go. We're getting commands. All right.
So, this is great. This is what we were after. We're getting the command. It's showing us what's being done. It's bringing everybody in. So, this is good news. So, the other thing that we can try is I'm just going to put events in here. Let's still trigger on current command, but if anybody who is not subscribed has been thinking that, you know, this is exactly the kind of content I like. I like to watch somebody flail for an hour and a half on trying to write something that they definitely have built before. That is exactly the -- now is the time, right? Get those sub bucks ready. Let me restart this thing is let's go. So, now that we're in here, it should show our events array, we've got our current command. So, if anyone is to -- events array, yeah. So, if anybody -- the Learn With Jason telethon needs your help. We're only one subscription short of our goal.
Let's do this. I am going to.
No, Prince, I think my gift subs don't work, but thank you so much.
Okay, so, I need to figure out -- there's a lot that goes into the Twitch -- like, Twitch doesn't just have subscriptions. Twitch has -- this is interesting. It re-ups even when there's no -- that's fascinating. I didn't realize that chat caused it to re-render. So that means I probably need to be careful about the way that we do this. But, so the way that we're gonna do it is what I eventually need to do is go through and find all of the different IRC events because their subscription, gift subscription, re-sub, prime sub. There are a few other ones in there I need to make sure to listen for and make sure they show up properly. Twitch API does not make it as easy as I feel like it could be.
Yeah, there's a lot. There's just a lot. I would love if they just gave me the GraphQL subscription API so that I didn't have to write all this stuff myself, but we're not there yet. We can dream, right? But, yeah, yeah, yeah, why don't we go ahead. Ooh, did we get it? Did it show up? Did it work? Come on.
Man, I don't know what I did wrong. I broke something. Okay, either way. Let's play with this in the command.
So we'll be able to trigger it with, like, one of the commands. So, what I'm gonna do is I am going to set up -- and, by the way, sorry, I'm like, oh, it didn't work. Thank you so much for subscribing.
Prince, thank you for gift subbing. Thank you so much. I really do appreciate it.
I just realized I'm over here being a jerk wallowing in my own inability to not get events to work and not appreciating how much love y'all are showing for me.
So, what we will get is we get this current command. What I want to do is, if we look at a current command, a sense of one being defined. I'm going to run one real quick. So, here's my current command. Why didn't the sound play with that? Did I, like, goof something up? Whatever. Let's.
Ahem. >> Oh, there it goes. Now it's doing stuff.
And that's just not something I'm willing to do. >> Maybe it needed to load. I'm not sure what that was. But, so, what we're gonna get when this comes out is I basically just want to check on currentcommand.command to make sure that something has actually changed. This will eventually be overwritten. And what I'm gonna do is, let's pull import. Use effect from a preact/hooks, and I'm also gonna need state because I need to be able to see whether it's visible.
So let's start saying const visible, set visible, use state and default to false. For our use effect, what I'm going to do is have our function, and that function is going to look for current command command. If the command doesn't change, my todo here is use subscription events. So, what it will do in the future is it's going to check on whether or not the subscriber name changed so we don't spam events if I get caught in a loop somehow. But what we can do instead here is that when the command changes, we can say -- or actually, maybe we want to use the time because it's possible that in this scenario someone could fire the same command, but it would be a different event. So, let's use the time instead. And so what I'm gonna do here is I'm going to say "set visible true" and then we'll set a time-out. Let's say we're going to run this for -- I don't know, how long is this video? How long is the video?
Let's check. It is 11 seconds. Okay, let's run it for a full 11 seconds. Nah, let's call it 10 because it's probably going to end slightly before that and I don't want an awkward loop. And then we'll set visible to false. And I'm gonna want to probably clean this up to fade in and fade out, but for now I'm happy, right? Like, this will do enough to prove that what I want is happening. And then what we can do here is we can say "visible &" and what I meant by that was not that. There we go. There we go. Okay, so, let's give this a shot and see what happens. So, I'm going to run Netlify Dev. I'm going to -- what don't you like? What did I miss? I missed something. Not retime of un-defined. That makes sense. So, what I'm going to do instead is let's do const , like, command time equals currentcommand and current command.time or false. I would love to use nullish coalescing or the optional chain operator, but the overlays here use chromium imbedded framework, which does not support the optional chaining, which is kind of a bummer, but that's fine, no matter.
So, we'll set command time here, and then what I'm gonna do is in command time is false, I'm going to just return so that it doesn't do anything if there is no command set. So, that should solve that problem. Let's see. So, now if we reload, it does what we expect. Somebody see if they can trigger this. Run a command. All right. We got a hype train emote. Thank you. And thank you again, everybody. Oh, look at it go. Yes! So this means now that that should, should, should being the operative word work. Did you see how it did what I want? Look how freaking slow the overlays are? Like, that's kind of magical, actually. Yeah, so, anyways -- but what we can do -- you can see here it's not queueing and stuff. I have to think through that because one of the things that I've noticed that causes a ton of chaos on a stream is when somebody gifts ten subs and you've got a ten-second subscription animation and there are sound effects and stuff. You end up with a minute and a half of sound effects and chaos, right? And it can -- I've seen it derail, right?
And I think there is a way that it could be really fun, but I also think that there is, I don't know, who knows. But, yeah, so let's -- what can we do? Let's deploy it. Git commit. Overlay should trigger on commands. Let's push and then we can go look at this thing build. Oh, we do need to add code. Yeah, it is called chromium embedded framework. This is what's running under the hood in OBS. So, this is -- and I'm not 100% sure if it's like this one or this one. But what I found was when I built this, it worked in all the browsers, like, it would work in Safari, Edge, Firefox and Chrome. When I opened it in OBS, it would just fail. I had to figure out how to debug OBS. It turns out there is a flag you can add. There's a lot of content to create and write about this stuff because it's just a bunch. But, yeah, so, what this ends up being is it's based on Chromium. Is it a fork? Let's look. So, it lets you embed a browser in an app, which is why it's useful for OBS.
What I don't know is, like, I don't know if this is an official project. I think it's not. And, you know, it's open source, and so I think it lags a little bit on feature completeness. But, yeah, like, that, I think -- I think it worked. You done? Yeah, yeah, yeah, okay.
So, let's refresh this thing over here. I'm gonna refresh the cache. Okay. It is still there but it's not playing. Somebody run a command and we should see me walk out on to screen. Eh? Eh? Anyone? Bueller. There we go. Look at it. Look at it go. Yes! Okay, so, look at that. We've got it working.
Ahem. >>
I'm a little confused by, like, the audio clashing, and I don't know why. So, I'll have to look into that and figure out what happened there, but this is pretty slick, right? And so now if we all stop running commands, what should happen is this should clear, then. So, let's try it and see what happens.
So I think holybuckets was the last one. Please hold. It will happen again.
I got to get rid of that pog Chan. Once this one finishes. There it goes.
The subscription is now gone. We did it. We built an overlay that functions based on web events. So this is a website. It's an imbedded web page. It's using chroma key. If we turn off the chroma key, and I can do that real quick. Let's set off filter. Somebody run that for me one more time.
Holy buckets, did that just work? >>
So, if we run, like, one of these. Yeah, see, chroma key's off, so now this is slick. I am so excited about this because it just does what we need. It cooperates. It displays properly. We get that walk out effect. And, like, my inspiration for this. If you're not -- if this just looks like something that I made up, it's definitely not. The walking in from the bottom of the screen is something that I -- back when cable television was, like, a continuous streaming thing and you had prime time shows and stuff, they would always have a sitcom where during the sitcom they would have characters from other another sitcom walk out on the bottom. They would jostle and smile at each other and they would be like -- right? And it was this really goofy thing where every time I saw it, it made me cringe.
So I thought, naturally, for this stream we needed it. We had to have it.
And I've been trying to think of a fun way to do subscriptions for a long time, and this is the first one that I felt like I could actually execute. The other ones that I want to make will eventually exist, but they're a little more ambitious and will require a little more time. But, yeah, oof, I'm so excited we got this working and in the allotted time.
Everyone, what a great day.
I'm so glad that you all came to help me forget about my problems for a little while, to bury the stress down a little bit, and to everybody, let's -- actually, let's do a quick shout-out again to the sponsors. So, to -- thank you so much for sponsoring this show.
Thank you to Jordan from White Coat Captioning who has been diligently creating live captions throughout the entire show, and, remember, that's available in the video -- like, in the video playback as well if you go and look. All the transcripts are here. So, you can always get that transcript. It is available to you.
And, you know, it means a lot to me that companies are willing to kick in to make this show more accessible. So, make sure you go check out the schedule. We've got such a jam-packed January. It's going to be so, so so much fun. We are going to -- oh, yeah, you're still here. We're going to learn React on Tuesday, so if you want to come back, we're going to do an introduction with Ali Spiteal. She is amazing. Following that, we've got Scott Moss coming on to teach Next. If you haven't seen Scott Moss' content, he is a powerhouse. He's going to teach us how to do Next. So, after you learn React, that will give you the basics to figure out what Next adds on top of react. We're going to bring back Ben Hong, good friend of mine to help us learn Vue's Composition API. We're going to learn how to get hired in tech.
What a month. Everybody check that out. Mark your calendars. You can at any time learn the add with Jason schedule to Google calendar. It's not going to send your alerts or anything. It's just going to let you know that it's happening so that you don't miss an episode. That will help with time zone problems. Nicky got into trouble today because he thought it was an hour later. That's not what happened. That's now how it works.
So, anyway. I know things are weird right now. I know 2021 maybe didn't get off to the start that we hoped, but let's just keep our fingers crossed that this is the dark before the dawn, right? As always, thank you so much for hanging out, and we will see you next time.
Learn With Jason is made possible by our sponsors: