Wasm Builders 🧱

Cover image for Streamlit meets WebAssembly - stlite
Yuichiro Tachibana (Tsuchiya)
Yuichiro Tachibana (Tsuchiya)

Posted on • Originally published at whitphx.info

Streamlit meets WebAssembly - stlite

TL;DR

  • I created a WebAssembly (Wasm) port of Streamlit, "stlite".
  • You can try it out and share your apps on "stlite sharing", the online code editor + app sharing platform for stlite.
  • The Wasm-based runtime provides many benefits such as offline capability, data privacy, and scalability.
  • In addition to stlite sharing, you can also host your Streamlit/stlite apps on your web site or create offline desktop applications. So you can create multi-platform apps only with Python coding.

Streamlit: a great Python web app framework

Streamlit logo

Streamlit is a web app framework with which we can create interactive web apps only with Python coding. It is especially famous and popular for data apps development as it well supports many data widgets cooperating the DS/ML Python ecosystem, while it also covers a wide range of general purpose web development.

When you run a Streamlit app, the Streamlit framework starts a web server and it serves a frontend app that is a single page application (SPA) whose contents are dynamically constructed according to your app script written in Python. Then the frontend app continuously communicates with the web server and triggers the Python code executions on the server upon some events to get some results that dynamically update the frontend view.
Due to this unique server-client architecture, we can construct interactive web apps only by writing the logics in the server-side Python code.

Streamlit architecture

stlite: client-side Streamlit powered by Pyodide

There had been an epoch-making invent in the Python world - Pyodide. It is a CPython runtime compiled for WebAssembly (Wasm) that runs on web browsers (and NodeJS).

Pyodide logo

So I customized Streamlit to run on Pyodide runtime and released it as "stlite"!

stlite logo

On stlite, the entry point is a JavaScript file that mounts the Streamlit frontend SPA into the HTML page, loads the Pyodide environment, and launches the Streamlit Python server in the Pyodide environment on the web browser.

With this architecture, thanks to Pyodide, the Python runtime no longer exists on the server side since it runs on the web browser.
The web server is only for serving the static files such as HTML, JS, and CSS.

stlite architecture

Pros and Cons

As it runs completely on the browsers, stlite has some benefits that the original Streamlit does not have.

  • Offline capability:
    As even the Streamlit "server" runs on the browser, all the components resides on the client side, so once all the resources are loaded from the web server, the app can run offline.

  • Data privacy:
    Since the entire app runs on your browser, even when you do "upload" some files from the file uploader on the page, these files are NEVER sent to any remote servers and only processed within your machine.
    This feature is sometimes beneficial especially in the applications of data science, machine learning, or statistics where Streamlit is widely used, as these often have strict privacy rules.

  • Scalability:
    The main workload such as machine learning computation written in Python moves from the server to each browser, so the system becomes scalable.

  • Multi-platform (web, desktop, mobile):
    As it runs on web browsers, it can also be an installable app (PWA), and can be bundled into a desktop app with Electron. Building mobile apps is also on the read map.
    As a result, you can create interactive apps only in Python with Streamlit and bundle them for each platform.

  • Online editing experience:
    I developed the online editor & real-time preview service for Streamlit apps based on stlite - stlite sharing that we will see below soon.
    Precisely, this is not purely because of WebAssembly, but the Wasm-based architecture made it easier to create such a service which could not have existed before.

On the other hand, stlite and Pyodide has some disadvantages as a tradeoff.

  • Some packages are not available:
    Some C extension packages such as TensorFlow cannot be installed because C extensions must be compiled for the Pyodide runtime specifically, while many popular C extensions are already available such as NumPy or Pandas.
    For more details, read the Pyodide articles such as this one.

  • Large initial payload:
    A large amount of resources will be downloaded when the user opens the app because Pyodide loads the whole Python runtime and the standard libraries, and slite also downloads the necessary wheel files including the streamlit package.

  • Network restriction:
    For the security reasons, accessing remote resources from the stlite applications are restricted by the browser, e.g. CORS.

  • The source code is open:
    Note that all the source code and hosted data are sent to the client side, so they are visible to the users. You must not put any secrets on the source code of the stlite apps.

(This section is highly inspired by the blog post about Shinylive.)

Online code editor + app sharing platform: stlite sharing

stlite sharing is an online code editor + app sharing platform for stlite. It is the easiest and fastest way to try out stlite.

stlite sharing OGP

The following screenshot is the online editing & app preview mode of stlite sharing. You can try it out at https://edit.share.stlite.net/ .

stlite sharing screenshot

When you create/edit/delete the files on the left pane, those changes are applied to the virtual file system on top of which stlite runs, then the Streamlit server detects the file changes and recommends you to hot-reload the application, as shown in the screenshot below.
(The file change detection and hot-reloading is a part of the Streamlit core features. If you are not familiar with it, read the official document.)

stlite sharing screenshot on the file change

The file changes are saved to the file system when the "Save" button is clicked.
You can also add or delete the files on the file tabs.

Shareable URLs

On stlite sharing, you can get the shareable URL of the current app displayed at top right. There is also the "Open App" link next to it, through which you can open the URL.

stlite sharing screenshot on the file change

By navigating to the URL or clicking the link, the app opens in the sharing mode as below, where only the Streamlit app opens and there are not the editor, the sharing link, and any other widgets.

stlite sharing screenshot on the app sharing mode

Its URL is https://share.stlite.net/ with a long hash like #!ChBz....

The host https://share.stlite.net/ is for the app sharing mode of stlite sharing while the editor mode we have seen above is at https://edit.share.stlite.net/.

