Geert Roumen
Published © GPL3+

Let Arduino Control Your Browser

Recently Google Chrome added the functionality to communicate with serial devices from the browser. Build interactive experiences easily!


Things used in this project

Hardware components

Arduino UNO
Arduino UNO
This should work with any Arduino (or Serial device)
USB-A to B Cable
USB-A to B Cable


Read more


Simple code on the Arduino to turn on and off an LED with the WebSerial interface

When an "A" is sent the light will go on, when a "B" is sent the light will go off, in the meantime it sends the analog Values to the browser
void setup() {
// put your setup code here, to run once:

void loop() {
// put your main code here, to run repeatedly:
        char data =;
            case 'A':
                digitalWrite(13, HIGH);
                //do this when A is sent from the USB-host/computer
            case 'B':
            digitalWrite(13, LOW);
                //do this when B is sent from the USB-host/computer
            case 'Q':
                //Send the current value on analogPin 0 to the USB-host/computer
    Serial.print(", ");

WebSerial browser code

This is example code that connects Google Chrome via webSerial to your Arduino
    <button id="connectButton">Connect</button><span id="statusBar">Disconnected</span><br />
    <input id="sendText" type="text" /><input id="sendButton" type="button" value="send" /><br />
    <textarea readonly id="receiveText" ></textarea>

<script type="text/javascript">
This code is highly based on the webSerial tutorial by Google:

The intention is to get you started with using webSerial for your Arduino project.


    //Define the elements
let sendText = document.getElementById("sendText");
let sendButton = document.getElementById("sendButton");
let receiveText = document.getElementById("receiveText");
let connectButton = document.getElementById("connectButton");
let statusBar = document.getElementById("statusBar");

//Couple the elements to the Events
connectButton.addEventListener("click", clickConnect)
sendButton.addEventListener("click", clickSend)

//When the connectButton is pressed
async function clickConnect() {
    if (port) {
        //if already connected, disconnect

    } else {
        //otherwise connect
        await connect();

//Define outputstream, inputstream and port so they can be used throughout the sketch
var outputStream, inputStream, port;
navigator.serial.addEventListener('connect', e => {
    statusBar.innerText = `Connected to ${e.port}`;
    connectButton.innerText = "Disconnect"
  navigator.serial.addEventListener('disconnect', e => {
    statusBar.innerText = `Disconnected`;
    connectButton.innerText = "Connect"
//Connect to the Arduino
async function connect() {

    //Optional filter to only see relevant boards
    const filter = {
        usbVendorId: 0x2341 // Arduino SA

    //Try to connect to the Serial port
    try {
        port = await navigator.serial.requestPort(/*{ filters: [filter] }*/);
        // Continue connecting to |port|.

        // - Wait for the port to open.
        await{ baudrate: 9600 });

        statusBar.innerText = "Connected";
        connectButton.innerText = "Disconnect"
        let decoder = new TextDecoderStream();
        inputDone = port.readable.pipeTo(decoder.writable);
        inputStream = decoder.readable;

        const encoder = new TextEncoderStream();
        outputDone = encoder.readable.pipeTo(port.writable);
        outputStream = encoder.writable;

        reader = inputStream.getReader();
    } catch (e) {

        //If the pipeTo error appears; clarify the problem by giving suggestions.
        if (e == "TypeError: Cannot read property 'pipeTo' of undefined") {
            e += "\n Use Google Chrome and enable-experimental-web-platform-features"
        connectButton.innerText = "Connect"
        statusBar.innerText = e;
//Write to the Serial port
async function writeToStream(line) {
    const writer = outputStream.getWriter();

//Disconnect from the Serial port
async function disconnect() {

    if (reader) {
        await reader.cancel();
        await inputDone.catch(() => { });
        reader = null;
        inputDone = null;
    if (outputStream) {
        await outputStream.getWriter().close();
        await outputDone;
        outputStream = null;
        outputDone = null;
    statusBar.innerText = "Disconnected";
    connectButton.innerText = "Connect"
    //Close the port.
    await port.close();
    port = null;

//When the send button is pressed
function clickSend() {
    //send the message
    //and clear the input field, so it's clear it has been sent
    sendText.value = "";


//Read the incoming data
async function readLoop() {
    while (true) {
        const { value, done } = await;
        if (done === true){
        //When recieved something add it to the big textarea
        receiveText.value += value;
        //Scroll to the bottom of the text field
        receiveText.scrollTop = receiveText.scrollHeight;


Geert Roumen

Geert Roumen

6 projects • 7 followers
I’m a maker and interaction designer, bridging the digital and physical world. and make prototypes and do research in a playful way.