Write Once Run Anywhere

The portability promise and Web Assembly

The Promise

The promise of writing code that will run on multiple platforms is not new. There have been many runtimes, languages, frameworks and platforms built to solve this problem. The abstraction level varies. Here, we look above the hypervisor and OS layers.

Web browsers were built to render HTML, which is a standard. Yet, different implementations caused much pain and anguish to developers. JVM (Java Virtual Machine) runs apps written in Java (and other languages), on several devices. Though it is still widely used, it could not conquer the web.

On the web, there was a time when games and other rich content were written in Flash. It was a runtime which delivered the same experience on supported browsers. Alas, it was a source of several security bugs and was eventually discontinued.

In mobile development, there are many cross-platform frameworks. The idea remains the same: code that runs on multiple OS. Today, many frameworks target web and native devices both. There are many such examples.

Most recently, Web Assembly (Wasm) provides a way to run code written in multiple languages on the web at near-native speed, with client apps running on the web that previously couldn't have done so.

Web Assembly

Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications. Apart from portability, it has other goals like efficiency, safety, high performance, etc.

Let us take a look at portability. It can be run on Web and Non-web environments. We will not be compiling to wasm or dealing with any wasm code. Instead, we will use it to run a complete development environment in the browser.

Starters: Web

Pyodide runs Python in the browser powered by Wasm. It can be used as a kernel to run Jupyter notebooks using Jupyterlite. Here's how you can run it locally

Assuming you have Python and a virtualenv, create a project dir, say python-wasm. Create a requirements.txt file:

jupyterlite-core
jupyterlite-pyodide-kernel
ipywidgets

Install the requirements and run

# Install requirements
python -m pip install -r requirements.txt
# Create a Jupyterlite site
jupyter lite build --output-dir dist
# Run
jupyter lite serve

Open the link displayed in the output:

 Serving JupyterLite Debug Server from:
            /Users/python-wasm/workers/public/_output
        on:
            http://127.0.0.1:8000/index.html

And you can run (almost) any Python code! You may be able to interact with the notebook as long as the content is cached in the browser storage but changes do not persist across sessions.

Another interesting project is CPython Wasm. We can run sqlite, Postgres, and even Wordpress!

Entrée: Non-Web

Wasm can be run beyond the web. It can be run from docker containers, directly using the various runtimes, at the edge and more.

Let us try an interesting project Wasm Workers Server which can be installed easily:

# Install runtime
curl -fsSL https://workers.wasmlabs.dev/install | bash

Now, we can run workers in multiple languages using this runtime. But wait ...

Dessert: Best of both

We already have a Jupyterlite site (which runs on wasm) and now, a wasm runtime. Why not integrate these two?

Wasm Workers Server (wws) is capable of serving static content. So, let us run the Jupyterlite using wws

In the python-wasm dir, create another dir public and move the dist dir there.

# Project structure will look like
 └── python-wasm
    └── public
        └── dist

# Install the wws Python module
cd python-wasm
wws runtimes install python latest

# Run the server
wws
❯ wws
⚙️  Loading routes from: .                                                                                                                                             ─╯
🗺  Detected routes:
    - http://127.0.0.1:8080/
      => ./index.py
🚀 Start serving requests at http://127.0.0.1:8080

Now your Jupyterlite site is accessible at http://127.0.0.1:8080/dist/

Now, this is entirely powered by Web Assembly!

Here's the code on Replit: