 Well we can do like this tap dance routine so we can synchronize the video to the audio. Oh we could try that. Three, two, one. Imagine that just snapped in half right now. We're on the last episode of The Batch. We're all very tired. There's been difficulties. We literally smashed a table in half. Right okay. Nice I can hear noise. Oh it's just tap dancing. Right. Right. Don't point at me. Every time we do one of these episodes there'll be someone in the comments who says wait a minute what are you guys using to advance the slides? Is that a joy con? It is. It is a joy con. And so I thought well why don't we do an episode on that? Why won't you talk about that? Yeah because like you know people are like why do you do that? And the reason is because I am a cheapskate. Yeah I am absolutely a cheapskate. The literally the first conference talk I ever gave I was like I'm not spending money on slide clicker that's money I'd rather spend on other things. And what have I got to do? Do you know I used I used a wireless mouse. Yeah sure. But do you know wireless mice like to be on a surface? They're not very ergonomic to hold in your hand. And also the thing at the bottom starts going crazy of like where is the floor? Only four people have lost eyesight in the audience this time. So everyone was like didn't know what you said. All we saw was like yeah. So then the next year I used a Wiimote and special piece of software to make all that work because it's a special piece of kit that works in a special way. Not this though. No I think I remember at the start we discovered this is Bluetooth it can just connect to your computer cool and then you still need a special piece of or we used a piece of software to map the buttons because it's a normal joy pad I guess. Yeah. Map it to keyboard buttons. Yes but you don't need to do that not on the web because we have the gamepad API. An API I both love and love. And we're going to talk about why and I'm going to have a look at the code that we actually use in this slide deck in order to make it work because I think there's a few interesting little techniques in there that we could talk about. I'm glad that you call that snippet interesting techniques. Summer I've rewritten it for the sake of this episode. It's not the original one we wrote but it'd be interesting to see if you can spot the differences because it's not that it's not that different. It's fundamentally the same. Anyway first this is the gamepad API. Yeah there you go. Navigator Give me them gamepads and it doesn't even need permission. Ah but your modern browsers won't show the gamepad unless the gamepad is used while you have the page open. So even if you've got 20 gamepads connected this is going to return not until you touch one of them. I'm trying to remember isn't it even a holy array? Yeah mate so the idea is that it should like the gamepads won't move in this array. Yeah they have a fixed order. They have basically a fixed ID but they won't be there as you said until you actually touch one of the buttons or the analog sticks. And I think this is a bad part of the API. I think we have better ways to give them an ID. Do you know what they have an ID but it's not unique. Which it's funny for ID which stands for identifier. It's an identifier for that brand of controller but if you have two of the same brand then anyway anyway so you do this. I've got over the fact of putting boolean in .find. It's a pattern now people for me I now look it's like all right you're getting the truthy values. Yeah I used to be against this because like what if they change boolean to take two arguments now this is going to. No they can't anymore because of this. They can't anymore because of this. So do you know what I've made my piece with with this and then yeah you get your axes and you get your buttons so your an axe. And my axe. So you're going to get two of those for a analog stick. Yeah because that's your x and your y. So you've got two analog sticks like your old PlayStation controller or your Xbox controller that's what you're going to get four. Yeah axes. I'll tell you remember I think the touchpad sometimes are exposed to the axes as well but not always. Oh there's a proposal for an extension to the spec which deals with the touchpad feature on I don't think Chrome supports it. I didn't look too much into that other than to know it exists. Here's a great site GamePadTester.com and this is me with a PlayStation controller. You can see me pressing our bunch of buttons testing the stakes. Now the buttons have a float attached to them and that's for your triggers. Which again are an axis. Yeah but it can it exposes them as buttons which is interesting. Oh interesting. But the button has a float value. Yeah the button has the state of all buttons do they're just sometimes one and sometimes zero but I guess these can be in between. Yeah yeah exactly. So the others are just going from zero to one. There is a pressed boolean. There is a touched boolean. So for some of your VR pads where just resting your finger on the button is a thing it can pick up so that is represented as well. Anyway. Yes this is what I was waiting for. Yeah so your way of actually making this work in code is you have to pull this stuff. Which is so counter to the web I feel like. The justification for it at the time was this is what games developers do. Like today we have a game loop. But you can adapt one to the other. Oh yeah. Not the other way around at least not without like if you want to adapt this API design to an event driven you have to pull and figure out the diffs. If you had an event driven API you could decide to put it in a global state object and just pull the state object. Yes exactly and oh you could just you know have both be available right. Yeah it's yeah I think this is a real mistake in the design of the system. It's not because like this doesn't give you a way I think to figure out what has changed from the previous joypad state. No you have to figure it out yourself. You would have to maintain that state. So it's like I gain nothing I feel like. Absolutely there is a draft spec for this where you have an event button down and you also have like gamepad or change like a change event or similar. It's sort of taking a lot of hints from the pointer API. Yeah but of course that isn't real yet. Is it implemented? It's not implemented yet. There's a draft spec for it. It might actually be behind the flag at this point by the time this video goes out. It's something we're working on. So even in that case so this would be the code like you've got your because you might have some gamepads connected at the start so you would listen to those and then you do you get this gamepad connected event which is an event that already exists that that isn't anything new and then you know you can start listening to that particular gamepad the various events on that but can't do that today. So should we take a look at the code that we're using our slide deck? Yeah. This is what I was aiming for. Is it slightly different to the code you had? Yeah. I wanted to make a system like this where you map buttons to actions. And this is you know where I wanted to go for this presentation so button zero is next and button three is previous just what happens to map to something friendly on here. Yeah. So let's do that then. Gonna have an async function. Which this was in your implementation as well. I like this. I like this model. Like you said knowing that something has changed is not information it gives you so that is something we need to track. There we go. Keeping. Got our array there for that. Better get one index for each button. No hold on to it. There's a difference from your implementation but this while true you had this in yours as well. Now this is a bit of code that would have terrified me in like before async functions. Yes. But I find myself using while true quite a lot. Yes. I'm quite a bit there's a lot of patterns I often find myself using it with timeout or other events where I just like I await an event in a for loop. Yes. Absolutely. And that's kind of what we're going to be doing here. I always I tend to start with while true now and then afterwards go would this be more readable as a for loop or as something else. Yeah. Because say what you want while true loop is very simple to understand. Yeah. Do you know like okay I'm going to keep going around in this thing and then you just wait for things in that loop and it's actually I think a very intuitive way. I think right the intent. Absolutely. We saw this before. This is we're going to get an array of game pads and we're going to get rid of those stupid gaps in the array. Yeah. With your filter. The spec says get game pads should return an array doesn't in Chrome take that much. So hopefully they fix that at some point. What we're going to do here is if that length is zero there are no game pads there are no game pads. So we're going to wait for one to be connected. Yeah. So this is something where I often have an await next event helper function which just takes a string for the event name and you just wait for the next event of this name. Yes. And it's I'm using once true here and this is something that I very rarely find once useful because normally I don't I don't so much want just one of those events. I want one that's specifically relevant to this item or whatever like I see this often with transition and for example we're like you actually want only one transition and event but you need to make sure it has the right event target before you register because of bubbling and such. Yeah. But that's not the case here. So once true actually rarely in a rare circumstance is great and the reason like if you miss off that once true you leak. Yeah. That's the problem. It's going to work but you're going to be leaking memory because it's actually not not a breaking leak like you just you're just consuming memory for no reason and it's just one closure. So maybe it adds up though especially in a while true. Absolutely. Yeah. Exactly. And then another awaits. And this is a case where you could just do new promise request animation frame. You could but again I feel like that is more likely that we change the arguments that get sent to something. I wouldn't trust it. Yeah. So this is basically saying like wait the next frame and this is actually as a side note I think quite interesting because this pattern can be iffy if you're actually talking about DOM manipulations or animations because it is still a micro task boundary. Yes. But it's still going to be before the next visual render. Yeah. But I think they can be ordering issues maybe. So I would with other frames maybe but it's all it's like well I like it. Yeah. Unless the browser's busted. It is it is spec to happen before the next frame because it will always do that micro task type micro task checkpoint inside the render steps as well. And now we're going to go through our game pads. We know that we've got more than one at least one at least one. Yes. True. At least one. And this is similar to the bit of code you had before. I'm going to go through the buttons and find out like whether they're pressed or not. Yeah. This one isn't a proper array. So we're fine. Turns out. Okay. Thank you. So now we've got a state which is an array of booleans. And again here in dot buttons every button has a fixed position in that array. Yes. Yes. On this specific game. And they don't disappear. Good. Yeah. Exactly. And we're going to see if a button has changed. So I've got a last state looking that up for this game pad. Right. So this is a slightly different version from the one you wrote in that because this will support multiplayer slides which we have done before. We have done that before. And then if we don't have a last state I'm just going to create a little false thing. An array of the same length but everything is false because you don't have a last state yet. Yeah. Okay. Yeah. You could probably use an empty array and rely on undefined but whatever. If you're writing typescripts this is maybe going to be happier about it. And then yeah we're just going to go through all of them buttons. I love dot entries. This is something I find myself using a lot. Yeah. It gives you an iterable where you get the index and the value and the value and you can destructure them like that. I like this. I use it a lot. Was the button pressed previously going into our last state. That's why we needed the index. There it is. And then there we go. Like is the button pressed and was it not pressed before? And this is another thing you would need to deal with with the game pad API is like if you hold the button down then you're going to get many frames where the button is pressed. That's the thing because you're pulling yourself every frame. You don't want to trigger next slide on every frame because it's actually quite hard to press the button for just one frame. So we would check this is basically a little threshold that you like. Yeah so it's just like we're essentially we're recreating button down is the event we're creating there. And then yeah going into our listeners one for that button. Look at you with optional chaining. Yeah I did actually have a separate if statement for this and then I went hang on hang on. Nice. We've got modern JavaScript so that's saying like if that exists call it if it doesn't exist. Doesn't matter. Don't do it. Don't error either. And then we save the state. And that's it. And what I like about this code is that if I put this on the page it's going to be doing that thing every frame which is kind of expensive but that only happens once there's a gamepad connected. Yeah. Which is nice because it's just going to wait at that you know waiting for a gamepad to connect. Yeah if you don't have a gamepad to plug in at all that that while true loop every frame will just stop running because it will wait for a gamepad connected which will never happen. Yes and similarly if you disconnect all your gamepads it will then you know that array of gamepads is going to come down back to zero and it will pause the RAF loop. And I guess what we're saying is the joycon is just a gamepad it literally shows up as a normal gamepad. Yeah and you can use a PlayStation controller Xbox controller you know PS4, PS5 like that that was the change from like the bad old days like the Wiimote was a special thing that the browser didn't understand that the operating system didn't understand you needed to get special drivers for it. And I don't know if you have a slide for this. No this is the last slide. I also I don't know how but I made it rumble. Oh yes so that is only in Chrome. Oh interesting. There's a kind of extension to the spec which is in Chrome as a way of like you can iterate over the haptic features of the gamepads and you can tell them to do their haptic thing like you can do that on this and that works you can do it on the PlayStation that works as well. And do you know if we have because these also have gyroscopes and accelerometers in there and those extension as well? There is a spec for it. Oh cool in the same extension spec I think that the rumble stuff is in it has yeah the access to the gyroscope access to touch pad style stuff I don't think that has an implementation yet but they are. But in specs that's good. Yeah yeah it's written down and along with this like new event system which would save us all of this code. That would really level up I think game development on the web. So yeah if you want to impress people so sometimes like it's great when when you put so much effort into an episode and like you finally get the first comment on the video and it's like oh I wonder if they've noticed the amazing animations I've done and the comment is joycon how you're like so how there you go we don't need to talk about it anymore again. Let's not cut but that was great Can we get some thumbnails on this one? Yeah We are out of here. All right let's go.