ESP32: 64 WS2812B RGB LED 8×8 Matrix (Arduino IDE)

This guide shows how to use the 64 WS2812B RGB LED 8×8 matrix panel with the ESP32 using Arduino IDE. We’ll show you how to control each LED, set different colors/brightness, draw shapes/icons, and create a rainbow RGB effect.

ESP32: 64 WS2812B RGB LED 8x8 Matrix Arduino IDE

We also have a dedicated guide that shows how to use the WS2812B Addressable RGB LEDs (Neopixels) with the ESP32 board using Arduino IDE.

Prerequisites

Before proceeding, make sure you have the Arduino IDE installed and the ESP32 boards. Follow the next tutorial if you haven’t already:

Introducing 64 WS2812B RGB LED 8×8 Matrix Panel (Neopixels)

The WS2812B 8×8 Matrix Panel has 64 RGB LEDs (also known as neopixels) that allow you to control each LED independently with any color and brightness using just one digital pin of your ESP32.

64 WS2812B RGB LED 8x8 Matrix Panel

Powering the WS2812B LED 8×8 Matrix Panel

The LED matrix panel should be powered using a 5V power source. At 5V, each LED draws about 50mA when set to its full brightness. This means that at full brightness in white for every 30 LEDs, the matrix panel may draw as much as 1.5A. Make sure you select a power source that matches your needs. An AC to DC power adapter that provides 5V and 2A should do the job:

If you use an external power source, don’t forget to connect the power source ground to the ESP32 GND pin. In this tutorial, we’ll control 64 LEDs (it’s a square matrix 8×8) using an external power source.

Parts Required

For this project, you need the following parts:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Wiring 64 WS2812B RGB LED 8×8 Matrix to the ESP32

Wiring the WS2812B to the ESP32 is quite simple, as it only requires one digital pin (DIN) to control each LED individually.

64 WS2812B RGB LED 8x8 Matrix Panel Pins
8×8 MatrixESP32
V-GND
DINAny digital pin (for example: GPIO 2)*
Power Adapter
GNDESP32 GND
GND8×8 Matrix V-
5V8×8 Matrix V+

* you can use any other suitable GPIOs. Check the ESP32 Pinout Guide:

Wiring the 8×8 matrix panel to the ESP32 is very simple. We’ll share the wiring diagram below with an external power adapter, but you can also use the 5V directly from the ESP32 if you are not using white color with full brightness.

If you’re using a power adapter, connect GND to V-, and connect GPIO 2 to the DIN (data) pin. Then, wire the external power adapter 5V to the V+ and GND to V-.

ESP32 64 8x8 RGB LED Matrix Panel WS2812B Wiring Circuit Diagram

Installing the FastLED Library

There are several libraries to interface the WS2812B LEDs with the ESP32. We’ll use the FastLED library. You can install it quickly by following these steps:

  • Open the Arduino IDE Library Manager
  • Search for FastLED
  • Install the FastLED library by Daniel Garcia
Installing WS2812B Addressable RGB LED Neopixel FastLED Library Arduino IDE

Example #1 – ESP32 Control WS2812B 8×8 Matrix Panel RGB LEDs Individually

The following code sets a different color for each WS2812B RGB LED in the matrix using (X, Y) coordinates. Upload the following code to your board, and it will work straight away.

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-64-ws2812b-8x8-matrix-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <FastLED.h>

#define DIN_PIN        2
#define MATRIX_WIDTH   8
#define MATRIX_HEIGHT  8
#define NUM_LEDS       (MATRIX_WIDTH * MATRIX_HEIGHT)
#define BRIGHTNESS     80          // Brightness (0-255)
#define LED_TYPE       WS2812B
#define COLOR_ORDER    GRB
#define ZIGZAG         true        // Set false if your panel is wired in straight rows instead of zigzag
#define DELAY_MS       500

CRGB leds[NUM_LEDS];

// Convert (x,y) coordinates to the corresponding index in the matrix
uint16_t coordToIndex(uint8_t x, uint8_t y) {
  if (ZIGZAG && (y & 0x01)) {
    x = (MATRIX_WIDTH - 1) - x;
  }
  return (y * MATRIX_WIDTH) + x;
}

