In this project, we’ll use Myduino AIoT Education Kit with an built-in 8×8 LED Matrix to create a web-based drawing board. You’ll be able to draw your own patterns directly in a web browser and then display them live on the matrix! Perfect for students, makers, or IoT beginners. Let’s go from zero to hero.
Objective

In this project, you’ll learn how to control and draw on an 8×8 LED Dot Matrix using an ESP32 and a web browser. By the end of the tutorial, you will be able to:
- Connect your ESP32 to WiFi and access a web-based drawing tool.
- Design your own custom pixel art on an 8×8 LED matrix.
- Switch between Dark Mode / Light Mode for a cool UI experience.
- Generate and copy bitmap code from your drawings for your next projects.
- Understand how web technologies (HTML, CSS, JavaScript) interact with ESP32 to control hardware in real time.
In short: You’ll not only light up the matrix, but also learn how IoT turns hardware into interactive art!
Circuit Connections

| ESP32 Dev Module Board | 8×8 Dot Matrix |
| IO5 | CS |
| IO18 | CLK |
| IO23 | DIN |
Logic Flow
- ESP32 connnects to WiFi and launches a webserver
- The webpage loads with and 8×8 pixel grid
- You draw by clicking squares (ON/OFF)
- When you press Update, the pattern is sent to ESP32
- The ESP32 displays it on the 8×8 LED Matrix
- Extra features:
- Clear / Fill buttons
- Dark / Light theme toggle
- Copy bitmap array (C code format)
Code Lab
Step 1: Install Library
Before you upload the code, you need to install the libraries first. Go to the sidebar and click Library Manager. Search for the library listed below. Sometimes the file may not appear at the top of the results, so you might need to scroll down until you found the library you need.
- WiFi.h (already include in ESP32)
- Web_Server.h (already include in ESP32)
- MD_MAX72xx.h (MD_MAX72XX by majicDesigns)
- SPI.h (already include in ESP32)

