How to Use the Web Serial API in JavaScript
This article provides a practical guide to the Web Serial API, explaining how web applications can directly communicate with hardware devices over serial connections. You will learn the core concepts of the API, security and permission models, and step-by-step implementations for requesting device access, reading incoming serial data streams, and sending data to connected microcontrollers or peripherals using JavaScript.
What is the Web Serial API?
The Web Serial API provides a direct communication bridge between web pages and serial hardware devices, such as microcontrollers (e.g., Arduino, ESP32), 3D printers, barcode scanners, and custom sensors. Historically, interacting with local serial ports required native desktop software, specialized browser plugins, or local proxy servers. The Web Serial API removes these intermediaries by exposing serial ports directly to the browser via JavaScript.
Security and Browser Requirements
Because direct hardware access carries inherent security risks, the Web Serial API implements strict security guardrails:
- Secure Contexts (HTTPS): The API is only accessible
over HTTPS or
localhostduring development. - User Activation: Port access cannot be triggered programmatically on page load. It requires explicit user interaction, such as clicking a button.
- Browser Support: The API is primarily supported in Chromium-based browsers, including Google Chrome, Microsoft Edge, and Opera on desktop platforms (Windows, macOS, Linux, ChromeOS).
Requesting and Opening a Port
To initiate a connection, call
navigator.serial.requestPort(). This prompts the user with
a browser dialog listing available serial devices. Once the user selects
a device, the port must be opened with a specified baud rate.
async function connectSerial() {
try {
// Prompt user to select a serial port
const port = await navigator.serial.requestPort();
// Open the serial port with a specific configuration
await port.open({ baudRate: 9600 });
console.log("Connected to serial device.");
return port;
} catch (error) {
console.error("Connection failed:", error);
}
}You can optionally pass filter parameters to
requestPort() to only display devices with specific USB
Vendor IDs (VID) or Product IDs (PID).
Reading Data from a Serial Port
The Web Serial API uses the Streams API to handle continuous data
flow. Reading data involves getting a reader from
port.readable and listening for incoming chunks in an
asynchronous loop.
To handle text data, pipe the stream through a
TextDecoderStream.
async function readSerialData(port) {
const textDecoder = new TextDecoderStream();
const readableStreamClosed = port.readable.pipeTo(textDecoder.writable);
const reader = textDecoder.readable.getReader();
try {
while (true) {
const { value, done } = await reader.read();
if (done) {
// Stream has been closed
break;
}
if (value) {
console.log("Received data:", value);
}
}
} catch (error) {
console.error("Read error:", error);
} finally {
reader.releaseLock();
}
}Writing Data to a Serial Port
Sending data uses the port.writable stream. You can
write raw binary data using Uint8Array or pipe text through
a TextEncoderStream to transmit string data directly.
async function writeSerialData(port, message) {
const textEncoder = new TextEncoderStream();
const writableStreamClosed = textEncoder.readable.pipeTo(port.writable);
const writer = textEncoder.writable.getWriter();
try {
await writer.write(message + "\n");
console.log("Data sent:", message);
} catch (error) {
console.error("Write error:", error);
} finally {
// Release the writer lock so the port can be reused or closed
writer.releaseLock();
}
}Closing the Serial Port
To cleanly disconnect from a device, all active stream readers and
writers must release their locks before calling
port.close().
async function disconnectSerial(port, reader, writer) {
if (reader) {
await reader.cancel();
}
if (writer) {
await writer.close();
}
await port.close();
console.log("Serial port closed.");
}By leveraging standard stream piping and promise-based interfaces, the Web Serial API enables developers to build real-time hardware dashboards, configuration tools, and device interfaces entirely within the browser.