Build a game played on 2+ devices in 4 hours · Web Dev Challenge S2.E2

Build a game played on 2+ devices in 4 hours · Web Dev Challenge S2.E2

Show Video

What would you create if you had 30 minutes to plan and 4 hours to build? Today, we challenge three teams of two developers to build an app that's a game you have to play on at least two devices. Looking over your shoulder like I understand Go. I'm staring at my screen like I understand temporal. How's the front? I'm looking. I got I got the the clicks are not working. It

just stopped working. Welcome to the webdev challenge. Welcome everyone. Today we have brought you all together to take on the webdev challenge. And the challenge that you are going to be taking on is to build a game that requires at least two devices to play. Uh and to do that you're going to be building with temporal. And to

talk a little bit about what temporal is, we've got our friend Alex here. Now, I'm Alex Garnett. Uh, I am a developer advocate at Temporal, and I get to do cool events like this sometimes. Temporal is what's called a durable execution platform. It lets you spend all of your time coding for what you actually want to code and not on edge cases, infrastructure failures, and things like that. It makes your software fault tolerant by default and gets you state tracking, error handling, and graceful roll back and retries from whatever unexpected states you might get into. So you don't crash when you don't

want to, and you can always retry in place. It models your code as a series of events and it transitions between those events as needed. If you hit something you don't expect, you can always go backwards or back to the start. And if an external service goes

down, it can just retry in place indefinitely. That way you don't have to worry about outages, downtime, or anything like that. We've got SDKs for every language you could think of. Python, Go, Java, Ruby, Net, PHP, and for today, TypeScript. It's not a game engine, but I think it's a pretty nice everything engine. So, good luck, have fun, and thanks for having us. All

right, so from here, we're going to move into the planning phase. You're going to have 30 minutes to plan your apps. Your 30 minutes is going to start right now. Let's get it on. [Music] So, will I need to keep track of I just jumped and you're going to send the jump back to me? I in a way that would be easier so that I would track that. I just feel a little

laggy, right? Because like it's going to do a full round trip and then your guy jumps. I'll jump immediately. I'll do an optimistic interaction. My name is Adam Margal. I'm Lane Wagner. We have a game called Flappy Face. We're going to be

using a local machine learning model to look at your eyebrows and your jaw or whatever else is happening and use those to trigger jumping. We're hoping that it makes for some good TV as people are playing our game going or you can smile if you want to be happy and play. You know, it's going to be freaky. We're

going to be using vanilla go on the back end besides of course the temporal SDK and then vanillajs on the front end with some no build. Yeah, no build. We're eliminating all of the potential errors and just going straight for what we're both really good at. Yeah, CSS and Go. That's what we're That's what we're doing. I'm feeling 100% confident then and 110% confident in Adam. I I guess there's a lot I'm worried about, but I'm still feeling good. I'm I'm pretty good at Go. Been writing a lot of

Go. not very good at anything else. So, that's going to be unfortunate. We're just going to get stuck on mundane stuff. You just don't even know. So, you could have this great plan, but who knows? You The idea itself is one of the funnier things I've heard in a long time. So, I'm really excited to see just kind of what they do with that. It's

already funny. I think they'll be able to send something back and forth from a server. So, like out of the gate, I'm already chuckling, right? Because like it's going to do a full round trip.

What's probably going to happen, I don't want to tell you how to do a challenge, but you're going to have workers deployed server side that are running the server side part you wrote as workflows and activities, and you're going to have clients that are actually going to then send signals back and forth to those works. And the clients are going to be represented by each individual player probably. I have no right. So each player gets a client maybe. I think that's probably the

easiest way to start. I'm Nikki. I'm Sarah. Ideally, one player will steal while the other player guesses what that player stole and then they switch roles. So, we're going to be using uh our tech stack is going to be next, view, tailwind. Um, that's Yeah, that's really

And temporal. And temporal. I feel very confident on the view and next side of things on the temporal side. I'm still a little iffy, but that's what I have Nikki for. She's my uh she is the backend person. We're going to be building the backend in Typescript, which is based on the serverless uh features for Next, which is a viewbased framework. Nikki and I have never built

something together before. We've known each other since we were teenagers, and this is our first time working on a project together. And I'll come back around. Uh but um that should be a great