// Set the color of a pixel at (x,y)
void setPixel(uint8_t x, uint8_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t brightness = 255) {
  if (x >= MATRIX_WIDTH || y >= MATRIX_HEIGHT) 
    return;
  CRGB color = CRGB(r, g, b);
  color.nscale8(brightness);
  leds[coordToIndex(x, y)] = color;
}

void setup() {
  FastLED.addLeds<LED_TYPE, DIN_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();
}

void loop() {
  setPixel(0, 0, 255, 0, 0);         // First LED x=0 and y=0  |  Red - Full brightness
  FastLED.show();
  delay(DELAY_MS);

  setPixel(3, 3,  0, 255, 0, 128);   // LED x=3 and y=3  |  Green - 50% brightness
  FastLED.show();
  delay(DELAY_MS);

  setPixel(7, 7, 0, 0, 255, 200);    // Last LED x=7 and y=7  |  Blue - ~78% brightness
  FastLED.show();
  delay(DELAY_MS);

  FastLED.clear();                   // All LEDs off
  FastLED.show();
  delay(DELAY_MS);
}

View raw code

How Does the Code Work?

Let’s take a quick look at the code to see how this first example works.

Including Library

First, you need to include the FastLED.h library, which is required to control the WS2812B LEDs.

#include <FastLED.h>

Global Variables

The matrix panel data in pin (DIN_PIN) is connected to the ESP32 GPIO 2.

#define DIN_PIN   2

Define the matrix width and matrix height, then multiple those two values to calculate the total number of LEDs.

#define MATRIX_WIDTH     8
#define MATRIX_HEIGHT   8
#define NUM_LEDS            (MATRIX_WIDTH * MATRIX_HEIGHT)

Set the brightness of all LEDs (range is from 0 to 255).

#define BRIGHTNESS   80

Define the LED type WS2812B and set the Color Order to GRB. Most WS2812B LEDs use GRB order, not RGB.

#define LED_TYPE              WS2812B
#define COLOR_ORDER    GRB

The ZIGZAG variable is used in the coordToIndex() function to assign the correct LED position depending on whether the LEDs are wired in a zigzag or if all the matrix rows are wired in the same direction.

#define ZIGZAG     true

Delay time for the animation in milliseconds (500ms = 0.5s).

#define DELAY_MS   500

Create an array called leds that holds all colors for the 8 LEDs. CRGB is FastLED’s color type definition (Red, Green, Blue).

CRGB leds[NUM_LEDS];

coordToIndex()

The coordToIndex() function converts the (x, y) coordinates to the corresponding index in the matrix:

uint16_t coordToIndex(uint8_t x, uint8_t y) {
  if (ZIGZAG && (y & 0x01)) {
    x = (MATRIX_WIDTH - 1) - x;
  }
  return (y * MATRIX_WIDTH) + x;
}

setPixel()

The setPixel() function sets the color and brightness of a single pixel on the LED matrix. First, it checks if the coordinates are correct, then builds a CRGB color from the supplied color values (red, green, blue), scales that color’s intensity by the brightness parameter (from 0 to 255), and finally writes the correct color into the global leds array at the index returned by the coordToIndex() function.

void setPixel(uint8_t x, uint8_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t brightness = 255) {
  if (x >= MATRIX_WIDTH || y >= MATRIX_HEIGHT) 
    return;
  CRGB color = CRGB(r, g, b);
  color.nscale8(brightness);
  leds[coordToIndex(x, y)] = color;
}

If you have an LED matrix in zigzag you refer to the coordinates of each LED as follows:

WS2812B RGB LED 8x8 Matrix LED Set Pixel individually Position

setup()

In the setup(), start by initializing the LED strip or ring using the FastLED library. You need to pass the arguments:

  • LED Type = WS2812B
  • Data Pin = DIN_PIN
  • Color Order = GRB (Most WS2812B LEDs use GRB order, not RGB)
FastLED.addLeds<LED_TYPE, DIN_PIN, COLOR_ORDER>(leds, NUM_LEDS);

Set the global brightness level and turn off all LEDs at the start.

FastLED.setBrightness(BRIGHTNESS);   
FastLED.clear();

loop()

The loop() runs continously. It first sets the first LED (with coordinates x=0 and y=0) to the color Red (R=255, G=0, B=0).

setPixel(0, 0, 255, 0, 0);

The previous line only sets the color. To actually change the color from the physical LEDs, you must call the show() method.

