Web Serial
Serial ports from the browser: Arduino, ESP32, 3D printers, lab instruments.
requestPort() asks the user for a port (USB or Bluetooth), open() sets the speed, and then you read and write with streams. It is used to flash firmware, for debug consoles or to configure devices with nothing to install.
Desktop Chrome and Edge since 89. Firefox shipped it in 151 (May 2026) with a site-permission add-on the browser generates itself. Chrome for Android since 148; over USB only on devices with the new Android serial API. Not Safari.
What it is for
- Flashing boards from the web
- Serial consoles and microcontroller debugging
- Driving 3D printers, CNC machines and instruments
Demo
A complete serial terminal: connect an Arduino, an ESP32, a micro:bit or any USB-serial adapter, choose the speed and type. The minimal example includes an Arduino sketch that echoes what it receives.
Browser support
Limited availability| Feature | Desktop | Mobile | |||||
|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Safari | Chrome Android | Firefox Android | Safari iOS | |
| navigator.serial | 89Chrome: 89 | 89Edge: 89 | 1511Firefox: 151 | —Safari: No | 1482Chrome: 148 | —Firefox: No | —Safari: No |
- 1Desktop. The first time it asks you to install a site-permission add-on.
- 2Bluetooth (RFCOMM) since 138; USB since 148 only on devices with the new Android serial API.
Compatibility generated on every deploy from MDN browser-compat-data 8.1.3 (24 Sep 2026) · web-features 3.40.0 · checked by hand: 1 Oct 2026
Minimal code
<!doctype html>
<meta charset="utf-8">
<title>Web Serial · ejemplo mínimo</title>
<!-- Terminal serie: abre un puerto (Arduino, micro:bit, ESP32…), muestra lo que llega y envía líneas.
Chrome y Edge 89+; Firefox 151+ (pide instalar un complemento de permiso); Chrome Android 148+. HTTPS o localhost. -->
<button id="connect">Abrir puerto</button>
<input id="line" placeholder="Texto a enviar" disabled> <button id="send" disabled>Enviar</button>
<pre id="out"></pre>
<script>
const $ = (s) => document.querySelector(s);
let writer;
$('#connect').onclick = async () => {
if (!('serial' in navigator)) return ($('#out').textContent = 'Web Serial no está disponible en este navegador.');
const port = await navigator.serial.requestPort();
await port.open({ baudRate: 115200 });
// Escribir: texto → bytes
const encoder = new TextEncoderStream();
encoder.readable.pipeTo(port.writable);
writer = encoder.writable.getWriter();
$('#line').disabled = $('#send').disabled = false;
// Leer: bytes → texto, hasta que se cierre el puerto
const reader = port.readable.pipeThrough(new TextDecoderStream()).getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
$('#out').textContent += value;
}
};
$('#send').onclick = async () => {
await writer.write($('#line').value + '\n');
$('#line').value = '';
};
</script><!doctype html>
<meta charset="utf-8">
<title>Web Serial · ejemplo mínimo</title>
<!-- Terminal serie: abre un puerto (Arduino, micro:bit, ESP32…), muestra lo que llega y envía líneas.
Chrome y Edge 89+; Firefox 151+ (pide instalar un complemento de permiso); Chrome Android 148+. HTTPS o localhost. -->
<button id="connect">Abrir puerto</button>
<input id="line" placeholder="Texto a enviar" disabled> <button id="send" disabled>Enviar</button>
<pre id="out"></pre>
<script>
const $ = (s) => document.querySelector(s);
let writer;
$('#connect').onclick = async () => {
if (!('serial' in navigator)) return ($('#out').textContent = 'Web Serial no está disponible en este navegador.');
const port = await navigator.serial.requestPort();
await port.open({ baudRate: 115200 });
// Escribir: texto → bytes
const encoder = new TextEncoderStream();
encoder.readable.pipeTo(port.writable);
writer = encoder.writable.getWriter();
$('#line').disabled = $('#send').disabled = false;
// Leer: bytes → texto, hasta que se cierre el puerto
const reader = port.readable.pipeThrough(new TextDecoderStream()).getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
$('#out').textContent += value;
}
};
$('#send').onclick = async () => {
await writer.write($('#line').value + '\n');
$('#line').value = '';
};
</script>What’s new
Keep in mind
- requestPort() needs a user gesture; getPorts() returns already-authorised ports.
- Before closing, cancel the reader and release the writer; otherwise port.close() fails.
- In Firefox for enterprise it is off by default (DefaultSerialGuardSetting).
Further reading
Sources
- MDN · Firefox 151 for developers19 May 2026
- Mozilla Hacks · Web Serial support in Firefox19 May 2026
- Chrome 148 release notes5 May 2026
- Blink · PSA Web Serial on Android10 Feb 2026
- WHATWG · Web Serial2 Jun 2026