starting point. Okay. Winner check. Oh, yeah. So, all of these will always return the current game context. So if you make a move, you add a piece, the latest game state, and then the front end just reacts to the data. Tells me if it's if there's a winner, if there's not a winner, who the winner is. My name is Nick Taylor. I'm Shashi Lo. We're going

to be building Connect 4, but kind of putting a bit of twist on it. On the front end, we're going to use React with TypeScript, and then we're going to spin up a storybook uh application as well to document our components. And on the back end, it's going to be a Node.js back end and we're going to be using a websocket server and that's going to wire up to the front end with a custom React hook that will be listening to the websocket server. I think something we're going to

struggle with this time. We have like some stretch goals. It's pretty clear to me what we need to do. So, I'm feeling pretty good about the plan. They

mentioned that it was it was like connect for plus and so I'm curious to see what they had in mind. Designing game mechanics is really hard. Uh so you know it's always kind of fun to see how somebody messes with the rules of something that we all know pretty well.

Okay. Yeah. So I I can manage that in the in the server state then and then after that like writing this is a CSS but like so like for example say like um we start the game. So starting a game is starting the workflow. We add the two players and then the workflow like it's going to check is the game over? No. So like that while loop won't go indefinitely. It's just going to run the

workflow and stop. Yep. So one of the cool things about temporal is your workflows aren't like blocked ever. They're not just executing in a loop.

They're waiting until they have some more logic to run and then a worker will actually pick up the task and run it. Okay. So it just kind of freezes the state. So once there's nine guesses inside of their board, they're done. Yeah, they're done. But if they're if

we're doing it the opposite, it would be the same exact same. Okay, it that's why I'm saying like both players would have the exact same thing of it. So here's what we got. We got a temporal and go server interacting here. So the client

and the Yeah, that makes total sense to me. Okay, cool. So that you're actually using Go to send SDK functions back to the temporal server. Yeah. So let's imagine we people jumped 10 times. Um

can we replay the 10 jumps? It's a stretch goal for us to just to hit a button and have the whole game replay. Oh yeah, that's you can totally do that like that. You'll be amazed that once you get to that point how straightforward that is kind of that'll be like like at the end you just like you know turn one more variable and you just get that for free. Gotcha. So if we

have a workflow that waits for connections begins a pool waits for a pool of connections um begins what part is the workflow and what part is the go server supposed to be responsible in that kind of model. So your SDK is basically a temp like is that's a temporal client in effect, right? So that client can send signals or updates or queries. That's uh that's planning time. More pencils down immediately. Everybody stop planning. Son of feeling you feeling confident.

How you feel? Temporal is the mystery, right? But we've got we've got we got Alex our very own Scooby-Doo over here. Alex mystery. everybody walking into a concept that's unfamiliar. I think nobody here has worked with Tim Pearl before. I'm a little concerned about how how much time people will lose to getting their heads around how this sort of programming works. I'm also really excited to see

once they do get that mental model to click, how far they're going to be able to go using this kind of workflow builder. You've got 4 hours to build your apps and that time starts right now. [Music] What's the differentiator between like activities and workflow.ts files? Activities are usually what you put uh code that can fail in, right? So if you have an external service call that can go on an activity because activities are allowed to be retrieded automatically. Okay? And that's where you get a lot of the benefits of temporal is through those retry policies for your activities. Okay, we we generally say

that uh workflow code has to be deterministic whereas activities have to be item potent. Yeah, I'm just trying to figure out the uh temporal stuff. I'm pretty much uh I got the workflow, got the activity going. I'm trying to figure out the relationship between uh this running server instance and the activities themselves, but I'm real close. Sick. The simplest thing we typically use a client for in our examples is to start a work. Okay, so I need to have a

workflow. And so that's why a lot of our examples are like you have like a worker and then a starter cuz like all the client has to do is just kick off a workflow. Okay, workflow is going to do one thing and exit. So you can just see our simplest clients just say start the workflow and then return basically. Okay, got it.

You don't need an external database for most the things you're going to be using for temporal. So you can keep all your variables in your workflow and those are persisted back to the temporal server which in a real life production deployment is backed by Cassandra or something. And so the idea is you can keep all your data in that workflow. Oh so the the optional params to activity and workflow well maybe not workflow but activity like to create an activity and and the values you return from the activity those are defined by you. Yeah.

Yeah, those are just stored values on the workflow side. I'm looking for the ones that are the most clear. So like a smile. Okay. Yeah. So eyebrow like eyebrow waggle. See how it goes over the.5 threshold. So that's

what I'm looking for. Jaw open does it too. Very clear signal. Mhm. I look is not going to be good. Pucker. Oh, you can duck face it. Oh my god.

Pucker works. Oh, that's amazing. Smile looks like it's pretty good, too. Smile's pretty good. Well, and are we

looking at the eyebrows or the mouth shape? It's the mouth, right? The mouth. I think it might be mouth stuff cuz your eye smile mouth smile left or right. Smile is good. Yeah. This will be my quest next is to filter out all of these signals and just trigger jumps with any of them. I love it. I'm so excited for

the kissy face. That's Oh my goodness, dude. Mouth pucker is hilarious. I can't believe it.

I'm so glad we scrolled the list more. inside of your style sheet. Do you have a targeting link? Uh, add one to your style sheet. I

already did that. Oh, okay. Never mind. Now, what's my next idea? I'm out of ideas now. Sorry. Thanks. Bye. I thought for a second this was your code. I was like, damn, dude. You

got so much done. I wish. I'm just kidding [Music] there. I'm looking over your shoulder like I understand. Go. I'm staring at my screen like I

understand temporal. So, I'm working on the back end part. So, I'm working with temporal. So I'm able to start a new workflow here. So like I have a websocket server running and I see you're using the web UI for introspection as well which is cool. So you can see basically like the log of the signals that were all sent to the running workflow. If you expand one of

those, can you check out the payload? Like is that containing any parameters? Cool. Nice. Okay. So you can see it sent like a UID and a player name. Yeah. And that's what I sent uh from here. So So that's definitely working. Yep. It's a

great start. How's it going, Go team? Pretty much off the races. I think I've got the last of the confusing parts figured out. Nice. Okay, so you've got your front end running via websockets or Yep. Just a just vanilla JavaScript connected through the websocket API. That makes

sense. Nice work. So, I've got a working face tracking and mostly working game to make the level get harder. It starts out with a slow easing and ends with a really fast Where's the face part come in? Well, technically right now you should be using your face to jump. But is when I turned that on, it got all shitty and janky. I don't know if I can even do the vision thing right now. It is literally dropping the game down to like four frames per second, which is completely unusable for a game that needs to be running.

I don't know anything about what your game is going to be like at this point, do I? I don't think you've told me. She can explain it better. Can she? Yeah. just you're going to try to steal stuff from your other from the other player and then guess what they stole off of your board. That's really neat. That's actually a really cool multiplayer game. Okay, so it's designed for two people, right? And you just have to send a snake back and forth whenever they pick one of them from each other and then guess what they stole on yours. That seems can I say incredibly well scoped? Just as a as a general question like you're So you're doing a story book which feels like a lot of setup for something that you don't have a lot of time to build. With Storybook, we're

just default doing the default documentation. So, it's super fast. But the reasoning is so that we can build the combination of components on the screen and then test it out too because as he's building the back end, I'm going to build now the fullfledged page. Yes. So, you're kind of treating it like a almost like a sandbox environment. Correct. Yeah. Yeah. Because then now as

I build this onto the story book, I can pass the different props and mock the data and then now we can see how it's going to look like context and that's all you need. That's clever. Yeah. So, kind of like a go slow to go fast thing, right? Little bit of extra setup, but then you get a ton of speed out of the out of the back end. Exactly. Very cool. Nice. I hadn't really ever thought of

using Storybook as effectively a mock server. Okay. Should I test off of your stuff now? Yeah, that's a good good question. You might be able to use your face to try. Well, here. Connect your

camera then try your Oh, no. Cuz Oh, yeah. It works. Okay. So, now hit enter to start the game.

Oh, you're out of bounds. Oh. Oh. Got to There you go. Am I puckering? What am I doing? You can pucker. You can eyebrow.

You can smile. Your jaw opens up. Yeah. [Music] Okay. So, what we have is recreatable. We have a recreatable scenario. We're integrating our services. This is amazing.

