Running Python in the Browser
Running Python in the web browser has been getting a lot of attention lately. Shaun Taylor-Morgan knows what he’s talking about here - he works for Anvil, a full-featured application platform for writing full-stack web apps with nothing but Python. So I invited him to give us an overview and comparison of the open-source solutions for running Python code in your web browser.
That’s no longer true. There are quite a few ways to run Python in your web browser. This is a survey of what’s available.
I’m looking at six systems that all take a different approach to the problem. Here’s a diagram that sums up their differences.
The x-axis answers the question: when does Python get compiled? At one extreme, you run a command-line script to compile Python yourself. At the other extreme, the compilation gets done in the user’s browser as they write Python code.
You interact with the page structure (the DOM) using a toolbox of specialized Python objects and functions. For example, if you
import document, you can find any object on the page by using
document like a dictionary. To get the element whose ID is
name-box, you would use
document["name-box"]. Any readers familiar with JQuery will be feeling very at home.
Here’s a basic example. I wrote a Hello, World page with just an input box and a button:
<input id="name-box" placeholder="Enter your name"> <button id="greet-button">Say Hello</button>
To make it do something, I wrote some Python. When you click the button, an event handler fires that displays an alert with a greeting:
def greet(): alert("Hello " + document.getElementById("name-box").value + "!") document.getElementById("greet-button").addEventListener('click', greet)
I wrote this in a file called
hello.pyand compiled it using
document object for interacting with the DOM.
The same widget I wrote above can be written in a script tag like this:
<script type="text/python"> from browser import document, alert def greet(event): alert("Hello " + document["name-box"].value + "!") document["greet-button"].bind("click", greet) </script>
Pretty cool, huh? A script tag whose type is
There’s a good explanation of how it works on the Brython GitHub page. In short, you run a function when your page loads:
that transpiles anything it finds in a Python script tag:
The Skulpt website has a Python REPL that runs in your browser. It’s not making requests back to a Python interpreter on a server somewhere, it’s actually running on your machine.
Skulpt does not have a built-in way to interact with the DOM. This can be an advantage, because you can build your own DOM manipulation system depending on what you’re trying to achieve. More on this later.
Advantages: It’s a very faithful implementation of Python, and code gets executed quickly.
Disadvantages: A web page that embeds PyPy.js contains an entire Python interpreter, so it’s pretty big as web pages go (think megabytes).
You import the interpreter using
<script> tags, and you get an object called
pypyjs in the global JS scope.
There are three main functions for interacting with the interpreter. To execute some Python, run
pypyjs.set(variable, value) and
Here’s a script that uses PyPy.js to calculate the first ten square numbers:
PyPy.js has a few features that make it feel like a native Python environment - there’s even an in-memory filesystem so you can read and write files. There’s also a
document object that gives you access to the DOM from Python.
The project has a great readme if you’re interested in learning more.
Batavia is a bit like PyPy.js, but it runs bytecode rather than Python. Here’s a Hello, World script written in Batavia:
<script id="batavia-helloworld" type="application/python-bytecode"> 7gwNCkIUE1cWAAAA4wAAAAAAAAAAAAAAAAIAAABAAAAAcw4AAABlAABkAACDAQABZAEAUykCegtI ZWxsbyBXb3JsZE4pAdoFcHJpbnSpAHICAAAAcgIAAAD6PC92YXIvZm9sZGVycy85cC9uenY0MGxf OTc0ZGRocDFoZnJjY2JwdzgwMDAwZ24vVC90bXB4amMzZXJyddoIPG1vZHVsZT4BAAAAcwAAAAA= </script>
Bytecode is the ‘assembly language’ of the Python virtual machine - if you’ve ever looked at the
.pyc files Python generates, that’s what they contain (Yasoob dug into some bytecode in a recent post on this blog). This example doesn’t look like assembly language because it’s base64-encoded.
Batavia is potentially faster than PyPy.js, since it doesn’t have to compile your Python to bytecode. It also makes the download smaller -around 400kB. The disadvantage is that your code needs to be written and compiled in a native (non-browser) environment, as was the case with Transcrypt.
Again, Batavia lets you manipulate the DOM using a Python module it provides (in this case it’s called
The Batavia project is quite promising because it fills an otherwise unfilled niche - ahead-of-time compiled Python in the browser that runs in a full Python VM. Unfortunately, the GitHub repo’s commit rate seems to have slowed in the past year or so. If you’re interested in helping out, here’s their developer guide.
Mozilla’s Pyodidewas announced in April 2019. It solves a difficult problem: interactive data visualisation in Python, in the browser.
Python has become a favourite language for data science thanks to libraries such as NumPy, SciPy, Matplotlib and Pandas. We already have Jupyter Notebooks, which are a great way to present a data pipeline online, but they must be hosted on a server somewhere.
If you can put the data processing on the user’s machine, they avoid the round-trip to your server so real-time visualisation is more powerful. And you can scale to so many more users if their own machines are providing the compute.
Mozilla took charge of the WebAssembly CPython project and recompiled NumPy, SciPy, Matplotlib and Pandas into WebAssembly too. The result is a lot like Jupyter Notebooks in the browser -here’s an introductory notebook.
It’s an even bigger download than PyPy.js (that example is around 50MB), but as Mozilla point out, a good browser will cache that for you. And for a data processing notebook, waiting a few seconds for the page to load is not a problem.
documentobject to access the DOM. It’s a really promising project!
MAKING A CHOICE
I’ve given you six different ways to write Python in the browser, and you might be able to find more. Which one to choose? This summary table may help you decide.
There’s a more general point here too: the fact that there _is_ a choice.
Why not treat the whole web stack this way? The future of web development is to move beyond the technologies that we’ve always ‘had’ to use. The future is to build abstractions on top of those technologies, to reduce the unnecessary complexity and optimise developer efficiency. That’s why Python itself is so popular - it’s a language that puts developer efficiency first.
ONE UNIFIED SYSTEM
Remember I said that it can be an advantage that Skulpt doesn’t have a built-in way to interact with the DOM? This is why. If you want to go beyond ‘Python in the browser’ and build a fully-integrated Python environment, your abstraction of the User Interface needs to fit in with your overall abstraction of the web system.
If you’re reading this in 2024, why not get in touch and tell me whether I was right?