Step 2: Code
Copy and paste the code in Arduino IDE
#include <WiFi.h>
#include <WebServer.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
// ====== WIFI CONFIG ======
const char* ssid = "YOUR_WIFI_NAME";
const char* password = "YOUR_WIFI_PASSWORD";
// ====== MATRIX CONFIG ======
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 1
#define CS_PIN 5
MD_MAX72XX matrix = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// ====== WEBSERVER ======
WebServer server(80);
// Store 8x8 pattern
bool pattern[8][8]; // x = 8, y = 8
// HERE IS WHERE THE WEB SERVER STARTED
String htmlPage() {
String html = R"rawliteral(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Myduino AIoT Education Kit 8x8 LED Matrix</title>
<style>
:root {
--bg-color: #f9f9f9;
--text-color: #222;
--cell-off: #ffffff;
--cell-on: #222222;
--btn-bg: #007bff;
--btn-text: #ffffff;
--btn-hover: #0056b3;
--card-bg: #ffffff;
--border-color: #ccc;
}
body.dark {
--bg-color: #1e1e1e;
--text-color: #f9f9f9;
--cell-off: #333333;
--cell-on: #00ff99;
--btn-bg: #00cc88;
--btn-text: #000000;
--btn-hover: #009966;
--card-bg: #2a2a2a;
--border-color: #444;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: var(--bg-color);
color: var(--text-color);
margin: 0; padding: 0;
display: flex; flex-direction: column;
align-items: center; justify-content: flex-start;
min-height: 100vh;
}
h2 { margin: 20px 0; font-size: 28px; }
.logo {
margin-top: 20px;
margin-bottom: 10px;
}
.logo img {
max-width: 250px;
height: auto;
}
.card {
background: var(--card-bg);
padding: 20px;
border-radius: 16px;
box-shadow: 0 8px 20px rgba(0,0,0,0.2);
text-align: center;
}
table { margin: 20px auto; border-collapse: collapse; }
td {
width: 30px; height: 30px;
border: 1px solid var(--border-color);
cursor: pointer;
transition: background 0.3s, transform 0.1s;
}
td.off { background: var(--cell-off); }
td.on { background: var(--cell-on); }
td:hover { transform: scale(1.1); }
.btn {
margin: 10px 5px;
padding: 10px 20px;
font-size: 16px;
border: none;
border-radius: 8px;
cursor: pointer;
background: var(--btn-bg);
color: var(--btn-text);
transition: background 0.3s, transform 0.1s;
}
.btn:hover { background: var(--btn-hover); transform: scale(1.05); }
textarea {
width: 90%;
height: 150px;
margin-top: 15px;
padding: 10px;
border-radius: 8px;
border: 1px solid var(--border-color);
font-family: monospace;
font-size: 14px;
background: var(--card-bg);
color: var(--text-color);
}
footer {
margin-top: auto;
padding: 15px;
font-size: 14px;
opacity: 0.7;
}
</style>
</head>
<body>
<div class="logo">
<img src="https://i0.wp.com/myduino.com/wp-content/uploads/2025/05/myduino_logo_a_white_bg-1.png?fit=3639%2C1000&ssl=1" alt="Myduino Logo">
</div>
<h2>8x8 LED Matrix Controller</h2>
<div class="card">
<table id="matrix"></table>
<div>
<button class="btn" onclick="updateMatrix()">Update Matrix</button>
<button class="btn" onclick="clearMatrix()">Clear</button>
<button class="btn" onclick="fillMatrix()">Fill</button>
<button class="btn" onclick="toggleTheme()">🌙 / ☀️</button>
<button class="btn" onclick="generateBitmap()">Copy Bitmap</button>
</div>
<textarea id="bitmapOutput" readonly></textarea>
</div>
<footer>Myduino Team | Powered by Myduino AIoT Education Kit</footer>
<script>
const table = document.getElementById('matrix');
for (let y=0; y<8; y++) {
const row = document.createElement('tr');
for (let x=0; x<8; x++) {
const cell = document.createElement('td');
cell.className = 'off';
cell.onclick = () => {
cell.className = (cell.className === 'off') ? 'on' : 'off';
};
row.appendChild(cell);
}
table.appendChild(row);
}
function updateMatrix() {
let data = "";
for (let y=0; y<8; y++) {
for (let x=0; x<8; x++) {
data += (table.rows[y].cells[x].className === 'on') ? "1" : "0";
}
}
fetch("/update?data=" + data);
}
function clearMatrix() {
for (let y=0; y<8; y++) {
for (let x=0; x<8; x++) {
table.rows[y].cells[x].className = 'off';
}
}
updateMatrix();
}
function fillMatrix() {
for (let y=0; y<8; y++) {
for (let x=0; x<8; x++) {
table.rows[y].cells[x].className = 'on';
}
}
updateMatrix();
}
// Dark/Light Mode Toggle
function toggleTheme() {
document.body.classList.toggle("dark");
}
// 🖨 Generate C Bitmap
function generateBitmap() {
let lines = [];
for (let y=0; y<8; y++) {
let rowBits = "";
for (let x=0; x<8; x++) {
rowBits += (table.rows[y].cells[x].className === 'on') ? "1" : "0";
}
let byteValue = "B" + rowBits;
lines.push(" " + byteValue + ",");
}
let output = "byte myBitmap[8] = {\n" + lines.join("\n") + "\n};";
document.getElementById("bitmapOutput").value = output;
// Copy to clipboard automatically
navigator.clipboard.writeText(output).then(() => {
alert("✅ Bitmap copied to clipboard!");
});
}
// Force start at top (logo first)
window.onload = function() {
window.scrollTo(0, 0);
}
</script>
</body>
</html>
)rawliteral";
return html;
}
void handleRoot() {
server.send(200, "text/html", htmlPage());
}
// ====== MATRIX DRAW ======
// Don't adjusting here or your Dot Matrix will not same as Web Server
void drawPattern() {
matrix.clear();
for (int y=0; y<8; y++) {
for (int x=0; x<8; x++) {
matrix.setPoint(7 - x, 7 - y, pattern[y][x]); //
}
}
matrix.update();
}
void handleUpdate() {
if (server.hasArg("data")) {
String data = server.arg("data");
if (data.length() == 64) {
int idx = 0;
for (int y=0; y<8; y++) {
for (int x=0; x<8; x++) {
pattern[y][x] = (data[idx++] == '1');
}
}
drawPattern();
}
}
server.send(200, "text/plain", "OK");
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
matrix.begin();
matrix.clear();
server.on("/", handleRoot);
server.on("/update", handleUpdate);
server.begin();
Serial.println("Webserver started.");
}
void loop() {
server.handleClient();
}
Before you upload the code, make sure to insert your WiFi Name and WiFi Password correctly or it will not connect. When you sure it’s correct, then just upload the code.

Step 3: Drawing Web Server
- After done upload, open the Serial Monitor and copy your IP Address.
- Paste your IP Address in your browser such as Google.

Step 4: Draw your first design
- 8×8 grid: Click the boxes to turn ON/OFF
- Update button: Send design to ESP32
- Clear button: Reset the matrix
- Fill button: Turn all LED’s ON
- Dark/Light Mode Toggle
- Copy bitmap button: copy the C array of your drawing for your next projects


System Check
Here’s how it will be if it’s working properly:
- The web app loads in your browser
- Clicking pixels updates the 8×8 Dot Matrix
- Copy bitmap button generates Arduino array code
- Dark/Light mode toggle changes theme
If something fails:
- Check wiring (DIN > 23, CS > 5, CLK > 18)
- Verify WiFi credentials are correct
- Make sure ESP32 is on the same network as your device
Troubleshooting Guide
| Problem | Solutions |
| Webpage not loading | Check Serial Monitor for ESP32 IP Address |
| Matrix shows random pixels | Verify wiring of DIN/CS/CLK |
| No WiFi connection | Double check SSID & Password |
| Copy bitmap not working | Ensure JavaScript buttons are enabled |
Buy from:
Myduino AIoT Education Kit from Myduino.com






