Python in the browser
Some time ago the python world became crazy because of the release of Pyscript, a framework for running python in the browser. A lot of articles were written in the week after, and in a few days Pyscript gained more than 10k stars! I don't usually buy the hype, and so I wasn't so much bought in. It seemed to me a cool tech demo, but nothing usable in practice. After some research I also discovered Pyodide, which Pyscript in built on (to be fair, Pyscript is a wrapper on Pyodide and some CSS and javascript files), which funnily enough has less stars than Pyscript, and some other projects than enabled to run python in the browser, years before Pyscript. So I dismissed this as unrelevant.
However some time later I was working on btclib, the Bitcoin library I contribute to, and I found myself missing some kind of online tools to speed up my development. Remembering about python in the browser I decided to give it a try to build a website. I could have build it completely in javascript, using some open soure bitcoin libraries, but I decided to use my own library written in python. I thought that it could have been a funny learning experience, and that I could then share my experience on this blog. As a tool, I decided to use Pyodide, because I thought it was the most complete option out there and I didn't like Pyscript lack of flexiblity. So, without furthere ado, let's jump right into it!
Pyodide
Rigth out of the gate I encountered a major problem with pyodide. Our bitcoin library relies on hashlib
, a standar python module, for cryptography functions; one of them, ripemd160
, was disabled by OpenSSL
, which hashlib
relies upon, las year, for curious reasons. For CPython this is not a major problem because you can still enable it manually modifying a config file. It is annoying but not disruptive. However Pyodide uses a bundled OpenSSL, so you can't modify a config file to enable it. Fortunately Pyodide is open source so I could open a pull request to get it fixed. The devs were super supportive and fixed it in very little time. Still, this is a symptom of the fact the Pyodide is not yet production ready. To enable this feature I was forced to use the development version of Pyodide instead of the stable one, and while it was not a problem for me, for production environments where stability is key it would certainly be.
Another problem which affects Pyodide is the startup time. Before executing the python code the browser needs to download the entire Python runtime and additional packages, which can take a lot of time. And during this time no user input is processed, so it feels really slow from a user perspective. To give you some numbers, on my machine the first time I visit the website the startup time is about 3.6 seconds, after which the startup times howevers around 2.7 seconds. This is a lot of time, and it can definetely impact the user experience.
Benchmarks
After the startup time, another slowdown is the execution time. Running python in the browser will obviously have a performance hit, but how much are we talking about?


There are a lot of things to unpack here. Let's look at the first graph, which contains results from the pyodide benchmark suite. The first thing that struck me is that in a few cases Pyodide is actually faster than CPython. But in general Pyodide is around 40% slower than native if we take the geomean of all the tests. This seems to be in line with the performance of Web Assembly compared to native code. If we look at their mean firefox and chrome are neck and neck (within 1%), but if we look at the graph we can see that chrome is faster or equal most of the times, while there are a few examples where it is significantly slower. This is due to each browser behaviour under particular circumstances, and it can greatly influence the performance of your code.
However I was not satisfied with the Pyodide benchmark suite. First of all a lot of this benchmarks are based around numpy
, which relies heavily on C code to speed-up the execution. This means that we are testing more C code and less the performance of Pyodide. So I created my own test suite using some benchmarks from the benchmarks game. We can see the results in the second image. This time the results are very different. In pure python tests Pyodide is about 3.4 times slower than CPython when run in Firefox, and 3 times in Chrome. The second suite is much smaller, so we have to take the results with a pinch of salt, but it seems clear to me that the Pyodide benchmark suite is a little skewed. This may not be such a problem to Pyodide devs, as it seems that the main use case of Pyodide is data visualization in the browser, which heavily uses numpy.
This results where run using Python 3.10.2. This is the latest version supported by Pyodide. Considering that the newsest version of CPython, version 3.11, brings a 1.25x performance gain, it's clear that the peformance hit of Pyodide is not small compared to desktop CPython. So if you want to run computationally heavy code in the browser, then Pyodide is not ready yet!
Final evaluation
Even though I encountered some issues, there are a lot of things that work out of the box with Pyodide. Because it is based on CPython there are no problems of incompatibilities. Importing an external library is as simple as adding a single line of code and it works flawlessly. So if you only want to run code in a library which exists only in python and you don't care about performance and startup time, Pydodide is what you are looking for.
Other solutions
Pyodide is not the only solution for running python in the browser. Before it lots of projects attempted to achieve this goal, in different ways. Let's go through a bunch of them:
Onorable mentions
First of all I would like to mention some projects with historical significance, or that took alternative routes to run python in the browser, but unfortunately are no longer under development:
PyPy.js: it adds a javascript backend to PyPy, a fast JIT implementation of python. It provided speed and features similar to CPython. It is not maintained since 2016 and the devs recommended looking at Pyodide.
Batavia: it compiles the program source first to bytecode, like CPython, and then executes this bytecode in a virtual machine written in javascript. It was developed by Beeware, a project that creates tools to build native cross-platform application in python.
Transcrypt
Transcrypt is a tool which compiles python to javascript ahead of time. This means that the only thing which is shipped is a javascript file, there is no big runtime to be downloaded to slow the startup time. Moreover the compiled javascript is incredibly fast, like 6 times faster than CPython! Amazing!
The problem is that most of the time it doesn't work. There are a lot of quirks here and there which at the end of the day provides a terrible experience. The biggest problem for me is the fact is that you can't import a nested module. So this is possible
import x
but this is not
import x.y
For my use case this makes Transcrypt unusable, since I want to use an external library. It is a cool proof of concept but it is useful if you want to compile single scripts, at which point you are better off writing javascript directly. Moreover it doesn't seem to be actively maintained, despite the creator claim. Still I decided to put it here instead of the onorable mentions because it is one of the projects with the greatest potential, however I don't know if I would recommend it going forward.
Brython
Brython is another project which compiles python code to javascript, but instead of compiling it ahead of time it compiles it during the page load. This means that it doesn't have the horrific download times of Pyodide but at the same time it will not be as jast as Transcrypt. In fact it is about 3.5 slower than Pyodide.
Brython can import external libraries, although the process is a little cumbersome. You have to compile all your dependencies into a javascript bundle using a command line script provided by Brython. This is feasable even for large projects, although not as clean as the solution provided by Pyodide.
In my particular use case I encountered an issue where the library I wanted to use had to read some files to function, but this file was not included in the javascript bundle. Brython enables you to create a virtual file system, so I could add all those files in it, but the process is quite long. Still, Brython is one of the better project out there for running python in the browser, and it is worth looking at.
Skulpt
Skulpt compiles python to javascript after page load, and it provides a runtime for this compiled javascript to be executed in. Unfortunately this runtime is not very lacking, and most of the standard library is not implemented. Moreover if you want to import an external module you have to implement it in javascript, there is no way to load an external python library.
This, combined with its horrible performance, makes it usable only for creating online learning tools (which to be fair seems to be their goal).
Conclusion
Python in the browser is clearly a thing the fascinates a lot of people, looking at the sheer number of projects that tried to achieve this. However most of them do not support the broader python ecosystem, which is it's strength, and are simply replacement for javascript. There are niches where this is not a problem, like if you want to create learning environments, but this was not what I was looking for.
If you want, like me, to use python libraries inside the browser, Pyodide at the time seems the only reasonable option. Even though it has some rough edges it gets the job done, and let's us create some amazing things using our favourite programming language!