FastLED.show();

Add a delay of 500 milliseconds:

delay(DELAY_MS);

Then we set the color and brightness of two other LEDs in the matrix.

setPixel(3, 3,  0, 255, 0, 128);   // LED x=3 and y=3  |  Green - 50% brightness
FastLED.show();
delay(DELAY_MS);

setPixel(7, 7, 0, 0, 255, 200);    // Last LED x=7 and y=7  |  Blue - ~78% brightness
FastLED.show();
delay(DELAY_MS);

Finally, we turn off all the LEDs by calling the clear() method.

FastLED.clear();
FastLED.show();
delay(DELAY_MS);

Demonstration

Here’s an illustration of the example running:

ESP32 64 WS2812B RGB LED 8x8 Matrix Control each LED individually Arduino IDE

Example #2 – ESP32 Draw Shape on WS2812B 8×8 Matrix Panel

The following code draws a shape with a heartbeat effect on the WS2812B 8×8 matrix panel. You can upload the following code to your ESP32 board.

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-64-ws2812b-8x8-matrix-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <FastLED.h>

#define DIN_PIN       2
#define MATRIX_WIDTH  8
#define MATRIX_HEIGHT 8
#define NUM_LEDS      (MATRIX_WIDTH * MATRIX_HEIGHT)
#define BRIGHTNESS    80         // Brightness (0-255)
#define LED_TYPE      WS2812B
#define COLOR_ORDER   GRB
#define ZIGZAG        true       // Set false if your panel is wired in straight rows instead of zigzag
#define FLIP_H        false      // Set true if the image displays mirrored left to right
#define FLIP_V        false      // Set true if the image displays upside down

CRGB leds[NUM_LEDS];

// Heart shape (8x8)
const bool heart[MATRIX_HEIGHT][MATRIX_WIDTH] = {
  {0,0,0,0,0,0,0,0},
  {0,1,1,0,0,1,1,0},
  {1,1,1,1,1,1,1,1},
  {1,1,1,1,1,1,1,1},
  {1,1,1,1,1,1,1,1},
  {0,1,1,1,1,1,1,0},
  {0,0,1,1,1,1,0,0},
  {0,0,0,1,1,0,0,0}
};

// Convert (x,y) coordinates to the corresponding index in the matrix
uint16_t coordToIndex(uint8_t x, uint8_t y) {
  if (FLIP_H) x = (MATRIX_WIDTH - 1) - x;
  if (FLIP_V) y = (MATRIX_HEIGHT - 1) - y;

  if (ZIGZAG && (y & 0x01)) {
    x = (MATRIX_WIDTH - 1) - x;
  }
  return (y * MATRIX_WIDTH) + x;
}

// Set the color of a pixel at (x,y)
void setPixel(uint8_t x, uint8_t y, CRGB color) {
  if (x >= MATRIX_WIDTH || y >= MATRIX_HEIGHT) 
    return;
  leds[coordToIndex(x, y)] = color;
}

// Draws the heart shape - scales its color by a brightness factor (0.0 - 1.0)
void drawShape(CRGB color, float scale) {
  FastLED.clear();
  CRGB c = color;
  c.nscale8_video((uint8_t)(scale * 255));
  for (uint8_t y = 0; y < MATRIX_HEIGHT; y++) {
    for (uint8_t x = 0; x < MATRIX_WIDTH; x++) {
      if (heart[y][x]) setPixel(x, y, c);
    }
  }
}

// Pulses a heartrate LED pattern by changing the brightness over durationMs
// It creates a fade in and fade out effect
void pulse(CRGB color, float from, float to, uint16_t durationMs) {
  const uint8_t steps = 24;
  for (uint8_t i = 0; i <= steps; i++) {
    float t = (float)i / steps;
    float eased = (1 - cos(t * PI)) / 2.0;
    float scale = from + (to - from) * eased;
    drawShape(color, scale);
    FastLED.show();
    delay(durationMs / steps);
  }
}

void setup() {
  FastLED.addLeds<LED_TYPE, DIN_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();
  FastLED.show();
}

void loop() {
  // Red color
  CRGB color = CRGB(255, 0, 0);
  // Heartbeat animation
  pulse(color, 0.30, 1.00, 120);
  pulse(color, 1.00, 0.55, 140);
  pulse(color, 0.55, 0.85, 100);
  pulse(color, 0.85, 0.30, 160);
  drawShape(color, 0.30);
  FastLED.show();
  delay(300);
}