[Music] Are we giving the players an ID or like what's No. Okay. Well, I guess no, cuz I do you have a a field for them? You know what? You know, we'll just do this. We'll say has player one. No, just player one. Hold on a second. Hold on. Hold on. Hold on. Just name them player one, player two.

Whatever. They don't get names anymore. [Music] Uh, but basically, I can create a new game now. Uh, Shashi's been wiring up uh stuff for the UI. So, like I already have actions to send to the websocket server. We can add a new player and stuff. So, it's just wiring up a few

things near the end. It's didn't get as far as we wanted to, but I feel good. This is a lot. Yeah, you got a you got a lot going on here and that's So, I'm excited. And it works on both devices right now. Oh, see, and that was like

the big thing, right? So, I feel like that's the way to do it, right? Make sure that it's doing the the requirements and then sort of like add polish until the time runs out. Yeah, totally. Uh, so I'm working on the game board itself. Um, I got the wrapper done

here, but the game board, what we're going to do is I have this object that's coming back from Nick. Um, it's a two-dimensional object, um, with a bunch of arrays and so it it hits every cell and so now I will know which cell, row, and column is supposed to be a player and whatnot. And that way that data is saved on the back end. And anytime like if I'm all synced or anything when I pass through the server will pass me the correct um pretty much the matrix so that I can uh show the correct uh tokens in the right areas. [Music] So what's left? You got about a little less than 30 minutes. We need to create the So when people are connecting we take their player IDs. I'll create a

bird for each of them that are like subordinate looking birds. And I had a test just a second ago that had other birds bouncing around with yours being kind of dominant. So you can tell which one is you. Oh, I got you. So we have a game starting. We have people pooling. We have the server starting the game. People can play it. You can make contact

with the wall and die. We're sending deaths. We're sending jumps. We're doing a lot of the stuff. We need to see

background birds basically. Yeah, we kind of need to do the multiplayer aspect, but we've got messages passing and we have mechanism. So what's what's left to make sure that they both are working together. So, I got the player one starting and so now I need to make sure the other device I don't know that that's you don't know that the other device is working yet. Got it. So, so now it's just kind of getting the messaging back and forth.

Yep. All right. So, Nikki, talk to me a little bit about what you've got going on here. So, learned cool things today about persisting data through a temporal workflow is that you can create an activity, instantiate an object, you can carry that object throughout your workflow. Right. So this is where they will go to our like homepage. It will start a game, generate a game ID and then another player can log on and start and then player one, player two will be added and then as they play along, it'll be added along their game board arrays. [Music] Something happened with uh they failed. I wasn't getting and look it's

like I'm continuing to I'm still hammered into the ground also and not making collisions. [Music] 11 minutes. Okay, let me solve that problem. I'll get the birds the multi bird thing.

Okay. Fixed. I think you work on like why it's breaking the the motions. Yeah, you got it. The the clicks are not working. It just stopped working. It's just a TypeScript

error that's causing the thing to crash. Oh, there we go. We back. I don't I don't think I am either. How's everybody feel? I failed. And that 100% confidence might have been. We tried to build a game called break in the law. And the goal was to be a memory

game where um the two players, one person would steal from one person's board and then you'd have to take a turn and then guess which item was stolen from the board. This ID is generated from um temporal and passed to the client and so the first person it goes to the first game and theoretically the second player would go to the player two spot. Then you would steal you would guess new steel. Oh hey look the icons are stay. Oh they were staying. That was

great. It was very challenging because temporal was a really it was a brand new like concept and trying to figure that out and so I went into that knowing that there was a challenge involved and if I had like eight hours I think we would have come out like a lot more successful with an actual finished product. And so what what do you think is standing between you and a finished app now? Like it it looks pretty close here. So yeah, that's what it was. It was just attaching uh we needed to attach the icons. We were handling them on the client side at first instead of handling them inside temporal, which is what we should have been doing. And then that

persists all the way through the temporal workflow, which is really freaking cool cuz that was no database. I didn't have a whole lot of faith in that. I'm a changed woman. Like that's really cool. I like that a lot. All right, so here's the CLI. So our workflows, we did get them running. We