In the long hash part of the URL like #!ChBz...,
all the source code and data edited on the editor are encoded and embedded, so you can share the app only by copying this URL.

Another good point of this approach is, since all the code and data are embedded into the URL hash, these data are never uploaded and saved to the remote server.

The encoded URL hash is also loadable on the editor mode, so if you open the editor mode URL, https://edit.share.stlite.net/ with the hash, the app data will be restored on the editor.

Note that some SNS and URL shortening services cut off the long URLs, so you should take care of it when sharing the URL on such platforms. For the details, read this part of the Shinylive article.

This URL mechanism is inspired by Shinylive.

Adding files

By clicking the "+" button at the file tabs area, you can create a new file.

You can also upload files from your local env (the files are never "uploaded" to any remote servers. They are just sent to the virtual file system on your browser.) Though this uploader, you can also add binary files.

Add and upload buttons

Files in directories and Multipage apps support

You can edit the file name on the tab, and importantly, by inserting the file path delimiter, /, you can create files in directories.

Editing the file name on the tab

With this feature, stlite sharing supports the Streamlit's Multipage apps.
In short, by creating a Python file in the pages/ directory (pages/*.py), you can add a new page in the app.
For the details, read the official document about Multipage apps.

Installing packages

When you need to install some packages, use the special "requirements" tab.

The requirements tab

The package names written in the editor on this tab will be passed to the package installer micropip.install() internally (on the Pyodide runtime, micropip is used as a package manager) when clicking the "Save" button.
Write one package name per line.

This is a special tab in that the file content is not saved into the file system. It is used only for specifying the package names passed to micropip.install().

Please note again that some C extension packages cannot be installed. The following screenshot is the sample of such a case, where installing a C extension failed.

Installation failure

Samples

You can find some samples on the side menu, so check out these! I hope some inspire you.

For example, the "Streamlit Hello" demo runs the official Streamlit demo on stlite while it is available through the streamlit hello command with the original Streamlit.

streamlit hello demo

With a recent release of Pyodide 0.21, many C-extensions became available including OpenCV.
stlite sharing includes the samples using OpenCV and scikit-image with real-time video streaming. Even these samples run on web browsers and nothing are sent to any remote servers!

OpenCV demo

https://twitter.com/whitphx/status/1583352801195065346

Loading apps from GitHub or Gist

Instead of the encoded URL hash explained above,
you can host your Python script somewhere and pass its URL to stlite sharing via the URL hash as below.

https://share.stlite.net/#https://...py
Enter fullscreen mode Exit fullscreen mode

If the URL is of GitHub or Gist, you can pass the preview page URL instead of the raw file URL.
For example, https://share.stlite.net/#https://github.com/napoles-uach/test/blob/main/app.py works.

When you need to install some packages, use the following format instead.

https://share.stlite.net/#url=<Script URL>?req=<Package1>&req=<Package2>
Enter fullscreen mode Exit fullscreen mode

The example of this format installing opencv-python and matplotlib follows: https://share.stlite.net/#url=https://github.com/whitphx/stlite-sample/blob/main/opencv-image-processing.py&req=opencv-python&req=matplotlib
There is also the Gist version: https://share.stlite.net/#url=https://gist.github.com/whitphx/f23b7b2bbda19cd421121bd72ebf2101&req=opencv-python&req=matplotlib

Host your Streamlit app on your web site

stlite also supports self-hosting the apps.

All you have to do is write and host a single HTML file.

It would be something like below, where you can embed the Python source code into the JS code as a string literal.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <title>stlite app</title>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/@stlite/mountable@0.15.0/build/stlite.css"
    />
  </head>
  <body>
    <div id="root"></div>
    <script src="https://cdn.jsdelivr.net/npm/@stlite/mountable@0.15.0/build/stlite.js"></script>
    <script>
      stlite.mount(
        `
import streamlit as st

name = st.text_input('Your name')
st.write("Hello,", name or "world")
`,
        document.getElementById("root")
      );
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

If you need to install some packages, specify the entrypoint file name, and/or use multiple files, use the following more flexible API.

stlite.mount(
  {
    requirements: ["matplotlib"], // Packages to install
    entrypoint: "streamlit_app.py", // The target file of the `streamlit run` command
    files: {
      "streamlit_app.py": `
import streamlit as st
import matplotlib.pyplot as plt
import numpy as np

size = st.slider("Sample size", 100, 1000)

arr = np.random.normal(1, 1, size=size)
fig, ax = plt.subplots()
ax.hist(arr, bins=20)

st.pyplot(fig)
`,
    },
  },
  document.getElementById("root")
);
Enter fullscreen mode Exit fullscreen mode

After writing the HTML file, host it wherever you like, such as GitHub Pages.
In your local env, you can test it with python -m http.server 8000.

This YouTube video may guide you to set it up on GitHub Pages (although the stlite API used in the video is out-dated now.)

https://youtu.be/VQdktxgbmmg

Please read the README for the details about self-hosting apps!

Bundle your Streamlit app as a desktop app

The stlite apps run on web browsers, so it is also possible to bundle them into desktop apps with Electron.

desktop app sample

https://twitter.com/DataChaz/status/1577750208364134421

To create your own desktop app with stlite,
follow this instruction! -> https://github.com/whitphx/stlite/tree/main/packages/desktop-cli#readme

There is a sample app repository and its distributable files too.

Tell us your story!

When you create some apps with stlite, please share it!

If it's on stlite sharing, all you have to do is copy and paste the URL 👍

These are good places to share your apps, samples, or case studies!

Top comments (0)