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.

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.

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:
- ESP32 board – any model of your choice
- 8Ă—8 64 LED Matrix Panel with WS2812B
- 5V 2A power adapter
- Jumper wires
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.

| 8×8 Matrix | ESP32 |
| V- | GND |
| DIN | Any digital pin (for example: GPIO 2)* |
| Power Adapter | |
| GND | ESP32 GND |
| GND | 8×8 Matrix V- |
| 5V | 8×8 Matrix V+ |
* you can use any other suitable GPIOs. Check the ESP32 Pinout Guide:
- ESP32 Pinout Reference: Which GPIO pins should you use?
- ESP32-S3 DevKitC Pinout Reference Guide: GPIOs Explained
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-.

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

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);
}
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:

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:

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);
}
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:
- First, it clears the display;
- Then, creates a copy of the input color and scales its brightness using the scale factor (0.0 to 1.0);
- 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:

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++;
}
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:

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:
- Arduino IDE: WS2812B Addressable RGB LEDs (Neopixels) with ESP32
- MicroPython: WS2812B Addressable RGB LEDs with ESP32 and ESP8266
Learn more about the ESP32 with our resources:
We hope you enjoyed this project and learned something new.
Thanks for reading.





A very nice set of examples is in this tutorial. Nice work! Thanks!