View raw code

Code Overview

Now let’s take a quick look at the code in this section. The code initialization is very similar to Example #1, but instead of controlling each LED individually, we will draw a heart shape 8×8.

const bool heart[MATRIX_HEIGHT][MATRIX_WIDTH] = {
  {0,0,0,0,0,0,0,0},
  {0,1,1,0,0,1,1,0},
  {1,1,1,1,1,1,1,1},
  {1,1,1,1,1,1,1,1},
  {1,1,1,1,1,1,1,1},
  {0,1,1,1,1,1,1,0},
  {0,0,1,1,1,1,0,0},
  {0,0,0,1,1,0,0,0}
};

If you want to mirror the shape horizontally or vertically, you can set these variables to true or false.

#define FLIP_H        false        // Set true if the image displays mirrored left to right
#define FLIP_V        false        // Set true if the image displays upside down

The drawShape() function draws a heart shape on the LED matrix panel:

  1. First, it clears the display;
  2. Then, creates a copy of the input color and scales its brightness using the scale factor (0.0 to 1.0);
  3. Finally, it iterates over every pixel of the matrix and lights the LED where the heart shape is defined in the heart[y][x] array by calling setPixel() with the scaled color.
void drawShape(CRGB color, float scale) {
  FastLED.clear();
  CRGB c = color;
  c.nscale8_video((uint8_t)(scale * 255));
  for (uint8_t y = 0; y < MATRIX_HEIGHT; y++) {
    for (uint8_t x = 0; x < MATRIX_WIDTH; x++) {
      if (heart[y][x]) setPixel(x, y, c);
    }
  }
}

The pulse() function fades the heart shape’s brightness from one level to another during a specified time duration. Inside this function, there’s a loop that creates the fade effect over 24 small steps, at each step it redraws the heart shape with the new brightness, updates the LEDs, and pauses a bit between steps to create the heartrate pulse effect.

void pulse(CRGB color, float from, float to, uint16_t durationMs) {
  const uint8_t steps = 24;
  for (uint8_t i = 0; i <= steps; i++) {
    float t = (float)i / steps;
    float eased = (1 - cos(t * PI)) / 2.0;
    float scale = from + (to - from) * eased;
    drawShape(color, scale);
    FastLED.show();
    delay(durationMs / steps);
  }
}

The main loop() runs the red heartbeat effect indefinitely. It first sets the color to red, then calls the pulse() function 4 times to make the heart shape brighten, fade a bit, brighten again more softly, and fade out. After that, it holds the heart shape drawn at low brightness for a short pause before starting the sequence again.

void loop() {
  // Red color
  CRGB color = CRGB(255, 0, 0);
  // Heartbeat animation
  pulse(color, 0.30, 1.00, 120);
  pulse(color, 1.00, 0.55, 140);
  pulse(color, 0.55, 0.85, 100);
  pulse(color, 0.85, 0.30, 160);
  drawShape(color, 0.30);
  FastLED.show();
  delay(300);
}

Demonstration

Here’s an illustration of the example #2 drawing the red heart shape. The LEDs’ brightness will fade in and fade out to create the heartbeat effect:

ESP32 64 WS2812B RGB LED 8x8 Matrix Hearbeat Effect Arduino IDE

Example #3 – ESP32 Rainbow Effect on WS2812B 8×8 Matrix Panel

The next code creates a rainbow effect with colors moving smoothly across all LEDs. Upload the code below to your ESP32 board.

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-64-ws2812b-8x8-matrix-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <FastLED.h>

#define DIN_PIN        2
#define MATRIX_WIDTH   8
#define MATRIX_HEIGHT  8
#define NUM_LEDS       (MATRIX_WIDTH * MATRIX_HEIGHT)
#define BRIGHTNESS     80          // Brightness (0-255)
#define LED_TYPE       WS2812B
#define COLOR_ORDER    GRB
#define ZIGZAG         true        // Set false if your panel is wired in straight rows instead of zigzag

// Rainbow animation 
#define BASE_HUE           160    // Initial blue hue for new LED (0-255)
#define HUE_STEP_PER_TICK  3      // Amount the LED hue shifts per frame
#define DELAY_MS           80     // Animation speed
uint16_t ledsLit = 0;             // Number of LEDs currently turned on
uint16_t frame = 0;               // Counts animation frames

