Web Streams Flow to Microcontrollers

August 24, 2026 | Peter Hoddie, Principal

Guides – A new kind of documentation for Embedded JavaScript Developers

Web Streams are a foundation of the modern Web platform. Web Streams are also used in JavaScript server runtimes, making them part of Ecma TC55's Minimum Common Web API. Streams are widely adopted because they are a powerful tool for creating data flows from simple building blocks. They solve difficult programming problems, freeing developers to focus on how to use data rather than how to move it around. Given their popularity in the JavaScript ecosystem, Moddable is pleased to be able to bring this fundamental Web platform API suite to Embedded JavaScript developers.

The reason Web Streams haven't been practical on microcontrollers before is that they are a large, relatively complex suite of APIs. To squeeze them into an embedded device, Moddable had to create a completely new implementation of streams, written in C, that is tightly coupled to our XS JavaScript engine. This approach allows us to apply optimizations that reduce code size, memory use, and CPU overhead. While our implementation is both new and highly optimized, it is also compatible with the Web Streams you find in web browsers because we validate Moddable's Web Streams against the same test suite used by browsers. That means you can bring your experience with Web Streams from the web to microcontrollers.

The example later in this article shows just how easy it is to get started with Web Streams. You can find many great resources about Moddable's Web Streams support in our Guide to Web Streams.

Higher-Level Programming

Web Streams themselves are a relatively low-level API. Many high-level Web platform APIs that simplify development are built on them. As part of integrating Web Streams, Moddable has implemented several of these high-level APIs.

fetch()

One of the most fundamental operations a web page performs is retrieving data from the web. Today that is done using the fetch() API. The Moddable SDK has supported a subset of fetch() for several years. What it lacked was support for request and response bodies that are bigger than RAM. When you run on embedded devices with limited memory, that is a critical feature. Now that we have Web Streams, we've provided a fetch() implementation that integrates Web Streams so request and response bodies can be streamed. Learn more about integrating fetch() into your projects in our Guide to HTTP.

We've also implemented the standard DecompressionStream (using our pako emulation), a transform stream building block that decompresses data using gzip and deflate algorithms. This speeds data delivery by allowing web servers to deliver compressed data to embedded devices. Thanks to Web Streams, decompression happens transparently inside fetch() so you don't need to add code to support compressed HTTP responses.

WebSocketStream()

The web standard WebSocket() API works well for low-bandwidth connections, but has limitations when used with high-bandwidth connections that can lead to failures. These are solved by the experimental WebSocketStream() supported in Chrome and the Moddable SDK. The WebSocketStream() API models the WebSocket connection as a ReadableStream and WritableStream carrying WebSocket messages. This approach is very natural for integrating WebSockets into your project. Learn more about integrating WebSocketStream() into your projects in our Guide to WebSockets.

Web Serial

Web Serial is a proposed standard that integrates UART / Serial support into web browsers (currently Chrome and Firefox). One of the main motivators in its creation was to allow browsers to communicate directly with embedded devices. Now, Web Serial can be used on embedded devices too. Web Serial is a simple API that models the serial connection as readable and writable streams. Operations like converting the streams to text are easily handled by inserting a TextDecoderStream or TextEncoderStream.

Moddable's Web Serial implementation is built on the ECMA-419 Serial class. It is available on microcontrollers including the ESP32 family, Raspberry Pi Pico, nRF52, and ESP8266. It is also supported in the Moddable SDK's simulator on macOS, Windows, and Linux.

Streaming Embedded IO

Web Serial demonstrates the confluence of Web Streams and the IO capabilities of a microcontroller. Let's look at that by using Web Streams to implement a classic embedded starter project: connecting a button to an LED.

In this example, a ReadableStream wraps the ECMA-419 Digital input to create the button's stream, and a WritableStream wraps the ECMA-419 Digital output to create the LED's stream. They are connected together by pipeTo(), which sends the output of the button to the input of the LED. Web Streams deliver each button state change to the LED.

import { ReadableStream, WritableStream } from "web/streams";

const Digital = device.io.Digital;

const button = new ReadableStream({
    start(controller) {
        this.io = new Digital({
            pin: device.pin.button,
            mode: Digital.InputPullUp,
            edge: Digital.Rising | Digital.Falling,
            onReadable() {
                controller.enqueue(this.read());
            }
        });
    }
});

const led = new WritableStream({
    start() {
        this.io = new Digital({
            pin: device.pin.led,
            mode: Digital.Output
        });
    },
    write(value) {
        this.io.write(value);
    }
});

button.pipeTo(led);

Notice that the Web Streams integration adds just a few lines of code beyond the usual ECMA-419 Digital input and output code. Unlike a typical native C embedded implementation using a polling loop, the Web Streams version is completely non-blocking.

Now, let's add a transform stream to modify the button's output before it reaches the LED. Replace the final line above with the following code. Here, the transform stream inverts the button's value. The inverter is inserted into the stream using pipeThrough().

import { TransformStream } from "web/streams";

const invert = new TransformStream({
    transform(value, controller) {
        controller.enqueue(value ^ 1);
    }
});

button.pipeThrough(invert).pipeTo(led);

This simple example shows how easy it is to compose reusable, independent streaming modules to build useful data flows. You could take this further by piping the button stream to a WebSocketStream() to send the button values as messages to a remote device, or to Web Serial to monitor the button presses on your computer.

Dive In

If you've never used Web Streams before, give them a try. You might discover a valuable new tool for your projects. Our Web Streams Guide is a great place to start, with links to ready-to-run examples.

Web Streams bring powerful new capabilities to Embedded JavaScript, capabilities that help organize and structure data flows. This reliable, formal structure is essential because embedded projects are becoming bigger and more complex. Web Streams provide this infrastructure in a standard way, allowing you to leverage the extensive knowledge and code around Web Streams in your own development.