936: Realtime LED Wall With React + Websockets ![rw-book-cover](https://wsrv.nl/?url=https%3A%2F%2Fmegaphone.imgix.net%2Fpodcasts%2F5197fe5a-42f7-11f0-affd-87d9985a1760%2Fimage%2Fc86a54acd72683732c4773e25bf0ae14.png%3Fixlib%3Drails-4.3.1%26max-w%3D3000%26max-h%3D3000%26fit%3Dcrop%26auto%3Dformat%2Ccompress&w=512&h=512) ## Metadata - Author: [[Syntax - Tasty Web Development Treats]] - Full Title: 936: Realtime LED Wall With React + Websockets - Category: #podcasts - URL: https://share.snipd.com/episode/fb5546f1-a52f-4038-a675-76e9af48eaa2 ## Document Notes ## Highlights - **Hack Week LED Grid Project** - Wes built a real-time web-controlled LED grid as his Hack Week project combining hardware and software. - He let the wider internet draw on it and used it to display GitHub activity and live pixel art. Transcript: Wes Bos Welcome to Syntax. Today I'm going to be detailing my Hack Week project, which is my real-time web-controlled LED grid that I put together. So Sentry has this thing called Hack Week where you get a week to build whatever it is that you want, maybe a product. Scott and CJ did some really cool stuff. I opted to do something that was a mix of both hardware and software, as well as letting the wider internet be able to control it. And it was a super fun project. I learned a lot about it. And today we're here to talk about all of the different pieces and get into the nitty gritty. ([Time 0:00:00](https://share.snipd.com/snip/130698f9-f91f-47af-b3f7-12a0c4e717db)) - **Soldering A 3x3 WS2812 Matrix** - Wes soldered nine 16x16 WS2812 panels into a 3x3 matrix and rewired leads for his custom frame. - He desoldered pre-made leads, resoldered custom wiring, and estimates about an hour of soldering work with retries. Transcript: Wes Bos So first of all, the LED grid itself is 2,300 and something pixels. So what it is, is I bought these little 16 by 16. They're about, I don't know, 10 bucks, eight bucks a piece. And it's 256 different individually addressable LEDs. And how that works is that you send power to all of these LEDs, but then you can also send data signal to every single LED. And you can change it to be any color that you want, right? They're RGB LEDs. Then you can actually chain them together and make an entire matrix of them. So what I did is I took nine of them and I put them into obviously a three by three grid and soldered them all together. I actually ended up on desoldering all of the leads on the back of them and just going doing my own soldering because these clips were just a little bit too too big to fit behind the frame That I made. Scott Tolinski How long did that soldering take you? Because I'm so slow. Wes Bos I'm getting better at it. It's just that I had to, I did it like several times. Because my initial, the initial time that I did it, I used some like pre-made frame that somebody designed. And I couldn't get access to the back. So I kept breaking it trying to like, put the wires underneath it. It was so annoying. So I ended up just scrapping the entire thing that I found online and designing my own frame for it. But I don't know, soldering maybe, maybe like an hour or so just having to quickly desolder everything. ([Time 0:03:56](https://share.snipd.com/snip/94b895ee-5629-4c5d-a4c5-a0caa55bb1f5)) - **ESP32 + WLED Powers The Grid** - An ESP32 running WLED can drive addressable LEDs and expose HTTP/WebSocket APIs for control. - WLED acts as custom firmware that both serves a web UI and accepts pixel updates from remote apps. Transcript: Wes Bos And then what's powering it and sending the actual data, which is the third line on these, is something called an ESP32. And this is just a $5 microcontroller, which is absolutely wild. And it has a Wi-Fi chip in it, has a Bluetooth chip in it. And then it has all of these different pins. And these pins are used for sending serial data. Most of them are used for sending serial data out through the data line. And on this little ESP32 runs a little piece of software called WLED. So WLED is just this really nice piece of software that will allow you to, you can run all kinds of different animations. You can change the colors of them. I use this in several places. The little on-air sign that I have behind me, I have it off right now because it's blowing it out. The gym lighting, I'm currently doing my house in these LEDs and WLED powers all of that because it's a piece of software that can then send the data signal from the ESP32. It spits it out in serial and is able to deliver it to the different types of chips that run on these very common LED strips or LED grids. CJ Reynolds So would you say WLED is like a custom firmware for ESP32, or is it something that's also running on top of the firmware, like as a separate app or something like that? Wes Bos No, it's a totally custom firmware that you flash directly to these chips, And it's written in C, C++. When I did my controlling my Roomba, I dipped into that as well, writing my own lower level code that would send serial data out of these different pins. But it's quite amazing because WLED also has, like it has Wi-Fi. You can open it up on any browser. You can get apps that connect to it, but it also has several APIs that will accept incoming data. And those are the APIs that I used in order to send data from my application to it. It's literally running a web server on here. WLED is not running on my computer. It's literally running on NES. ([Time 0:06:07](https://share.snipd.com/snip/b356f30f-c315-463d-9a35-11528d3c0ad1)) - **Split Signals To Avoid Data Decay** - Split long LED chains into multiple output pins or segments to avoid data-voltage decay and protocol limits. - Send parallel signals from different GPIO pins rather than pushing data across thousands of pixels on one line. Transcript: Wes Bos Because the way that the data line works is that it snakes through it. So you start the data at one pixel, and it goes in and then into the next one into the next one. And that's actually how it does it with power as well. It sends the five volts in and out. But once you start to like you can do a little demo with one of these really quickly, you don't even have to have an external power supply. But once you get into putting a lot of them together, you hit the limitation of how many LEDs you can possibly send data through, which is 2048. So I was slightly over the actual limit. So what I ended up doing is I split them into three different sections. So one, two, three, And I have three different pins on my GPIO pins on the microcontroller. And those are each sending three different pieces of data out. And then you can go into the software and set up the layout of it. You can see here, I've got all nine of the panels. And then it takes care of, as far as you, the user, is concerned, it's just now, it's just one single grid. And WLED figures out how to properly send the data to each of them in which order. Scott Tolinski Is that a common solution to handle that? Yeah, yeah. Like to use WLED or... No, to break up, to send it in groups like that. Wes Bos The way that serial data works is that it sends high and low voltages. So if you're running like a 5-volt system, it might send like a 4.5 or a 5-volt to show like a 1, right? And then it might send like a 1-volt or 0.5 volt to show a zero, right? And you're basically just sending ones and zeros. And then as you know, like voltages over long runs can start to drift and it can start to die out. So if you have a very long run or if you have a, this is not a long run, but it is a lot of pixels, then the data can start to get weak as it goes through. And there's solutions to boosting it, but the better solution is to simply just chop it up into multiple segments. And then that way you can also send data out the different pins at the same time. ([Time 0:08:42](https://share.snipd.com/snip/4b0372bf-8ad8-469d-804b-aa0679b83e90)) - **Power Injection And Voltage Strategy** - Use higher voltage or multiple injection points to reduce required current and heating. - Prefer 12V or 24V setups for large builds and inject power to panels directly to avoid thermal and voltage-drop issues. Transcript: CJ Reynolds So each panel is five watts. So like, is there a separate power supply for the panel grid itself? And then it's just getting its data signal separately? Yes, exactly. Wes Bos So the ESP32 will run on 5 volts or 3.3 volts. And then you can also buy little adapters for them as well. I'm a big fan of this one where you can put like a 12 volts into it as well. And it will just, it'll has a buck converter, it'll dump it down. But you can power like one panel off of a usb like a one amp off of usb but once you go over one amp it's actually funny mac os x will be like we disconnected your usb thing because it was using Too much power yeah so once you once you pull more than that this all of these together can pull 94 amps which is 94 times what it should do over USB. I didn't do that because you literally cannot buy a 94 amp 5-volt power supply. The real solution when you do bigger stuff like this is instead of having to do more amps, what you do is you just increase the voltage. So I kind of regret buying five volt panels because it has required me to, in absence of the volts, I'm just pushing more amps. And when people do like big things, stadium things, yeah, like a stadium, they'll use like 48 volts, like that's commercial, right? Or 24 volts. And most hobbyists will use either 12 or 5 volts. So I almost do everything in 12 volts now just because it's, you have to fuss a lot less with it. But I had bought these, I don't know, six months ago. And I just, I didn't think about that. But I bought an external power supply, which is like, like this big. And then I also go into my WLED settings and I explicitly set the maximum power supply current to 18 amps right here. And, and that is plenty bright. I don't even have it. I have it at half brightness right now. And it's, it is plenty bright. ([Time 0:11:12](https://share.snipd.com/snip/997c242b-2157-42f7-9e20-aad08ea4162a)) - **Short Circuit Nearly Melted Wires** - Wes accidentally created a short by crossing positive and negative wires and almost melted wiring before adding a fuse. - He unplugged quickly, added a cheap fuse, and recommends installing fuses early to avoid fire hazards. Transcript: Wes Bos And I turned it on, I plugged it in, and it wasn't working. And then I immediately put my hand on it and I realized the wires were getting very hot. And I sillily didn't put a fuse in it at first. I had the fuse on my desk and I was like, I'll put that in at the end. And I luckily I grabbed it and unplugged it and realized I had crossed some wires, but I had created a short circuit and I didn't have a fuse in it. So it was just running the ambridge over and over through the things. And it was melting the wires, which would have caught on fire if I had left it. So that was my one scary moment there. And I after that, I quickly put a fuse is like, like 10 cents. I should have put that in at the very first. ([Time 0:16:27](https://share.snipd.com/snip/ac41b6fa-e3b8-4ae7-971f-9790f223363f)) - **Design A Crisp Multi-Layer Diffuser** - Use a diffuser with black baffles and a thin white top layer to create crisp, non-bleeding pixels. - 3D-print a grid that isolates each pixel and tune overlap to avoid hard seams while keeping strong pixel definition. Transcript: Wes Bos So in order to make them look nice, you have to diffuse them. And what I ended up doing is I first I downloaded a couple things on 3D printer world and whatever and printed them out. But I just was not happy with any of them. So I designed my own diffuser and after a couple trials this is what I ended up with is it is a 0.3 millimeters of white filament yeah and then I did black like baffles or grid on the end and What that did is it gave me like a very crisp look I did it all in white first and it bled a little which is a cool it's a vibe but it wasn't what i was going for with the whole pixel art here i also Didn't want like very defined lines as well so i i printed it i'm not sure if you can see this or not let me see if i can i feel like a makeup reviewer i printed it a couple of millimeters, like One millimeter up in up the side in white. And then that allowed me to not get nice big black lines. And then I did the rest of it in just black. And that was the that was the winning ticket to do it. And I had nine of them and ended up I ended up just gluing it. I wanted so badly to like make some sort of click together system but like I was doing way too much already with this yeah yeah you're saying your diffuser's got a BBL yes well I just have CJ Reynolds To say this is one of my favorite parts of the build because like I've tried to mess with pixel arrays before and I've never come across a good diffusing solution so I love the fact that Like each pixel is basically a black square it has like it has walls around it to prevent it from bleeding and then the top layer is just thin enough so that it can shine through like because I don't have a 3d printer and like when I was trying to do this I was like trying to use like foam board and pieces of paper or like you could buy like paper that's like slightly opaque, but Then you run into the bleeding issue. So like, it just looks blurry. But the fact that you have, like you've isolated each pixel to then shine through, like is awesome. Yeah. Wes Bos It's, it's really nice to be able to do that. A lot of people, I do regret or not, I don't like that there are little lines in here. I modeled it with a 0.2 millimeter lip on two of the edges. And what that allowed me to do is to overlap them very slightly. ([Time 0:18:08](https://share.snipd.com/snip/9807632c-bf89-4cd5-ac61-5b0b634909be)) - **Durable Objects As Real-Time State** - Cloudflare Durable Objects can act as hosted mutable state that persists between serverless invocations. - Pairing Durable Objects with Party Server enables real-time WebSocket sync across clients and a single source of truth for pixel state. Transcript: Wes Bos So I built a something with Cloudflare durable objects and and party kit. It's called party server. Now, the whole party kit thing doesn't look like it's being super maintained, but they are moving. Yeah, but they're moving it to something called PartyServer, which is a bit more of a peeled back layer, which I moved it in like an hour because I initially built it in PartyKit. So CloudFlare durable objects are essentially a JavaScript object or a JavaScript class. Hey, let me open up some code here. Scott Tolinski You're going to remove the photos the dogs were putting on your... Wes Bos Oh my goodness. Look at this, Scott. Cloudflare durable object solves the problem of how do you maintain state or how do you maintain like memory with a serverless function or a Cloudflare worker where these things spin Up and down when they're needed? And they spin up multiple versions of them when you need lots of them. You can simply just stick data on the object. And then that data is available anytime you come back to that instance of the durable object, which is very similar to if you were to just stick something in memory on a long-running server, Except that when you restart it, it's actually saved to a database. So it stays there. So the durable object was really cool. And then on top of that, Party Server gives you the ability to do like a real time. What that does is when somebody is drawing on this grid, what's happening is that there is a WebSocket connection from your browser to the Cloudflare Durable Object Server. It's a Cloudflare worker. It's running locally on my machine. But you could deploy this thing to the cloud as well. And then every single time there is like somebody has drawn to one of these grids right here, it's sending that data over a web socket to the backend. And then the backend is then saving that grid state to the durable object. And then it is then broadcasting those changes to everybody else who currently has this application open. There is ability to even do rooms in this one as well. I have it built into the app. But when you come to the homepage, it's just the default room. And it's really cool because like there's you can get into like doing database schemas and whatnot. But I'm just simply just I just have a like a like a JavaScript map and set for maintaining all of this data, right? It's just an array of 2300 pixels. And that array tells you which color every single pixel is. And then that data source of truth is the Cloudflare durable object. ([Time 0:21:52](https://share.snipd.com/snip/60a962b0-42ab-4f8e-bd0f-251f3034d730)) - **Diff And Batch Dirty Pixels** - Diff incoming edits server-side and only send 'dirty' pixel updates to the controller to minimize traffic. - Batch and recursive-send changes, and confirm responses to avoid resending and to keep the LED state in sync. Transcript: Wes Bos So what's happening is as part of the server, we are checking as frequently as we can for what are called dirty pixels, right? So data is coming in all the time. And then every now and then we run a function that then checks, what do we need to send to the microcontroller that's running WLED to tell it which pixels on this grid to now change. And the way that that works is there's a little bit of a diffing algorithm. It detects which pixels are dirty, meaning they have changed from what is currently on the LED grid. And then it creates just an array of all of the currently changed pixels, right? So if I were to just take red and paint like two or three of these, it's going to say, okay, pixel 400, pixel 800, and pixel 650 have changed, and those are red. And then what it's going to do is it bundles that then up into a request, and then it will send that to the web server that's running on the ESP32 via the WLED software. CJ Reynolds So I guess to clarify, is that just like a separate process running on your computer? It's basically another party server client that has a direct connection to the grid because it's on your local network? Wes Bos I actually did it both ways. And I'll tell you about the problems that it had. So what I'm doing and what you see right now is it's a function called check for update. I guess I can just show you because we have the code here, right? Yeah. Show me. There we go. It's just a method inside my durable object called updateLED. And that will then run a bunch of code that checks if there are dirty indices. And then if so, it will send it to the WLED. And I wrote a whole other WLED library for... There's a whole bunch of logic as to how it gets sent over. I'll talk about that in just a second as well. And then that will just that's a recursive function, which will try to it will call itself as soon as it gets response back from the WLED saying it has successfully updated the pixels. ([Time 0:28:56](https://share.snipd.com/snip/3b0d32a5-ca32-4a13-a6e5-b60c1cdee996)) - **WLED API Limits And JSON Overhead** - WLED exposes HTTP and WebSocket JSON APIs but microcontrollers struggle with large JSON payloads. - Protocol choice matters: WebSocket is low-latency but byte-limited, HTTP can handle larger payloads but still risks crashes on big transfers. Transcript: Wes Bos There is a WebSocket API, and there is an HTTP, like just a normal API endpoint that you can hit with like a fetch request. I just call that the fetch API as it's simpler. Both of them have limitations. The WebSocket API is faster because when you connect to it, you have a single socket connection open. And then anytime you want, you just send updated pixels. And that is so much faster than the fetch-based API. However, you can only send, I think it's something like a hundred pixel change at once. So if somebody was to take a Firefox logo and drag it on here, it would very quickly overwhelm the web socket. And not so much that it would reject it, so much that it would crash this thing here. Because there's only so much memory in these microcontrollers. Not that it can't hold an array of 2300 values, but the WebSocket and the HTTP API are JSON-based, meaning that the microcontroller has to take the JSON payload and parse it from JSON To whatever the C-based array is. And then it has to then send that data over GPIO via ones and zeros. So the WebSocket API can't send too big of payloads. And then the HTTP API can send something like it says it can do 2400. But I tested it. I kept crashing it. I think I'm sending something like 800 or it's bytes. It's not pixels. But basically, when I'm writing a full image, I have to send four or five consecutive fetch requests. Otherwise, it will crash and reboot the entire microcontroller. What happens when that crashes? It literally just the whole screen goes blank and then it reboots and then shows you the initial thing. And then the WebSocket reconnects. It's not a big deal. It recovers, but it obviously goes blank entirely. So I wrote a whole bunch of logic to say if this is a large, like I have a bunch of variables as to limits. There we go. WebSocket byte limit. So 1,400 bytes. And then HTTP byte limit is 5,000 bytes. So I can send just under four times more via fetch request than a WebSocket. So when you're drawing a little bit, this is all WebSocket. But then when you draw a lot of changes, then it will switch over to fetch and use those. So I wrote a bunch of logic in here to switch between the two. So that was my first two. And I wasn't really happy with either of those because I was I was doing like the the video and it was like, like one frame a second. It's really slow to actually draw. And I was like, man, like, that's kind of slow. And these WLEDs are not slow, right? You can put like gradients on them and whatnot. They're pretty powerful. But what is slow is, I'm pretty sure, is the JSON parsing and the WebSocket connections and having to run a web server. So ideally, you would use a lower level thing that would simply just send raw ones and zeros from whatever to it. And that's via something called UDP. And UDP is not like TCP. UDP is a one-way signal where you can just send commands and you never get a response back from it. You just send, send, send, send. And that's how I did a video many years ago, flying a drone with React. That was a UDP socket, just sending data from the React app over Wi-Fi. That's a UDP socket. But that still only allowed me to do 400 pixels at once. So that was still a bit of overhead. And it had the downside of Cloudflare workers, their runtime is not Node.js. Their runtime is something called WorkerD. And they support most of Node.js. Part of what they don't support is the socket API. ([Time 0:31:36](https://share.snipd.com/snip/baae9624-a919-4012-b1c2-6a158a9d2c7a)) - **Bridge To UDP For Real-Time Updates** - Use UDP or a local bridge to send raw pixel data when JSON APIs are too slow or crash the controller. - Run a local bridge to receive Durable Object updates and forward compact UDP frames to WLED for real-time performance. Transcript: Wes Bos So ideally, you would use a lower level thing that would simply just send raw ones and zeros from whatever to it. And that's via something called UDP. And UDP is not like TCP. UDP is a one-way signal where you can just send commands and you never get a response back from it. You just send, send, send, send. And that's how I did a video many years ago, flying a drone with React. That was a UDP socket, just sending data from the React app over Wi-Fi. That's a UDP socket. But that still only allowed me to do 400 pixels at once. So that was still a bit of overhead. And it had the downside of Cloudflare workers, their runtime is not Node.js. Their runtime is something called WorkerD. And they support most of Node.js. Part of what they don't support is the socket API. So you cannot open a UDP socket from a Cloudflare worker. It has to either be like a web socket or an HTTP request. So I ended up making a bridge that like CJ suggested was I made a server side script that basically just joined this room and watched for changes. And as those changes happened, it would send them over the UDP socket. ([Time 0:35:02](https://share.snipd.com/snip/80446565-64ef-4d97-a555-bd3ef6f55a71)) - **Exposing Local Server Invites Mischief** - Wes exposed his local Vite dev server to the internet via a Cloudflare Tunnel so strangers could connect. - He observed users writing console scripts to programmatically push pixel changes and one user flooding requests until he rate-limited them. Transcript: Wes Bos At one point, somebody wrote a thing to send a million requests through. And my internet was getting kind of weird where it just kept dropping in and out. But I killed it at that point. I think they went away. You probably would have to put some sort of rate limiting in there. Here's the webcam code. And the funny part about that is that people were abusing the drag and drop images. So what I did is I just went to my code and commented out that feature. And it live updated on everybody who had it currently opens website. You know, like I can work on something locally. ([Time 0:43:28](https://share.snipd.com/snip/0e857666-8629-4233-8567-77edccc566dc)) - **Use Rooms And Admin Controls** - Build admin rooms and remote controls to display curated content and switch scenes easily. - Use room routing and admin toggles to show stats, logos, or curated art without exposing the main canvas to chaos. Transcript: Wes Bos The future. I don't know about the future. This was this is a really fun project. I think what I would like to do is is to hang it on my wall and just to find a way for it to just be always open and put it on my website and just have people come by at any time, draw on it. The other thing is that I have admin controls where I can set which room is on there. So I can have one room, which is like forward slash syntax and if i were to draw yeah syntax logo to this one you'll see it's it's not mirrored to the led but if i go to the admin i could just Switch it yeah now that room is being painted so i could have like a like a syntax room i could have a drawing room i could have like a stats room and then i could just have them cycle through Those rooms or i could have buttons on my on my little stream deck here that would switch ([Time 0:45:34](https://share.snipd.com/snip/fae5bb9d-01ea-4fb8-b823-9a7bbff68f64))