2026-06-15·9 min read

Capture UART Boot Logs in a Browser — No Drivers, No Terminal

WebSerialUARTtutorialworkflowtools

For years, capturing UART output meant screen /dev/ttyUSB0 115200 or Putty, plus the ritual of installing a vendor driver for whichever USB-UART adapter you happened to grab from the drawer. The Web Serial API quietly killed that workflow. If you're on a Chromium browser, you can plug the adapter into a USB port and have working serial in a browser tab in under 30 seconds.

What Web Serial actually is

Web Serial (proposed standard, shipping in Chromium 89+) gives a web page permissioned access to a serial device over USB-CDC. The browser mediates: a user gesture opens a permission dialog, the user picks the port, and the page gets a paired SerialPort object it can read and write through. No native install, no plugin, no extension.

The browser supports the standard control flags (baud, data bits, stop bits, parity, flow control) and surfaces device metadata so a tool like /terminal can offer reasonable defaults per known vendor ID.

Browser support reality

  • Chromium browsers (Chrome, Edge, Brave, Arc, Opera, Vivaldi): works, desktop and Chromebook.
  • Firefox: no implementation, no work in progress.
  • Safari: no implementation. Apple has not signaled intent.
  • Mobile: Chromium on Android supports it for some devices; iOS Safari does not.

There's no polyfill. The API touches USB hardware directly through the browser's permission model; there's no way to shim it from JavaScript alone.

The minimum viable capture, in code

// Request a port (must be triggered by a user click, not page load).
const port = await navigator.serial.requestPort()

// Open at 115200 8N1 — the embedded-Linux default.
await port.open({ baudRate: 115200 })

// Read until the device stops emitting or the user closes the page.
const reader = port.readable.getReader()
const decoder = new TextDecoder()
while (true) {
  const { value, done } = await reader.read()
  if (done) break
  process(decoder.decode(value, { stream: true }))
}

That's the whole protocol-side picture. Real implementations (the BootIntel terminal included) add line buffering, ANSI-escape handling, baud-rate auto-detection, and disconnection recovery — but the read loop is the read loop.

Drivers — or rather, the absence of them

Modern OS USB stacks ship class drivers for the common UART chips:

  • FT232 / FT2232 (FTDI): macOS native since Big Sur, Linux native since kernel 4.x, Windows since Win10.
  • CH340 / CH341 (WCH): macOS native since Monterey, Linux native, Windows requires WCH driver on some older builds.
  • CP210x (Silicon Labs): native on every supported OS.
  • PL2303 (Prolific): native, but counterfeit chips occasionally need the genuine driver.

On a Chromebook, you don't install anything. On modern macOS or Windows, you don't install anything. The browser hands the OS the USB-CDC request and the OS hands back a stream.

Baud rates that trip people up

The default first attempt should always be 115200. If the log looks like ASCII garbage but with a recognizable pattern, you're on the wrong rate. The non-115200 rates worth remembering:

  • 74880 — ESP8266 + ESP32 ROM bootloader. They emit a short banner at this rate before switching. If you only see gibberish on 115200 followed by clean output, you missed the banner.
  • 57600 / 38400 — older Atmel and PIC boards, some industrial controllers.
  • 921600 — ESP-IDF can be reconfigured to this for fast flashing + boot output on production builds.
  • 1500000 — ESP32-S3/C3 default flasher rate; sometimes carries through to boot output.
  • 100200 — some Marvell-derived SoCs due to a clock- divider quirk. Don't assume your terminal supports it; many do not.
  • 9600 — vendor-specific dev kits, legacy debug interfaces.

Wiring gotchas

  • TX/RX must cross. Adapter TX → device RX, adapter RX → device TX. Both labeled-as-TX wired together is the most common "why is nothing happening" cause.
  • Match logic levels. 3.3V adapters to 3.3V devices. 5V adapters to 5V devices. Wrong direction in either case can damage the SoC. When in doubt, 3.3V is the safe default for anything built in the last decade.
  • Connect a common ground. Without it, floating reference makes UART unreadable even at the right baud.
  • Do not connect Vcc. The device is already powered by its own supply; tying Vcc back through the adapter can dump current back into the USB port.

Full pin-discovery walkthrough on a board with no silkscreen: Finding UART Pins on an Unknown Board.

The browser permission model

Web Serial enforces three rules worth understanding:

  1. User gesture required. navigator.serial. requestPort() only resolves if called from a click, keypress, or touch handler — never a page-load timer. Background scripts cannot access serial.
  2. Per-port permission, not per-vendor. Permission is scoped to the specific port the user selected. Plugging the same adapter into a different USB hub triggers a new permission flow.
  3. Permissions persist across reload but not across origin. A site that requested access yesterday can call navigator. serial.getPorts() today to see the still-paired ports without a dialog — but only for that origin.

These constraints are why the BootIntel terminal asks you to click "Connect" on first arrival even if you used it yesterday from a different machine.

From clean log to identified device — in 5 seconds, free

Once captured, paste the log into the Device Fingerprinter: it identifies bootloader, kernel, init system, package versions, and SoC family. Everything runs in the browser; nothing is uploaded. For deeper analysis (CVE matching, retention, diff between scans) sign up; for a one-off look, the fingerprinter is enough.

When Web Serial isn't the right tool

  • You're running on Linux without a graphical session — Web Serial needs a Chromium window. picocom or screen is still the right answer.
  • You need scripted, repeatable captures in CI — call pyserialfrom a test harness.
  • You're on Safari or Firefox — install minicom.

Related reading


Capture UART Boot Logs in a Browser — No Drivers, No Terminal · BootIntel Blog