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

ItemReason
Raspberry Pi (any model with GPIO)Runs the Node.js server and talks to the LEDs
8×8 (or larger) LED matrix with MAX7219 driverSimplifies wiring; you only need a few pins
Micro‑USB power supply for the PiKeeps everything powered
Jumper wires (female‑to‑female)Connects the Pi to the matrix
Breadboard (optional)Makes wiring tidy
SD card with Raspberry OS installedThe OS for the Pi
Laptop or desktop on the same networkTo 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 PinMatrix Pin
GPIO 10 (SPI MOSI)DIN
GPIO 11 (SPI SCLK)CLK
GPIO 8 (SPI CE0)CS
5 VVCC
GNDGND

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

  1. Start the server on the Pi:
node server.js
  1. Find the Pi’s IP address (e.g., hostname -I). Open a browser on any device on the same network and go to http://<pi‑ip>:3000.

  2. 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-native library.
  • 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.

Reactions
Do you have any feedback or ideas on how we can improve this page?