Most ESP32 web projects serve a small control page with a few buttons. In this project I pushed that idea further: the ESP32 hosts LogicLab Pro, a complete digital logic circuit simulator with an AI-assisted chat panel. Anyone on the same Wi-Fi network can open the ESP32's address in a browser and start building and testing logic circuits on a phone, tablet or PC.
The interesting part is how the web app gets onto the chip. The original single-file HTML app is about 128 KB. I compress it with gzip (about 32 KB), convert it into a C array, store it in flash with PROGMEM, and let the ESP32's built-in WebServer send it with a Content-Encoding: gzip header. The browser decompresses it, so the page loads quickly and the ESP32 does no compression work at runtime.
- One file to deploy. The whole web app lives inside the sketch. There is no filesystem to upload and no SD card to manage.
- Small footprint. Gzip cuts the app from 127, 930 bytes to 32, 125 bytes.
- No third-party libraries. The sketch uses only
WiFi,WebServerandESPmDNS, which ship with the ESP32 Arduino core. - Easy to update. Edit the HTML, run one Python command, re-upload.
logiclab_pro_v2.htmlis the original, unmodified web app.tools/embed_html.pygzips it and writesindex_html.h, a C header holding the bytes as aPROGMEMarray.- The sketch connects to your Wi-Fi (or starts its own access point as a fallback).
- When a browser requests
/, the ESP32 streams the gzip bytes with the right headers. - The browser unpacks the page and runs the simulator entirely on the client side. The ESP32 only delivers the file.
Hardware
- Any ESP32 development board (for example ESP32 DevKit)
- Micro-USB or USB-C data cable, depending on the board
Software
- Arduino IDE 2.x
- ESP32 Arduino core (version 2.x or 3.x) by Espressif
- Python 3 (only needed if you want to modify the web app)
LogicLabPro_ESP32/
├─ LogicLabPro_ESP32.ino main sketch
├─ config.h Wi-Fi and hostname settings
├─ index_html.h gzip-compressed web app (auto-generated)
├─ webapp/logiclab_pro_v2.html the original web app
└─ tools/embed_html.py HTML -> index_html.h converterStep-by-step buildStep 1: Install the ESP32 core- Open Arduino IDE and go to File > Preferences.
- In Additional boards manager URLs, add Espressif's package index URL (listed in the ESP32 Arduino core documentation).
- Open Tools > Board > Boards Manager, search for esp32 and install the package by Espressif Systems.
- Select your board under Tools > Board (for example ESP32 Dev Module). The default partition scheme is fine.
Download the project folder (GitHub link: [add your repository link]) and open LogicLabPro_ESP32.ino in Arduino IDE. The folder name must match the sketch name, so keep LogicLabPro_ESP32.
Open config.h and set your network name and password:
#define WIFI_SSID "YOUR_WIFI_SSID"
#define WIFI_PASS "YOUR_WIFI_PASSWORD"
#define WIFI_TIMEOUT_MS 15000
#define AP_SSID "LogicLab-ESP32"
#define AP_PASS "logiclab123"
#define HOSTNAME "logiclab"If the ESP32 cannot join your network within the timeout, it starts its own access point named LogicLab-ESP32 and the app is available at 192.168.4.1.
Connect the board, choose the correct port under Tools > Port, and click Upload. If the upload hangs on "Connecting...", hold the board's BOOT button until it starts.
Step 5: Open the appOpen Serial Monitor at 115200 baud. After a successful connection you will see something like:
=== LogicLab Pro on ESP32 ===
[WiFi] Connected. IP: 192.168.x.x
[mDNS] http://logiclab.local
[HTTP] Open: http://192.168.x.xType that address (or http://logiclab.local) into a browser on any device on the same network. LogicLab Pro loads.
[Add a photo or screenshot of the Serial Monitor output and the simulator running on a phone.]
Code walkthroughConverting the HTMLThe core of embed_html.py is just a few lines:
import gzip, pathlib
raw = pathlib.Path("webapp/logiclab_pro_v2.html").read_bytes()
gz = gzip.compress(raw, compresslevel=9, mtime=0)
# ...then write the bytes out as a C array:
# const uint8_t INDEX_HTML_GZ[] PROGMEM = { 0x1f, 0x8b, ... };
# const size_t INDEX_HTML_GZ_LEN = 32125;Run it from the project folder:
python3 tools/embed_html.pyServing the pagevoid handleRoot() {
server.sendHeader("Content-Encoding", "gzip");
server.sendHeader("Cache-Control", "no-cache");
server.send_P(200, "text/html; charset=utf-8",
(PGM_P)INDEX_HTML_GZ, INDEX_HTML_GZ_LEN);
}The Content-Encoding: gzip header tells the browser to decompress the data. send_P reads straight from flash, so the 32 KB array does not use RAM.
if (connectStation()) {
Serial.printf("[WiFi] Connected. IP: %s\n", WiFi.localIP().toString().c_str());
} else {
startAccessPoint(); // SSID: LogicLab-ESP32
}
if (MDNS.begin(HOSTNAME)) {
MDNS.addService("http", "tcp", HTTP_PORT);
}
server.on("/", HTTP_GET, handleRoot);
server.on("/index.html", HTTP_GET, handleRoot);
server.on("/health", HTTP_GET, handleHealth); // JSON status
server.onNotFound(handleNotFound); // redirect to /
server.begin();The /health endpoint returns the mode (station or AP), IP address, uptime and free heap as JSON, which is handy for quick debugging.
LogicLab Pro loads Tailwind CSS, Google Fonts and the Gemini API from the internet. The ESP32 only serves the HTML; your browser fetches those resources. That means:
- Station mode (ESP32 joins your router or phone hotspot): everything works, as long as your device has internet.
- Access-point mode: the client has no internet connection, so styling and the AI chat will not load.
The AI chat also needs your own Gemini API key. The app asks for it in the browser and stores it in that browser's local storage, so the key never passes through the ESP32. Local storage is tied to the address you use, so if the ESP32's IP changes you may need to enter the key again. Using http://logiclab.local avoids that.
- Edit
webapp/logiclab_pro_v2.html. - Run
python3 tools/embed_html.py. - Re-upload the sketch.
- Page loads but looks unstyled: the browser cannot reach the Tailwind CDN. Check that the client device has internet, or connect the ESP32 to a network that does.
logiclab.localdoes not resolve: mDNS support varies by operating system and network. Use the IP address from the Serial Monitor.- ESP32 starts in AP mode unexpectedly: double-check the SSID and password in
config.h. The ESP32 connects only to 2.4 GHz networks. - Compile error about a missing header: make sure
index_html.handconfig.hare in the same folder as the.inofile.
- Bundle Tailwind and the fonts locally (for example on LittleFS) for a fully offline setup.
- Add a WebSocket bridge so the simulated circuit can read and drive real ESP32 GPIO pins.
- Add OTA updates so you can change the web app without a USB cable.
- Add a simple captive portal for the access-point mode.
With a gzip-compressed C array and the stock WebServer class, an ESP32 can host a surprisingly capable web application. The same technique works for dashboards, configuration pages and educational tools. Try it with your own single-file web app and share what you build.
Explanations about web application performance



Comments