How to Create an Interactive Web‑Controlled LED Matrix Using Node.js and Raspberry Pi
You’ve probably seen those glowing wall panels at tech fairs and thought, “I could build one of those at home.” The good news is you don’t need a fancy lab or a big budget. With a Raspberry Pi, a cheap LED matrix, and a few lines of Node.js, you can turn any browser into a remote control panel. Let’s dive in and make something that actually lights up.
Why This Project Matters Right Now
In a world where remote work and home labs are the norm, having a physical display you can drive from a web page feels oddly satisfying. It bridges the gap between code you write on a laptop and the hardware that lives on your desk. Plus, the skills you pick up—GPIO handling, real‑time websockets, basic electronics—are reusable in countless other maker projects.
What You’ll Need
| Item | Reason |
|---|---|
| Raspberry Pi (any model with GPIO) | Runs the Node.js server and talks to the LEDs |
| 8×8 (or larger) LED matrix with MAX7219 driver | Simplifies wiring; you only need a few pins |
| Micro‑USB power supply for the Pi | Keeps everything powered |
| Jumper wires (female‑to‑female) | Connects the Pi to the matrix |
| Breadboard (optional) | Makes wiring tidy |
| SD card with Raspberry OS installed | The OS for the Pi |
| Laptop or desktop on the same network | To open the web control page |
All of these items can be found on a typical online electronics store for under $30. If you already have a Pi lying around, you’re practically ready to go.
Setting Up the Raspberry Pi
1. Flash Raspberry OS
Download the latest Raspberry OS Lite image from the official site. Use the Raspberry Pi Imager to write it to your SD card. When the flashing finishes, pop the card into the Pi and power it up.
2. Enable SSH and Wi‑Fi
If you’re using headless mode, create an empty file named ssh in the boot partition. Add a wpa_supplicant.conf file with your Wi‑Fi credentials. This lets you SSH into the Pi without a monitor.
ssh [email protected]
# default password is "raspberry"
3. Update Packages
sudo apt update && sudo apt upgrade -y
A clean, up‑to‑date system saves you from mysterious errors later.
Installing Node.js
Node.js runs the web server that will talk to the LED matrix. The easiest way is to use the NodeSource binary distribution.
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
node -v # should show v20.x
npm -v # should show a matching version
Now you have a modern Node runtime ready for our code.
Wiring the LED Matrix
The MAX7219 driver uses a simple SPI‑like interface: DIN, CLK, CS (sometimes called LOAD). Connect them as follows:
| Pi Pin | Matrix Pin |
|---|---|
| GPIO 10 (SPI MOSI) | DIN |
| GPIO 11 (SPI SCLK) | CLK |
| GPIO 8 (SPI CE0) | CS |
| 5 V | VCC |
| GND | GND |
If you’re using a breadboard, place the matrix module on it and plug the jumper wires into the corresponding pins. Double‑check the connections; a reversed wire can fry the driver.
Writing the Node.js Server
Create a new folder on the Pi and initialize a project:
mkdir led-matrix && cd led-matrix
npm init -y
npm install express socket.io rpi-led-matrix
The rpi-led-matrix package wraps the low‑level GPIO calls for the MAX7219. Here’s a minimal server script (server.js):
const express = require('express')
const http = require('http')
const socketIo = require('socket.io')
const { LedMatrix } = require('rpi-led-matrix')
const app = express()
const server = http.createServer(app)
const io = socketIo(server)
// Serve static files from the "public" folder
app.use(express.static('public'))
// Initialize an 8x8 matrix on SPI bus 0, device 0
const matrix = new LedMatrix({
rows: 8,
cols: 8,
chainLength: 1,
parallel: 1,
hardwareMapping: 'regular',
})
// Turn all LEDs off at start
matrix.clear()
matrix.render()
io.on('connection', (socket) => {
console.log('Client connected')
socket.on('draw', (data) => {
// data is an array of 8 numbers, each representing a row bitmap
matrix.clear()
data.forEach((row, y) => {
for (let x = 0; x < 8; x++) {
const on = (row >> (7 - x)) & 1
if (on) matrix.setPixel(x, y, true)
}
})
matrix.render()
})
socket.on('clear', () => {
matrix.clear()
matrix.render()
})
})
const PORT = 3000
server.listen(PORT, () => {
console.log(`Server listening on http://localhost:${PORT}`)
})
Explanation of Key Parts
- Express serves a simple static web page where you’ll draw.
- Socket.io creates a real‑time channel between the browser and the Pi.
- LedMatrix abstracts the SPI communication; you just set pixels and call
render().
Building the Web Control Page
Create a folder called public next to server.js and add an index.html file:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>LED Matrix Control</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; margin-top: 30px; }
#grid { display: inline-grid; grid-template-columns: repeat(8, 30px); gap: 2px; }
.cell { width: 30px; height: 30px; background: #222; cursor: pointer; }
.on { background: #0f0; }
button { margin: 10px; padding: 8px 16px; }
</style>
</head>
<body>
<h1>LED Matrix Control</h1>
<div id="grid"></div>
<br>
<button id="clearBtn">Clear</button>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io()
const grid = document.getElementById('grid')
const cells = []
// Build 8x8 grid
for (let y = 0; y < 8; y++) {
const row = []
for (let x = 0; x < 8; x++) {
const div = document.createElement('div')
div.className = 'cell'
div.dataset.x = x
div.dataset.y = y
div.addEventListener('click', () => {
div.classList.toggle('on')
sendPattern()
})
grid.appendChild(div)
row.push(div)
}
cells.push(row)
}
function sendPattern() {
const rows = cells.map(row => {
let byte = 0
row.forEach((cell, x) => {
if (cell.classList.contains('on')) {
byte |= 1 << (7 - x)
}
})
return byte
})
socket.emit('draw', rows)
}
document.getElementById('clearBtn').addEventListener('click', () => {
cells.flat().forEach(c => c.classList.remove('on'))
socket.emit('clear')
})
</script>
</body>
</html>
The page builds an 8×8 grid of clickable squares. Each click toggles a cell and sends the new bitmap to the Pi via websockets. The Pi receives the draw event and lights up the corresponding LEDs.
Running the Whole System
- Start the server on the Pi:
node server.js
-
Find the Pi’s IP address (e.g.,
hostname -I). Open a browser on any device on the same network and go tohttp://<pi‑ip>:3000. -
Click the squares—watch the LED matrix light up in real time!
If you see nothing, double‑check the wiring and make sure the matrix is powered. The console will print “Client connected” when the browser successfully opens the page.
Next Steps and Ideas
- Add color – Swap the MAX7219 for an addressable NeoPixel matrix and use the
rpi-ws281x-nativelibrary. - Create animations – Send an array of frames from the browser and loop them on the Pi.
- Integrate sensors – Hook up a temperature sensor and display a heat map on the matrix.
- Deploy with PM2 – Keep the server running after a reboot.
The beauty of this setup is that the core idea—using a web socket to push pixel data—remains the same no matter how fancy you get. At CraftCode Academy we love projects that start simple and grow with you. Give it a try, break a few LEDs (they’re cheap), and learn something new along the way.
- → How to Build a Smart Home Hub with a Raspberry Pi for Under $50 @techandtinker
- → Step-by-Step Guide: Build a Raspberry Pi Smart Light Switch for Under $30 @techandtinker
- → How to Build a Smart Home Hub with Raspberry Pi @techandtinker
- → Troubleshooting Common UART Communication Errors on Raspberry Pi: A DIY Checklist @serialcablechronicles
- → Step-by-step Guide: Building a Low-cost Ultrasonic Distance Sensor for Raspberry Pi @sensorcraft