Free & Open Source · v1.0 · Powered by esp-flash-button
Flash ESP32 firmware directly from the browser
One script tag. One custom element. Your users flash ESP32, ESP8266, and ESP32-S3 devices in seconds — no boot button, no IDE, no drivers, no server uploads.
Flasher modal theme — dark vs light (click to open)
Dark Theme
Light Theme
All button themes — each has a unique visual style
Red
Dark
Green
Light
Ghost
Minimal
AI Assistant
Give this full context to any AI
Copy the complete technical specification below and paste it to ChatGPT, Claude, Gemini, or any AI before asking coding or troubleshooting questions about this project.
ESP Flash Button — Complete Project Specification
This prompt contains the full technical specification. Paste into any AI before asking coding or troubleshooting questions.
ESP Flash Button is a free, open-source, single-file web component (IIFE pattern, ~2280+ lines) that lets website visitors flash ESP32/ESP8266/ESP32-S3 microcontrollers directly from the browser using the Web Serial API and esptool-js (loaded dynamically from unpkg CDN). No build step, no npm, no framework required. Deployed on Cloudflare Pages at esp-flash-button.pages.dev.
ARCHITECTURE:
- Shadow DOM used ONLY for the trigger button; the modal dialog and serial monitor are injected into document.body
- CSS variables for full theming: dark theme by default, light theme override via :root[data-theme="light"]
- All CSS variables are defined on :root, .__efb-overlay, .__efb-sm-overlay, .__efb-sm-modal, and .__efb-modal selectors for reliable inheritance
- esptool-js loaded on first click (lazy import from unpkg), stored in module-level cache
- MD5 implementation built-in (RFC 1321, Paul Johnston/blueimp) for firmware verification
- No telemetry, no tracking, no server uploads — fully local flashing
ATTRIBUTES (observed by the component):
manifest (required) — Absolute URL to firmware manifest JSON. Must be served with CORS headers.
label — Button label text (default: "Install Firmware"). Ignored when using slotted custom button via [slot="activate"].
theme — Button visual style. One of: red, dark, green, light, ghost, minimal. (default: "red")
erase-first — Boolean attribute. When present, erases all flash before writing. Also togglable as a checkbox in UI.
baud — Flash baud rate (default: 460800). Max 921600. Automatically reduced to 115200 for ESP8266/ESP8285 to prevent brownout.
BUTTON THEMES (Shadow DOM CSS — SHADOW_CSS const):
red (default): Bold pill shape, border-radius 100px, linear-gradient(135deg,#e03030,#b81f1f), font-weight 700, box-shadow 0 2px 12px rgba(224,48,48,0.35)
dark: Rectangular, border-radius 6px, 2px solid #2a2a2e border, linear-gradient(135deg,#1a1a1e,#111114), font-weight 600
green: Rounded, border-radius 12px, linear-gradient(135deg,#1a8c4e,#15703e), box-shadow ring 0 0 0 1px rgba(61,214,140,0.15), font-weight 600
light: Compact, border-radius 8px, linear-gradient(135deg,#3b82f6,#1d4ed8), font-size 13px font-weight 500
ghost: Dashed outline, border 1.5px dashed rgba(224,48,48,0.5), transparent background, red text, font-weight 500
minimal: Small underlined text, border-radius 4px, padding 6px 12px, font-size 12px font-weight 400, color #888 with underline, hover turns red
All themes share: transition on transform/box-shadow/background, hover lift effect, disabled state with opacity 0.45, ripple animation on click, spinner during loading
MODAL CSS — COMPLETE VARIABLE SYSTEM (_getModalCSS method, returns CSS string injected into document.head):
Dark theme defined on :root + overlay/modal selectors. Light theme via :root[data-theme="light"] override.
LAYOUT: .__efb-overlay (fixed inset, z-index 2147483646, flex center, backdrop-filter blur 8px), .__efb-modal (500px wide, max-height 90vh, border-radius 18px, slide-up animation)
HEADER: .__efb-header (flex row, 18px 20px padding, border-bottom), .__efb-hicon (40px, border-radius 10px, gradient bg), .__efb-htitle (15px, font-weight 700), .__efb-hsub (11px, lower opacity), .__efb-hclose (30px, border-radius 7px, hover effects)
BODY: .__efb-body (padding 18px 20px, overflow-y auto, flex column gap 12px, thin scrollbar)
FOOTER: .__efb-footer (padding 10px 20px, flex space-between), .__efb-credit (10px), .__efb-btnrow (flex gap 8px, wrap, justify-end)
BUTTONS: .__efb-btn (default: inline-flex, gap 6px, padding 8px 16px, border-radius 8px, font 13px font-weight 600), .__efb-btn.primary (accent gradient + shadow), .__efb-btn.ghost (transparent with border), .__efb-btn.success-btn (green gradient), .__efb-btn.change-port-btn (minimal), .__efb-btn.serial-btn (accent colored border)
FIRMWARE CARD: .__efb-fwcard-wrap (border-radius 12px, padding 14px 16px, flex column), .__efb-fwicon (36px, border-radius 9px, gradient), .__efb-fwname (14px, font-weight 600), .__efb-fwmeta (11px), .__efb-fwbadge (10px, green pill)
VARIANTS: .__efb-vlabel (10px, uppercase), .__efb-vchip (chip selector, padding 5px 12px, border-radius 7px, .selected state with accent border), .__efb-vchip.detected (green border)
STATUS: .__efb-srow (flex row, padding 11px 14px, border-radius 10px), .__efb-sdot (7px dot, states: green/red/amber with glow animations), .__efb-stext (13px)
CHIP PILLS: .__efb-pills (flex wrap, gap 7px), .__efb-pill (border-radius 20px, padding 4px 10px, .hi .pv state)
PROGRESS: .__efb-prog (border-radius 10px, padding 12px, flex column), .__efb-prog-track (height 4px, border-radius 100px), .__efb-prog-fill (gradient, width animated via JS), .__efb-prog-fill.done (turns green)
LOG: .__efb-log (border-radius 9px, font JetBrains Mono 11px, padding 10px 12px, max-height 140px), .__efb-ll types: info/success/warn/error/accent/dim
SUCCESS: .__efb-succ (border-radius 10px), .__efb-checkmark SVG with stroke-dasharray animation, .__efb-succ-meta (chip, flash size, duration, file count), .__efb-succ-md5 (per-file MD5 hashes)
ERROR: .__efb-errbox (red-tinted background, border-radius 10px), .__efb-errtitle (bold red), .__efb-errhints list
CHECKBOX: .__efb-checkrow (flex row, border-radius 9px, cursor pointer), .__efb-checkbox (16px, border-radius 4px, .checked state with accent gradient fill)
SERIAL MONITOR LINK: .__efb-sm-row (flex row, 11px font, hover effects with purple-ish tint)
SERIAL MONITOR MODAL: .__efb-sm-overlay (z-index 2147483647, backdrop-filter blur 8px), .__efb-sm-modal (560px wide, 520px height, max-height 90vh, border-radius 16px)
Header: .__efb-sm-hicon (34px, rounded 8px, green icon), .__efb-sm-htitle (14px, font-weight 700), .__efb-sm-hsub (11px, green, dot indicator), .__efb-sm-hclose (28px, rounded 7px)
Toolbar: .__efb-sm-toolbar (flex row, padding 8px 18px, border-bottom), .__efb-sm-label (10px, uppercase), .__efb-sm-select (baud/line-ending selector), .__efb-sm-tbtn (tool buttons: DTR, RST, Clear), .__efb-sm-tbtn.active (green), .__efb-sm-tbtn-clear (red-tinted)
Output: .__efb-sm-output (flex 1, overflow-y auto, font JetBrains Mono 11.5px, line-height 1.7, background bg), .__efb-sm-line types: .rx (received data, #c0c8d0), .tx (transmitted, #f59e0b amber), .sys (system, #404048 italic), .err (error, #e05050 red)
Input row: .__efb-sm-input-row (flex, padding 12px 16px), .__efb-sm-input (flex 1, rounded 8px, dark bg), .__efb-sm-send (red gradient button)
Footer: .__efb-sm-footer (flex space-between, padding 8px 16px), .__efb-sm-count (10px, monospace)
CSS VARIABLE NAMES (complete list): --efb-overlay, --efb-modal-bg, --efb-text, --efb-text2, --efb-text3, --efb-text4, --efb-text5, --efb-border, --efb-border2, --efb-border3, --efb-border4, --efb-surface, --efb-surface2, --efb-surface3, --efb-surface4, --efb-surface5, --efb-accent (#e03030), --efb-accent-dark (#b81f1f), --efb-accent-hover (#ff4a40), --efb-accent-dark2 (#a01a1a), --efb-accent-bg, --efb-accent-border, --efb-accent-shadow, --efb-accent-shadow2, --efb-green (#3dd68c), --efb-green-bg, --efb-green-border, --efb-green-glow, --efb-amber (#f59e0b), --efb-amber-glow, --efb-error (#e05050), --efb-error-border, --efb-error-bg, --efb-ghost-bg, --efb-ghost-border, --efb-ghost-hover-bg, --efb-ghost-hover-text, --efb-ghost-hover-border, --efb-ghost-text, --efb-ghost-cp-text, --efb-ghost-cp-border, --efb-ghost-cp-hover-text, --efb-ghost-cp-hover-border, --efb-ghost-cp-hover-bg, --efb-log-bg, --efb-log-border, --efb-log-scroll, --efb-pill-bg, --efb-pill-border, --efb-pill-pl, --efb-pill-pv, --efb-prog-bg, --efb-prog-border, --efb-prog-txt, --efb-prog-track, --efb-prog-fill, --efb-prog-fill-done, --efb-success-bg, --efb-success-border, --efb-succ-title, --efb-succ-sub, --efb-succ-meta, --efb-succ-meta-span, --efb-succ-meta-dot, --efb-succ-md5-border, --efb-succ-md5-title, --efb-succ-md5-file, --efb-succ-md5-addr, --efb-succ-md5-hash, --efb-scrollbar, --efb-check-bg, --efb-check-border, --efb-checklabel, --efb-fwcard-bg, --efb-fwcard-border, --efb-fwicon-bg, --efb-fwicon-border, --efb-fwicon-color, --efb-fwcredit-border, --efb-fwcredit-text, --efb-fwcredit-link, --efb-fwcredit-hover, --efb-fwname, --efb-fwmeta, --efb-errtext, --efb-errhint-text, --efb-errhint-prefix, --efb-vlabel, --efb-vchip-bg, --efb-vchip-border, --efb-vchip-text, --efb-vchip-hover-border, --efb-vchip-hover-text, --efb-sm-overlay, --efb-sm-bg, --efb-sm-border, --efb-sm-shadow, --efb-sm-header-border, --efb-sm-toolbar-border, --efb-sm-label, --efb-sm-select-bg, --efb-sm-select-border, --efb-sm-select-text, --efb-sm-select-hover-border, --efb-sm-select-hover-text, --efb-sm-input-border, --efb-sm-input-bg, --efb-sm-input-text, --efb-sm-input-placeholder, --efb-sm-input-focus-border, --efb-sm-tbtn-bg, --efb-sm-tbtn-border, --efb-sm-tbtn-text, --efb-sm-tbtn-hover-text, --efb-sm-tbtn-hover-border, --efb-sm-tbtn-hover-bg, --efb-sm-tbtn-clear, --efb-sm-tbtn-clear-border, --efb-sm-tbtn-clear-hover, --efb-sm-tbtn-clear-hover-border, --efb-sm-tbtn-clear-hover-bg, --efb-sm-output-bg, --efb-sm-output-scroll, --efb-sm-line-rx, --efb-sm-line-tx, --efb-sm-line-sys, --efb-sm-line-err, --efb-sm-empty, --efb-sm-input-row-bg, --efb-sm-input-row-border, --efb-sm-footer-bg, --efb-sm-footer-border, --efb-sm-footer-credit, --efb-sm-footer-link, --efb-sm-count, --efb-htitle, --efb-hsub, --efb-hclose-bg, --efb-hclose-text, --efb-hclose-hover-bg, --efb-hclose-hover-text, --efb-hicon-bg, --efb-hicon-border, --efb-hicon-color, --efb-credit-text, --efb-credit-link, --efb-credit-hover, --efb-stext, --efb-stext-strong, --efb-sdot, --efb-sm-hicon-bg, --efb-sm-hicon-border, --efb-sm-hicon-color, --efb-sm-htitle, --efb-sm-hsub, --efb-sm-hclose-bg, --efb-sm-hclose-text, --efb-sm-hclose-hover-bg, --efb-sm-hclose-hover-text, --efb-sm-footer-credit, --efb-sm-footer-link, --efb-sm-send-bg, --efb-sm-send-hover-bg, --efb-sm-send-text, --efb-sm-dot-bg, --efb-sm-dot-glow, --efb-sm-row-border, --efb-sm-row-bg, --efb-sm-row-text, --efb-sm-row-hover-border, --efb-sm-row-hover-bg, --efb-sm-row-hover-text
LIGHT THEME OVERRIDE: All vars redefined under :root[data-theme="light"] with lighter values (white modal bg, slate text, blue accent, light borders). Applied by setting data-theme="light" on the overlay and propagating to :root.
SERIAL MONITOR — FULL SPECIFICATION:
Opens from success panel after flash, or from serial monitor link row after chip detection
When opening: hides main modal overlay (display:none), propagates data-theme to :root, creates separate .__efb-sm-overlay at z-index 2147483647
BAUD SELECTION: dropdown with options 9600, 19200, 38400, 57600, 74880, 115200 (default), 230400, 460800, 921600. Changing baud stops and restarts read loop.
LINE ENDINGS: None, CR+LF (default), LF, CR
DTR TOGGLE: Toggles Data Terminal Ready signal. Shows [DTR ON/OFF] in output.
RST BUTTON: Sends DTR+RTS pulse sequence (DTR high, 100ms, RTS high, 50ms, both low) — triggers hardware reset on most ESP32 boards. Shows [RST pulse sent] in output.
CLEAR: Removes all output lines, resets line counter.
SEND: TextEncoder writes value + selected EOL to serial writable stream. Prefixed with "> " in output.
READ LOOP: Uses ReadableStream getReader() with TextDecoder stream mode, buffers partial lines, splits on newlines, limits to 500 lines in DOM. Handles errors gracefully with [Read error] messages.
PORT MANAGEMENT: On close, cancels reader, releases writer lock, closes port. On reconnect for flash, waits 200ms, force-closes port, explicitly re-opens at detect baud.
LINE COUNT: tracks total lines displayed, shown in footer
FLASH SEQUENCE — COMPLETE FLOW:
1. User clicks button -> _onButtonClick -> _openModal
2. _openModal: injects CSS into head, creates overlay+modal, calls _loadManifest
3. _loadManifest: fetches manifest JSON, validates "builds" array, calls _showReadyPanel
4. _showReadyPanel: renders firmware card, erase checkbox, status row, footer with Cancel + Connect
5. _connectAndFlash: navigator.serial.requestPort(), port disconnect listener, calls _detectChip
6. _detectChip: creates Transport + ESPLoader, calls loader.main() for chip detection, flash_id for size, auto-selects variant, shows _showFlashConfirm
7. _showFlashConfirm: shows Flash Firmware button + Serial Monitor link, stores detect state
8. _executeFlash: fetches all .bin files, calls loader.writeFlash with progress callback, MD5 calculation, hard reset
9. _showSuccess: displays checkmark animation, chip info, MD5 hashes, footer with Monitor + Close + Refresh buttons
10. Confetti celebration animation plays (80 particles flying outward from center with random colors)
RECONNECTION LOGIC: After serial monitor closes, _reconnectForFlash() disconnects transport, waits 200ms, force-closes port, re-opens at detect baud, creates new Transport+ESPLoader, re-runs loader.main() for bootloader re-entry. Handles ESP8266 baud reduction.
ERROR HANDLING: Connection failed, port selection cancelled, flash aborted by user, fetch failure, write failure. Each shows specific error box with troubleshooting hints. DOM events flash-success and flash-error dispatched.
CELEBRATION: After flash success, 80 confetti particles burst from center with randomized: angle, distance (100-350px), color (red/green/amber/blue/purple/pink/cyan), size (4-12px), shape (circle or square), delay (0-0.3s), duration (0.6-1.2s). Particles fly outward with rotation and fade.
BUTTON RIPPLE: All .__efb-btn clicks trigger a ripple effect (white circle scales from 0 to 4x with fade, 0.5s animation, then removed).
MANIFEST JSON FORMAT:
{
"name": "Firmware Name",
"version": "1.0.0",
"description": "Optional description",
"newImage": true/false,
"builds": [
{
"chipFamily": "ESP32",
"parts": [{"path": "firmware.bin", "offset": 0}]
}
]
}
chipFamily values: ESP32, ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6, ESP32-H2, ESP8266, ESP8285
Multi-chip manifests auto-select build matching detected chip family.
ESP8266/8285 automatically limited to 115200 baud.
DOM EVENTS: flash-success (detail: {chip, flashSize, duration, filesCount}), flash-error (detail: {title, message}). Both bubble and composed.
SUPPORTED USB-SERIAL CHIPS: CP2102, CP2104, CH340, CH341, FTDI FT232R, CDC-ACM. Requires Chrome 89+/Edge 89+/Opera 76+ on desktop.
DEPLOYMENT: wrangler pages deploy . --project-name esp-flash-button --branch main. Production URL: esp-flash-button.pages.dev. Script loaded from: https://esp-flash-button.pages.dev/esp-flash-button.js
LANDING PAGE: index.html at project root. Contains hero, demo (live component + theme cards), AI prompt section, usage guide, features grid, serial monitor simulation (with animated boot sequence + live output), comparison table, FAQ accordion, supported chips, CORS setup guide. Includes custom cursor with trailing particles, circuit board background animation, scroll-reveal animations, code copy buttons.
MODAL THEME: When button theme="light" is used, the modal and serial monitor render with a light theme (white backgrounds, blue accent #3b82f6, slate text). All other button themes (red/dark/green/ghost/minimal) use the dark modal theme (dark backgrounds, red accent #e03030). The modal theme switch is controlled by data-theme="light" on the overlay element, which triggers CSS variable overrides in the :root[data-theme="light"] section.
DEMO SECTION: Landing page demo shows both modal themes side by side — "Dark Theme" (theme="red" button) and "Light Theme" (theme="light" button). Users can click either to see the flasher modal in dark or light mode.
ANTI-COPY: Domain-lock prevents component initialization on unauthorized hosts (allows localhost, 192.168.*, 10.*, authorized domains). Copy event blocked on non-code elements. JS file is production-synced to /production/esp-flash-button.js.
Click to copy the full project specification
What is ESP Flash Button
The easiest way to let users flash your ESP firmware
An embeddable browser firmware installer built on the Web Serial API. No software installation, no IDE, no USB driver configuration.
Built for makers and product teams
Whether you sell dev boards, run a tutorial blog, or distribute firmware for your open-source project — embed the installer and let your users flash in one click. No build toolchain, no CDN account, no deployment pipeline.
100% private — local by design
Firmware files are fetched from your own server and written directly to the device in the browser. No data ever passes through a third-party server. Zero telemetry. Zero tracking. The device and your firmware never leave the user's machine.
Works everywhere on the modern web
A standard HTML web component. Deploy on WordPress, Ghost, GitHub Pages, Cloudflare Pages, or any static site host. No npm, no build step, no framework. One script tag and one custom element is all you need.
Significantly faster than alternatives
921600 baud flash speed with hardware compression. Automatic MD5 verification. Chip + MAC + flash size auto-detection. A 2 MB firmware completes in under 15 seconds — up to 10x faster than legacy browser tools running at 115200 baud.
How it works
From click to flashed firmware in under 15 seconds
The entire flash process runs in the browser — no server, no relay, no middleman.
01
User clicks the button
A modal opens showing firmware name, version, supported chip variants, and erase options. The user selects their ESP board from the browser's native serial port picker dialog. No configuration required.
02
Chip detection via bootloader
The SKR Flasher engine enters bootloader mode automatically via DTR/RTS signals — no BOOT button hold needed on standard boards. It identifies the chip family (ESP32 / ESP32-S3 / ESP8266), reads the MAC address, and detects installed flash size. The correct firmware variant from your manifest is auto-selected.
03
Firmware download and write
Firmware .bin files are fetched from your server, compressed in-browser, and written to flash memory with real-time progress and ETA. Every byte is verified with MD5 after writing — the hash is displayed in the success screen so users can confirm integrity.
04
Hard reset and success
The device is hard-reset via RTS to exit bootloader mode and boot the new firmware. A success screen shows chip info, flash duration, file count, and per-file MD5 hashes. From there, users can open the built-in Serial Monitor to verify their firmware is running correctly.
Quick start
Two lines of code
Drop these into any HTML page. No build step, no npm, no framework required.
1
Add the script tag
Load the component from Cloudflare CDN. One tag, zero dependencies, works in any HTML context.
<!-- Add once, anywhere in your page --><scriptsrc="https://esp-flash-button.pages.dev/esp-flash-button.js"></script>
2
Place the button element
Add the custom element wherever you want the flash button to appear. Point it at your firmware manifest JSON.
<esp-flash-buttonmanifest="https://yoursite.com/firmware/manifest.json"label="Install My Firmware"theme="red"></esp-flash-button>
3
Choose a button theme (optional)
Five built-in themes via the theme attribute: red (default), dark, green, ghost, and minimal. Or supply your own element with slot="activate".
<!-- Five built-in themes --><esp-flash-buttonmanifest="..."theme="green"></esp-flash-button><!-- Or bring your own button entirely --><esp-flash-buttonmanifest="..."><buttonslot="activate"class="my-btn">Flash Firmware</button></esp-flash-button>
4
Listen to flash events
React to success or failure with standard DOM events. Perfect for analytics, post-flash instructions, or confetti.
A simple JSON file that tells the component what firmware to flash and for which chips. Host it on your server alongside your .bin files.
Your server must send CORS headers so the browser can fetch the manifest and firmware files cross-origin. Add Access-Control-Allow-Origin: * to your server responses on the manifest and all .bin paths.
Single merged .bin — simplest, address 0x0
{"name": "My ESP32 Project",
"version": "1.0.0",
"description": "Optional description shown in the flash dialog",
"builds": [
{"chipFamily": "ESP32",
"parts": [
{"path": "firmware.bin", "offset": 0}]}]}
Multi-chip with separate partitions — auto-detected
Label text on the default button. Ignored when using a slotted custom button.
theme
"red"
Button theme: red, dark, green, ghost, or minimal.
erase-first
false
Boolean. When present, erases the full flash before writing. Also toggleable in the UI.
baud
921600
Flash baud rate. 921600 is fastest. Use 115200 for problematic cables.
Event
Detail payload
Description
flash-success
{ chip, flashSize, duration, filesCount }
Fired after firmware is written and device has hard-reset.
flash-error
{ title, message }
Fired if the flash process fails at any stage.
Features
Built on the SKR Flasher engine
The same battle-tested flash engine from SKR Electronics Lab's professional flasher — now embeddable.
No BOOT button hold required
The SKR engine handles ROM bootloader entry via DTR/RTS signals automatically. Most boards flash without holding any buttons at all.
Chip + MAC + flash size detection
Probes chip family, MAC address, and installed flash size on connect. Auto-selects the correct firmware variant from your manifest.
921600 baud with compression
Fastest flash speed supported by esptool-js with in-browser hardware compression. Typical 2 MB firmware in under 15 seconds.
MD5 verification after every write
Every .bin file is verified with MD5 immediately after writing. The hash is displayed in the success screen for manual confirmation.
Built-in Serial Monitor
After flashing, open the integrated serial monitor. Real-time line-buffered output, configurable baud rate, DTR/RTS control, and a send field for commands.
Works in any CMS or static site
Pure HTML custom element. Embed in WordPress, Ghost, GitHub Pages, Cloudflare Pages, Notion export, or any static host with zero build steps.
Five button themes
Choose from red, dark, green, ghost, or minimal via the theme attribute. Or use slot="activate" to supply any custom element.
100% local — zero server uploads
All flashing is fully local. Firmware and device data never pass through any third-party server. Zero telemetry, zero tracking, zero server costs.
DOM events and custom attributes
Listen to flash-success and flash-error events for analytics, post-flash actions, or UI updates. Configurable baud rate, label, and erase behavior via attributes.
Serial Monitor
Debug your firmware without leaving the browser
After flashing, open the built-in serial monitor directly from the success screen. Real-time line-buffered output, configurable baud rate and line endings, DTR/RTS signal control, and a command input — all in the browser.
Interactive preview — simulated output below, real output requires Web Serial
Serial Monitor
Connected at 115200 baud
BaudLine ending
Simulated ESP32 output — use the input below to send commands
The real Serial Monitor opens inside the flash dialog after a successful flash. It connects directly to your device via Web Serial — no relay server. This preview above is a fully functional simulator using the same UI. Requires Chrome 89+, Edge 89+, or Opera 76+ with a real ESP device connected via USB.
Comparison
ESP Flash Button vs ESP Web Tools
A detailed feature comparison. ESP Flash Button is the more capable, faster, and better-looking alternative.
ESP Flash Button
Auto bootloader entry — no BOOT button hold
Chip + MAC + flash size auto-detection
921600 baud with in-browser compression
MD5 verification after every write
Built-in Serial Monitor after flash
Five button themes + custom slot
Modern dark-mode UI with animated feedback
DOM events (flash-success / flash-error)
Erase flash toggle in the UI
Same manifest format (fully compatible)
ESP Web Tools
Requires BOOT button on most boards
Basic chip family detection
Slower default baud rate
No MD5 verification
No built-in serial monitor
Single default button style only
Dated, light-themed UI
DOM events
No UI erase toggle
Improv Wi-Fi standard support
Compatibility
Supported chips and boards
All ESP32-family chips supported by esptool-js. Any board with a USB-to-serial converter works without drivers.
ESP32
ESP32-S2
ESP32-S3
ESP32-C3
ESP32-C6
ESP32-H2
ESP8266
ESP8285
Browser requirement: Chrome 89+, Edge 89+, or Opera 76+ on desktop. The Web Serial API is not available in Firefox or Safari. USB-serial chips: CP2102, CP2104, CH340, CH341, FTDI FT232R, and any CDC-ACM serial adapter. Connection: Standard USB cable with data lines (not charge-only cables).
FAQ
Frequently asked questions
What is ESP Flash Button?
ESP Flash Button is a free, open-source web component that adds a one-click firmware installer to any website. It uses the Web Serial API to flash ESP32, ESP8266, and ESP32-S3 microcontrollers directly from the browser — no software installation, no IDE, no drivers required. It's built by SKR Electronics Lab and powered by Espressif's esptool-js.
How is this different from ESP Web Tools?
ESP Flash Button uses the SKR Flasher engine which automatically handles ROM bootloader entry — most boards flash without holding any buttons. It also runs at 921600 baud (fastest supported), performs MD5 verification after every write, detects chip type + MAC + flash size, includes a built-in serial monitor, offers five button themes, and features a modern dark-mode UI with real-time progress and animated feedback.
Which browsers are supported?
Google Chrome 89+, Microsoft Edge 89+, and Opera 76+ on desktop. The Web Serial API is required, which Firefox and Safari do not currently support. Mobile browsers are also not supported due to Web Serial API limitations.
Do I need to hold the BOOT button?
In most cases, no. The SKR Flasher engine manages bootloader entry automatically via DTR/RTS serial handshake lines. Simply plug in your board, click Connect, and the tool handles the rest. Some boards with non-standard USB-serial chips (particularly certain CH340 variants) may still need manual button intervention.
Is my firmware uploaded to any server?
No. Everything happens entirely locally in your browser. Firmware files are fetched directly from your own server and written to the device via the Web Serial API. No data passes through any third-party server. The component itself has zero telemetry and collects no information whatsoever.
What is the flash speed?
Default flash speed is 921600 baud with hardware compression enabled. A typical 2 MB firmware completes in under 15 seconds. You can reduce to 115200 baud via the baud attribute if you have a problematic USB cable or a board that doesn't support high baud rates reliably.
Can I use my own button styling?
Yes. In addition to the five built-in themes (red, dark, green, ghost, minimal) you can supply any element using the slot="activate" attribute. The component adds the flash behavior to your element while keeping all of your existing styles intact.
How does the Serial Monitor work?
After a successful flash, the success screen shows a Serial Monitor button. Clicking it opens a dedicated monitor window that reads from the same serial port using the Web Serial API readable stream. It buffers incoming bytes into complete lines, supports configurable baud rate and line endings, lets you toggle DTR, pulse RTS to reset the device, and send custom commands. The port is fully disconnected from esptool before the monitor opens, so there are no locking conflicts.
What CORS headers do I need?
Your server must send Access-Control-Allow-Origin: * on both your manifest JSON file and all .bin firmware files. Without CORS headers, the browser will block the fetch requests and the flash will fail with a manifest error. See the CORS setup section below for platform-specific instructions.
Server setup
CORS configuration
Your manifest and .bin files must allow cross-origin fetch requests. Add these headers to your server.