Nitro v0.11 shipped with Web Assembly support
Nitro 0.11 can run apps entirely in-browser.
What does this mean? #
Ordinarily, your Nitro app runs on a server (or cloud) somewhere, and displays interactive user interfaces in your web browser:
Network
│ Python
│ ┌──────────────────┐
│ │ Nitro │
│ │ ┌──────────────┐ │
│ │ │ │ │
│ │ │ App │ │
│ │ │ │ │
│ │ └─────▲─┬──────┘ │
Browser │ │ │ │ │
┌───────────┐ │ │ │ │ │
│ │ │ │ ┌─────┴─▼──────┐ │
│ │ I/O │ │ │ │ │
│ UI └─────────┼───────────┼─► Flask │ │
│ ◄─────────┼───────────┼─┐ │ │
│ │ │ │ └──────────────┘ │
└───────────┘ │ │ │
│ └──────────────────┘
│
With Nitro 0.11, you can put your Python code (and packages) on a static website somewhere (like Github Pages or S3 static websites), and have it load and execute entirely inside a web browser:
Network
│
Python │
┌──────────────────┐ │
│ Nitro │ │
│ ┌──────────────┐ │ │
│ │ │ │ │
│ │ App │ │ │ Web Server
│ │ │ │ │ ┌──────────────┐
│ └─────▲─┬──────┘ │ │ │ │
Browser │ │ │ ◄─────┼───┤ Static Files │
┌───────────┐ │ │ │ │ │ │ │
│ │ │ ┌─────┴─┴──────┐ │ │ └──────────────┘
│ │ │ │ │ │ │
│ UI └────┼─► Nitride │ │ │
│ ◄────┼─┐ │ │ │
│ │ │ └──────────────┘ │ │
└───────────┘ │ │ │
└──────────────────┘ │
│
How is this possible? #
Nitro 0.11 introduces an application runtime called Nitride, built atop Pyodide, a port of CPython to Web Assembly.
Nitride is a tiny (~5KB) layer that makes it possible to spawn a Python process in a Web Worker, which then controls the UI. The UI then continues to operate normally, assuming it's communicating with a Nitro server over the network. In reality, the Nitro "server" is simply running locally on a separate operating system thread.
What kind of apps is this useful for? #
Running apps this way is useful only if:
- You don't want to host your Python app in the cloud (and deal with everything else related to hosting, including monitoring, uptime, etc.)
- Your app doesn't need heavy compute, and you're fine with whatever processing power is available on your user's computer.
What does it look like in practice? #
Broadly, there are two ways to run your app:
- Embed your code directly in HTML using a
<script type="text/python">
tag. - (Recommended) Provide a configuration in a
<script type="application/nitro">
tag.
Embedding Python code directly in HTML looks like this:
<script type="text/python">
from h2o_nitro import AsyncView as View, box
async def main(view: View):
name = await view(box('What is your name?', value='Boaty McBoatface'))
feel = await view(box(f'How do you feel today, {name}?', value='intrigued'))
await view(f'What a coincidence, {name}, I feel {feel}, too!')
nitro = View(main, title='Hello Nitro!', caption='v1.0')
</script>
Although the above technique works, it's more convenient to write programs in .py
modules and load them dynamically using a YAML configuration that describes your app:
<script type="application/nitro">
language: python
entrypoint: example_hello.py
</script>
You can also load external packages, wheel files, and modules dynamically, like this:
<script type="application/nitro">
language: python
packages:
- numpy
- pandas
- bokeh
bundles:
- h2o-nitro-bokeh
files:
- example_bokeh_util.py
entrypoint: example_bokeh.py
</script>
For more comprehensive documentation, see https://nitro.h2o.ai/wasm/.
Summary #
The primary reason I added support for Web Assembly is to make our interactive docs run in-browser, and those docs are a lot more interesting to play with than the static documentation :)
Also, if you're interested in things like PyScript, Nitro offers a "batteries-included" approach - a huge variety of interactive components that you can snap together quickly and build applications, instead of using Python as a substitute for Javascript and futzing around with the browser DOM. Anything you can do with PyScript, you can do with Nitro, too!
Happy hacking!