CRGB leds[NUM_LEDS];

// Convert (x,y) coordinates to the corresponding index in the matrix
uint16_t coordToIndex(uint8_t x, uint8_t y) {
  if (ZIGZAG && (y & 0x01)) {
    x = (MATRIX_WIDTH - 1) - x;
  }
  return (y * MATRIX_WIDTH) + x;
}

// Set the color of a pixel at (x,y)
void setPixel(uint8_t x, uint8_t y, CRGB color, uint8_t brightness = 255) {
    if (x >= MATRIX_WIDTH || y >= MATRIX_HEIGHT) 
      return;
    
    color.nscale8(brightness);
    leds[coordToIndex(x, y)] = color;
}

void setup() {
  FastLED.addLeds<LED_TYPE, DIN_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();
  FastLED.show();
}

void loop() {
  // Turns on 1 LED per frame until fully lit
  if (ledsLit < NUM_LEDS) {
    ledsLit++;
  }

  // Update colors of all currently lit LEDs
  for (uint16_t i = 0; i < ledsLit; i++) {
    uint8_t x = i % MATRIX_WIDTH;
    uint8_t y = i / MATRIX_WIDTH;

    // Calculate hue based on how long this LED has been lit
    // Newer LEDs start at BASE_HUE, older ones shift further along the color wheel
    uint16_t age = frame - i;
    uint8_t  hue = BASE_HUE + (uint8_t)(age * HUE_STEP_PER_TICK);

    setPixel(x, y, CHSV(hue, 255, 255));
  }

  FastLED.show();
  delay(DELAY_MS);
  frame++;
}

View raw code

Code Overview

This example also start similarly to the previous ones, so we’ll focus mostly on the new code in the loop() function.

First, you can set these constants to control the starting blue hue and how much the color shifts each frame.

#define BASE_HUE           160           // Initial blue hue for new LED (0-255)
#define HUE_STEP_PER_TICK  3     // Amount the LED hue shifts per frame

It uses two static counters that store their values between frames:

  • ledsLit – how many LEDs are currently on
  • frame – counts animation frames
static uint16_t ledsLit = 0;        // Number of LEDs currently turned on
static uint16_t frame = 0;         // Counts animation frames

In the loop() function, if the matrix panel isn’t fully lit yet, it turns on one extra LED by increasing ledsLit.

// Turns on 1 LED per frame until fully lit
if (ledsLit < NUM_LEDS) {
  ledsLit++;
}

This next code section runs a shifting rainbow color effect to the 8×8 matrix panel. It goes through every lit LED, finds the (x, y) position, and sets its color based on how long it has been on. The most recent lit LEDs stay closer to blue, while older ones shift colors to create the rainbow effect.

// Update colors of all currently lit LEDs
for (uint16_t i = 0; i < ledsLit; i++) {
  uint8_t x = i % MATRIX_WIDTH;
  uint8_t y = i / MATRIX_WIDTH;

  // Calculate hue based on how long this LED has been lit
  // Newer LEDs start at BASE_HUE, older ones shift further along the color wheel
  uint16_t age = frame - i;
  uint8_t  hue = BASE_HUE + (uint8_t)(age * HUE_STEP_PER_TICK);

  setPixel(x, y, CHSV(hue, 255, 255));
}

Finally, it displays the updated colors to the LEDs with FastLED.show(). It has a short delay and increments the frame counter so the colors keep shifting in the next loop.

FastLED.show();
delay(DELAY_MS);
frame++;

Demonstration

Here’s what Example #3 looks like when it’s running while the LEDs are creating the rainbow effect:

ESP32 64 WS2812B RGB LED 8x8 Matrix Rainbow Effect Arduino IDE

You can watch the next short video that demonstrates all three examples.


Wrapping Up

In this tutorial, you’ve learned how to control WS2812B RGB LED 8×8 matrix panel using the FastLED library to control each LED individually, draw shapes and display the rainbow effect. We have more tutorials about RGB LEDs that you may like:

Learn more about the ESP32 with our resources:

We hope you enjoyed this project and learned something new.

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

1 thought on “ESP32: 64 WS2812B RGB LED 8×8 Matrix (Arduino IDE)”

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.