just didn't get them connected fully to the client side. So we have our um our workers and our activities all stored in our server folder. So whenever uh you go to your first index page whenever the component itself mounts I await fetch uh API and temporal and then the temporal API calls the client from uh the temporal source folder and then starts the client which probably not the way you're supposed to do it but it worked. And then, you know, we just had my basic components um just uh with some turn areas in there if it was your steel or if it was steel turn or if it was your turn to guess um with the game board. And it was literally just um pages, your homepage and then your game ID and then your player uh where the board would change depending on what turn you were on. This took big brain time. Temporal

was big brain time. Whenever you said that you break down the activities into small little steps, right? And so that was like starting the game, getting a game ID and then creating a you know our little steps and then you build them into your workflows and then so just like the tearing of information through temporal seem to like make it flow really well. That's what put it in perspective for me is that you like tiny little pieces build up to it and then you have all of your activities and your workflow. There's where your activities

come in and then you have your worker manage your workflow and then your client activate your worker, right? Something like that. Okay, I was close enough. Um, but yeah, that's pretty cool. I like temporal. I'll be using it again. Great work.

I think the the idea of um kind of turnbased is a good way to use this sort of this sort of setup. The way that they designed their app was simple and fun. And so it was a I thought a really clever way to approach this challenge. So what we tried to build was connect 4. And what we did first was did highle planning. As you can see, we got some

things done and we never got to our special features, which good thing we put them in special features. But overall, I think we both felt really good to where we were at for 4 hours. Uh, I was able to build components using Storybook and then throw it into Storybook so that we can kind of have an overview of what components we have. Uh it allowed us to

also mock uh components and objects and stuff into our components so we can see them being rendered. And so with the game wrapper, this is what we use to construct the board itself with the players on the side. Uh the design was based off a front end mentor. Because we

only have 4 hours, I didn't want to come up with the custom design. I said, let's just use something that's out there. So, as you can see, we tweaked it a little bit to minimize um the designs that we were going to implement. I found temporal pretty approachable. I I got blocked on something silly. The the Q name was incorrect. So, we lost some

time there. But regardless, I still think we made a lot of really great progress. You can see we have all kinds of games here. You can also see in the UI, you can see the things that we have here. We have signals and we're doing queries within the workflow. So, make move and join game are signals that were happening. The kind of secret sauce is I

wired up a websocket server and that's what the temporal client was instantiated. Basically the websocket server I ended up using a a custom hook in React just I just called it use websocket. Basically anytime anything happened, any action, as soon as the state changed, you would get the new state and then basically UI would rerender. And this is what Shashi was

saying in terms of like there wasn't too much logic on the client side aside from like just render me when something changes. And the other thing too is this worked on multiple devices. So like we didn't have a chance to wire it up to the hosted one, but we ended up going with the IPA for my computer on the Wi-Fi network. So Shashi was able to create a new game from the same server.

Yeah, there's not too much going on here. You just basically get messages and then I just keep setting some state if anything changes. Um, but that's basically like the secret sauce to wiring it up to the UI. Okay, so I can start the game and you're going to see the games there. And if I come back to

the workflows here, you're going to see there's this new game and you can see that a player joined. Yeah, you can see I'm making moves right now and they're actually updating with the signals here. Uh, the UI is rerendering. We just didn't get a chance to put the tokens in, but we basically have the XY coordinates on the right here. So, if I click here, you can see here the Z for the top. I thought it was interesting to use Temporal for a game because it's obviously showcasing some features of it. But, uh, I found this UI super

helpful for me to know what was going on. So, I wanted to play it. I wanted to play their app. Yeah, same. I can't believe how much they got done with the TypeScript SDK with Temporal. I mean

it's just impressive. So we started with um great ambitions. My confidence in myself was absolutely misplaced. My confidence my confidence in Adam was also misplaced. No no no. We started with an attempt to build the most distributed fault tolerant durable game of Flappy Bird called Flappy Face that you know powered by temporal. Uh 2 hours in I realized that you can't run a long running server within a temporal workflow. That was crushing because it

meant I'd have to refactor about 400 lines of code. So 2 hours in, I decided that instead of backing our application with temporal, instead we'd try to fool Alex by creating lots of fake temporal workflows on startup. And I don't know if you guys noticed, but all of ours were completed. We had enable enabling a webcam to like look at your face. And so here, I'll just show you some of the data cuz it's really fun. So here's my

