Web Serial
Web Serial gives a browser tab direct read/write access to any serial device -- Arduino, CNC machine, lab instrument. It is desktop-only, Chromium-only, and everything is raw bytes over a Streams-based API.
Serial ports existed before USB. They were the universal wire between computers and hardware -- scientific instruments, modems, industrial equipment, printers. USB replaced serial for most consumer devices. For custom hardware, microcontrollers, and machines that have not changed their protocol in 30 years, serial is still what runs the connection.
Web Serial brings that channel into a browser tab.
What "Serial" Means Here
The Web Serial API connects to any device that communicates over a serial port: USB-to-serial adapters, Bluetooth virtual COM ports, or directly over USB for devices that implement CDC (Communications Device Class). On macOS and Linux, these show up as /dev/tty* ports. On Windows, as COM* ports.
This is desktop only. Mobile devices do not have serial hardware in a form the spec targets. If you try to use Web Serial on a phone, there are no ports to connect to.
The Connection Flow
// Request a port -- user sees a picker
const port = await navigator.serial.requestPort()
// Open with the device's baud rate
await port.open({ baudRate: 9600 })The picker shows all available serial ports. You can filter by usbVendorId and usbProductId if you know your device's USB descriptor.
baudRate is required and must match the device. Wrong baud rate produces garbage data or no data at all. Other options -- dataBits, stopBits, parity, flowControl -- default to the most common values (8N1) and only need adjustment when a device's documentation specifies otherwise.
Reading Data
Web Serial uses the Streams API. port.readable is a ReadableStream:
const reader = port.readable.getReader()
try {
while (true) {
const { value, done } = await reader.read()
if (done) break
// value is a Uint8Array of incoming bytes
console.log(new TextDecoder().decode(value))
}
} finally {
reader.releaseLock()
}You own the reader lock while reading. Release it when done so the port can be closed or read again.
Writing Data
port.writable is a WritableStream:
const writer = port.writable.getWriter()
const encoder = new TextEncoder()
await writer.write(encoder.encode('Hello, device!\n'))
writer.releaseLock()Same lock pattern as reading. All data is bytes. TextEncoder / TextDecoder convert between strings and Uint8Array for devices that speak ASCII. Devices with binary protocols need manual byte construction.
ExpandWeb Serial: three-step connection flow, use cases, and port configuration options
Practical Use: Arduino
An Arduino connected via USB appears as a serial port. A sketch running on the Arduino that logs sensor readings over Serial.println() can be read directly:
const port = await navigator.serial.requestPort()
await port.open({ baudRate: 9600 })
const decoder = new TextDecoderStream()
port.readable.pipeTo(decoder.writable)
const reader = decoder.readable.getReader()
while (true) {
const { value } = await reader.read()
if (value) console.log(value) // e.g. "Temperature: 23.4"
}TextDecoderStream is a TransformStream that decodes bytes to strings as they arrive -- cleaner than calling new TextDecoder().decode() on each chunk manually.
Previously Granted Ports
Once a user grants access to a port, the browser remembers it:
const ports = await navigator.serial.getPorts()
// ports is an array of previously granted SerialPort objectsThis lets the app reconnect without prompting the user again on the next visit.
Support
Web Serial is light-green tier. It works in Chrome and Edge. Firefox has not implemented it. Safari has not implemented it. Apple has not indicated plans to ship it.
The reasoning from Apple and Mozilla is similar to their position on Web Bluetooth: low-level hardware access from web pages carries security and privacy risk they are not comfortable with.
if ('serial' in navigator) {
// Web Serial is available
}Web Serial works with devices that have a serial interface. Web USB goes one level lower -- it communicates with any USB device directly, even when no driver or serial protocol is involved.
The Essentials
- Web Serial gives a browser tab read/write access to any serial port -- USB-to-serial adapters, Bluetooth COM ports, Arduino, industrial equipment.
- Desktop only. No mobile serial hardware targets the spec.
navigator.serial.requestPort()-- user picks the port.port.open({ baudRate })-- baud rate must match the device exactly.port.readableandport.writableare Streams. Read with a reader lock, write with a writer lock, release both when done.- All data is
Uint8Array. UseTextEncoder/TextDecoderfor ASCII devices. Use manual byte construction for binary protocols. - Chromium only. Apple and Mozilla have not implemented it.
Further Reading and Watching
- Deep Dive w/Scott: Javascript WebSerial (YouTube) -- live exploration of the Web Serial API connecting a browser to an Adafruit microcontroller with real hardware
- Read from and write to a serial port -- Chrome for Developers -- official guide with the full connection flow, stream handling, error recovery, and filtering by USB vendor/product ID