Here is just one implimentation of using the ESP32 as a WebServer.

The example uses an ESP32 as a Webserver to service a Webpage which contains a matrix of switch buttons. One GPIO of the ESP32 controls a string of 100 Neopixels arranged in a 10x10 matrix just like the Webpage.

By clicking the mouse on the Webpage matrix an event sends a HTML header back to the ESP32, this header contains the position of the button pressed, which in turn toggles the particular Neopixel in the string.

I am using this as a Web Gui to set up the cells "manually" for my rendition of "Conways Life", makes for an easy visual user interface.

When the ESP32 connects to the WiFi network it outputs an IP address on the serial console, this IP address can then be used to point your various browsers etcetcetc too.

Important code bits to note are these :-

client.print("<a href=\"");client.print((y*10)+x); client.print("\">&#9898; </a>

This assigns simple characters to the webpage giving each one a unique reference number. In our case 0-99 for 10 rows of 10 columns for the 10x10 Neo matrix

GETbar5=header.charAt(5)-48; GETbar6=header.charAt(6)-48;

The Client poll using the GET command sends back a header string, luckily for us our data is at the front of the Queue so is easily extracted by picking the (mouse pressed character position 0-99)) number out of the string.

 

Complete code

#include <FastLED.h>
#define DATA_PIN    12       // change to your data pin
#define COLOR_ORDER GRB      // RGB order
#define NUM_LEDS    101       // number of LEDs in your neo matrix
#define LED_TYPE    WS2811
#define BRIGHTNESS 16
CRGB leds[NUM_LEDS];  //LED Array to store values
#define DELAY 150
#define SIZE 10
byte world[SIZE][SIZE][2]; // working array N.B. I designed this for "Conways Life" hence the strange declaration.
long density = 20;
int colour;

#include <WiFi.h>
int GETbar5;int GETbar6;int GETbarV;  // variables for extracting X,Y coords

const char* ssid     = "Your router id";
const char* password = "Your router password";
WiFiServer server(80);

String header; // Variable for HTTP request

void setup() {
  Serial.begin(115200);
   
 FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS);
 FastLED.setBrightness(63);
 blank(); FastLED.show();

dispneo();
  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void loop(){
  WiFiClient client = server.available();   // Listen for clients

  if (client) {                             // If a new client connects,
    //Serial.println("New Client.");        // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected()) {            // loop while the client's connected
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
           
           //In this example we are setting up a webserver (webpage) with a 10x10 matrix
           //When you select one of the cells in the matrix it outputs it to HTTP header
           //This header can be parsed after reading it into a string buffer
           // below I am using just the data from header position 5 and 6 , this relates to the referenced number of the mouse clicked character.
           // this can just as easily be data for Switching LEDs/servo positions etcetcetc
            GETbar5=header.charAt(5)-48; GETbar6=header.charAt(6)-48;

           // -48 converts the ascii character into a number value (i.e. 48("0") to 59("9") to numeric 0 to 9), some jiggery pokery has to be done with numbers less than 10 due to no leading zeros, hence 2 line to account for this below. (-16 = a space detected i.e. originally ascii space 32(-48)=-16)
            if ((GETbar6==-16)&&(GETbar5>=0)){GETbarV=GETbar5;}
            if (GETbar6!=-16){GETbarV=(GETbar5*10)+GETbar6;}
           
            world[GETbarV-(GETbarV/10)*10][GETbarV/10][0] = 1-world[GETbarV-(GETbarV/10)*10][GETbarV/10][0]; // invert the cell if ON switch OFF , if OFF swich ON (so you can draw onto the matrix real time)
                   
            // Display the HTML web page
            client.println("<!DOCTYPE html>");
            client.print("<html>");
             client.print("<head>");
             client.print("<style>h1{text-decoration: none;}</style>"); //format without any underline/crossthough/overlines
             client.print("<style>a{text-decoration: none;}</style>");
             client.print("<h1>ESP32 MRL Matrix</h1>"); // Title
            client.print("<span style='font-size:50px;';text-decoration: line-through;>"); // Size of following Dots
     
for (int y =0; y < 10; y++) { // webpage output for a 10x10 grid of simple characters
for (int x =0; x < 10; x++) {
  if (world[x][y][0]==0){client.print("<a href=\"");client.print((y*10)+x); client.print("\">&#9898; </a>");}   // 9898 This is just an empty circle character
  if (world[x][y][0]==1){client.print("<a href=\"");client.print((y*10)+x); client.print("\">&#9899; </a>");}   // 9899 This is just an filled circle character
     }
     client.print("<br>"); // newline on webpage
}
client.println("</style></span>");
           client.println("</body></html>");
           client.println();
            break; // Break out loop
          } else {currentLine = "";   }              // if newline character clear currentLine
          } else if (c != '\r') {currentLine += c; } // if you got anything else add it to the end until carriage return
       }
      }
      header = ""; // Clear the header variable
      client.stop();// Close the connection
  }
 displayoutput();  // Display latest Neo pixel screen

  delay(DELAY);
}
void displayoutput(){
    for (int y = 0; y < SIZE; y++) {
    for (int x = 0; x < SIZE; x++) {
       if (world[x][y][0]>0){leds[(y*10)+x]=CRGB::Magenta; }
        else {leds[(y*10)+x]=CRGB::Black;}
        }
   }
       FastLED.show();
}

void blank(){
  for (int y = 0; y < SIZE; y++) {
    for (int x = 0; x < SIZE; x++) { leds[(y*10)+x]=0; }
    }
}  

void dispneo()
{
  for (int y =0; y < 10; y++) {
   for (int x =0; x < 10; x++) {
     if (world[x][y][0]==0){;leds[(y*10)+x]=0;;}   // 9899
     if (world[x][y][0]==1){;leds[(y*10)+x]=CRGB::Green;;;}   // 9899
      }
   }
 FastLED.show();
}