face. It's very clownish. The interesting thing is over here on the right where we're seeing the look at my brow outer upper left. Then we got pucker. Oh, we couldn't figure out no sneer, but we learned that mouth pucker, which is great for me. Ooh. Ooh.

Ooh. It's great for me. Hates mustaches. We also found out that there was no need to implement sound effects because not a single person can do this without making their own sound effects. This is true. So then I made a a bird in our thing today. And I was talking earlier how

like uh it was just fun to put it in 3D cuz once I wanted the wings to flap, then I was like, I might as well rotate it and showcase it. So there's the bird in case we're in vanilla CSS. Yeah, that was really HTML, you know, good and easy to prototype in here. So, it's just a

it's it's three boxes. There's a box and two boxes on the edge that just pivot on the top and then I filled them with a triangle shape using a conic gradient like this right here. Conic gradient things in here are kind of hooking up with the websockets. We're listening to

the different websocket events. So, you can see that we were really close. We had multiple characters connecting. Uh,

we had jumping working and we had death and collision working, but we needed maybe another hour. We could have this all wrapped up with multiple clients bouncing through the level. You'll see in a second. Here's my collision

detection. Oh, yeah. So, I used to know JavaScript. I used to know library for it. So, like player jump is right here, right here. And I'm saying uh if you're not dead, you know, then hey, call jump to the um the websockets so that the other characters see you jump. Mark yourself as jumping and take the current translate Y off of the element. And cuz

it's falling, take it and put it right back in as a new value minus how big you can jump. And in this case, it was 100. And now we're going to animate to that new position over 2 seconds. So every time you jump, I'm actually just interrupting a key frame animation, turning it into a transition. And when the transition ends, I then say he's done jumping and he goes back to falling. That's literally my game

engine. So my game engine only needed to signal to the server a jump and death. The rest of it was totally uh off the main thread using CSS animations. I I also wrote an absurd amount of Go for a very small amount of functionality. It's kind of embarrassing. This is a giant state machine written in in basically vanilla Go. I do use the gorilla package

for websocket stuff that just handles, you know, different events like uh birds connecting, birds disconnecting, birds jumping, birds dying, handles all that kind of stuff. Um just basically this giant go file. When we connect, you can see I've got a little indicator. I'm also now getting a countdown from the server. So, the server's driving. And we fell because it's watching my eyes.

There we go. Oh. Oh, no. I can't do this for 4 seconds. Get ready. Oh, that works. So, that's it. Uh, we really, really wanted to have it all connected and make it to, you know, I don't know, 2,000 3,000 score. Usually around there, it

starts to get way too difficult. Ooh, I need to not talk much cuz if I get excited oh yeah. Hi. Oh. Oh, this is awkward. H. Hi. We're doing it. Show up. There we go. We

got to 2,000. Really, really solid stuff. I think that they won today. I hope Adam realizes that this video is going on the internet and everyone will see him like wagging his mouth open at a at a video. This was a lot of fun being here nerding out with other people. So,

the thing that I struggled the most with today was figuring out how to get the client for Temporal actually working. That was the most challenging aspect for me is just that like I felt like I was missing a piece like the connector to how everything would work smoothly. Um I felt like overall uh learning temporal and doing other things as well within the short amount of time we had it was a great success. It came together once we

got unblocked and things just moved really quickly from there and then it's like things just fell into place after that. Oh yeah. It's hard to describe this product but then you see the things here and it was illuminating. So to see the workflows and to see how other people were leveraging them immediately gave me applications that I can think of. So 2 hours in I got it and had to rip it out. But I got it. You're bringing people into a space where there is no analog. There's no like comparable thing. You can't make a metaphor. You

just sort of have to like hammer on the concept until they until it eventually it just clicks and everybody goes, "Oh my god, I get it." Right? But there's no shortcut to that. It's uh it's it's really challenging. I think that that

this durable workflow and and the way that these sorts of things function is one of those mental models that you it just it I don't know how to teach it faster. [Music] Heat. Heat. [Music] [Music] [Applause]

2025-05-02 12:28

Show Video

Other news

Expanding Access: Leveraging Health Technology in Rural Communities 2025-05-12 12:10
IBM Doubles Down on AI, Palantir Falls Short | Bloomberg Technology 2025-05-08 12:57
New Breadboard 8088 PC V2 #11 CGA Video Controller 2025-05-09 06:01