Running Python Programs in Your Browser

Using Pyodide and Webassembly The post Running Python Programs in Your Browser appeared first on Towards Data Science.

May 12, 2025 - 20:31
 0
Running Python Programs in Your Browser

In recent years, WebAssembly (often abbreviated as WASM) has emerged as an interesting technology that extends web browsers’ capabilities far beyond the traditional realms of HTML, CSS, and JavaScript. 

As a Python developer, one particularly exciting application is the ability to run Python code directly in the browser. In this article, I’ll explore what WebAssembly is (and its relation to the Pyodide library), talk about its benefits and everyday use cases, and dive into some practical examples of how you can use WebAssembly to run Python programs on the web.

These tools can also benefit data scientists and ML professionals. Pyodide brings a significant portion of the scientific Python stack (NumPy, Pandas, Scikit-learn, Matplotlib, SciPy, etc.) to the browser, meaning that using familiar tools and libraries during code development is possible. It can also be useful for demonstration purposes. As you’ll see in my final example, combining Python’s data processing power with HTML, CSS, and JavaScript for UI, you can quickly build interactive dashboards or tools without needing a separate backend for many use cases.

What is WebAssembly?

Webassembly is a low-level binary instruction format designed as a portable target for compiling high-level languages, such as C, C++, Rust, and even Python. It was created to enable high-performance applications on the web without some of the pitfalls of traditional JavaScript execution, such as run-time speed. Some key aspects of WebAssembly include:

  • Portability. WebAssembly modules run consistently across all modern browsers.
  • Performance. The binary format is compact and can be parsed quickly, which allows near-native execution speed.
  • Security. Running in a sandboxed environment, WebAssembly provides strong security guarantees.
  • Language Agnosticism. Although browsers primarily support JavaScript, WebAssembly enables developers to write code in other languages and compile it to WebAssembly (wasm).

What Can WebAssembly Be Used For?

WebAssembly has a wide array of applications. Some of the most common use cases include:-

  1. High-Performance Web Apps. WebAssembly can help applications such as games, image and video editors, and simulations achieve near-native performance.
  2. Porting Legacy Code. Code written in C, C++, or Rust can be compiled into WebAssembly, allowing developers to reuse existing libraries and codebases on the web.
  3. Multimedia Processing. Audio and video processing libraries benefit from webassembly’s speed, enabling more complex processing tasks in real-time.
  4. Scientific Computing. Heavy computations such as machine learning, data visualisation, or numerical simulations can be offloaded to WebAssembly modules.
  5. Running Multiple Languages. Projects like Pyodide allow Python (and its extensive ecosystem) to be executed in the browser without requiring a server backend.

If you frequently code in Python, that last point should make your ears prick up, so let’s dive into that aspect further.

Running Python on the Web

Traditionally, Python runs on the server or in desktop applications. However, thanks to initiatives like Pyodide, Python can run in the browser via WebAssembly. Pyodide compiles the CPython interpreter code into WebAssembly, allowing you to execute Python code and use many popular third-party libraries directly in your web application.

And this isn’t just a gimmick. There are several advantages to doing this, including:-

  • Using Python’s extensive library ecosystem, including packages for data science (NumPy, Pandas, Matplotlib) and machine learning (Scikit-Learn, TensorFlow).
  • Enhanced responsiveness as fewer round trips to a server are required.
  • It is a simpler deployment as the entire application logic can reside in the front end.

We’ve mentioned Pyodide a few times already, so let’s take a closer look at what exactly Pyodide is.

What is Pyodide

The idea behind Pyodide was born from the growing need to run Python code directly in the browser without relying on a traditional server-side setup. Traditionally, web applications had depended on JavaScript for client-side interactions, leaving Python confined to back-end or desktop applications. However, with the advent of WebAssembly, an opportunity arose to bridge this gap.

Mozilla Research recognised the potential of this approach and set out to port CPython, the reference implementation of Python, to WebAssembly using the Emscripten toolchain. This effort was about running Python in the browser and unlocking a new world of interactive, client-side applications powered by Python’s rich set of libraries for data science, numerical computing, and more. 

To summarise, at its core, Pyodide is a port of CPython compiled into WebAssembly. This means that when you run Python code in the browser using Pyodide, you execute a fully functional Python interpreter optimised for the web environment.

Right, it’s time to look at some code.

Setting up a development environment

Before we start coding, let’s set up our development environment. The best practice is to create a separate Python environment where you can install any necessary software and experiment with coding, knowing that anything you do in this environment won’t affect the rest of your system.

I use conda for this, but you can use whatever method you know best suits you. Note that I’m using Linux (WSL2 on Windows).

#create our test environment
(base) $ conda create -n wasm_test python=3.12 -y

# Now activate it
(base) $ conda activate wasm_test

Now that our environment is set up, we can install the required libraries and software.

# 
#
(wasm_test) $ pip install jupyter nest-asyncio

Now type in jupyter notebook into your command prompt. You should see a jupyter notebook open in your browser. If that doesn’t happen automatically, you’ll likely see a screenful of information after the jupyter notebook command. Near the bottom, there will be a URL that you should copy and paste into your browser to initiate the Jupyter Notebook.

Your URL will be different to mine, but it should look something like this:-

http://127.0.0.1:8888/tree?token=3b9f7bd07b6966b41b68e2350721b2d0b6f388d248cc69da

Code example 1 — Hello World equivalent using Pyodide

Let’s start with the easiest example possible. The simplest way to include Pyodide in your HTML page is via a Content Delivery Network (CDN). We then print out the text “Hello World!”




  
  Hello, World! with Pyodide


  

Python Hello, World!



  
  

I ran the above code in W3Schools HTML TryIt editor and got this,

Image by Author

When the button is clicked, Pyodide runs the Python code that prints “Hello, World!”. We don’t see anything printed on the screen, as it is printed to the console by default. We’ll fix that in our following example.

Code Example 2 — Printing output to the browser

In our second example, we’ll use Pyodide to run Python code in the browser that will perform a simple mathematical calculation. In this case, we will calculate the square root of 16 and output the result to the browser. 




  
  Pyodide Example


  

Running Python in the Browser with Pyodide



  
  
  

Running the above code in the W3Schools TryIT browser, I got this output.

Image by Author

Code Example 3 – Calling Python Functions from JavaScript

Another valuable and powerful feature of using Pyodide is the ability to call Python functions from JavaScript and vice versa. 

In this example, we create a Python function that performs a simple mathematical operation—calculating the factorial of a number—and call it from JavaScript code.




  
  Call Python from JavaScript


  

Calculate the Factorial of a Number



  
  

Here is a sample output when running on W3Schools. I won’t include the code section this time, just the output.

Image by Author

Code Example 4— Using Python Libraries, e.g. NumPy

Python’s power comes from its rich ecosystem of libraries. With Pyodide, you can import and use popular libraries like NumPy for numerical computations. 

The following example demonstrates how to perform array operations using NumPy in the browser. The Numpy library is loaded using the pyodide.loadPackage function.




  
  NumPy in the Browser


  

Matrix Multiplication with NumPy



  
  

Image by Author

Code Example 5— Using Python libraries, e.g. matplotlib

Another powerful aspect of running Python in the browser is the ability to generate visualisations. With Pyodide, you can use GUI libraries such as Matplotlib to create plots dynamically. Here’s how to generate and display a simple plot on a canvas element.

In this example, we create a quadratic plot (y = x²) using Matplotlib, save the image to an in-memory buffer as a PNG, and encode it as a base64 string before displaying it.




  
  Matplotlib in the Browser


  

Interactive Plot with Matplotlib

Plot will appear here
Image by Author

Code Example 6: Running Python in a Web Worker

For more complex applications or when you need to ensure that heavy computations do not block the main UI thread, you can run Pyodide in a Web Worker. Web Workers allow you to run scripts in background threads, keeping your application responsive.

Below is an example of how to set up Pyodide in a Web Worker. We perform a calculation and simulate the calculation running for a while by introducing delays using the sleep() function. We also display a continuously updating counter showing the main UI running and responding normally. 

We’ll need three files for this:- an index.html file and two JavaScript files.

index.html




  
  Pyodide Web Worker Example


  

Running Python in a Web Worker

Status: Idle


  

worker.js

// Load Pyodide from the CDN inside the worker
self.importScripts("https://cdn.jsdelivr.net/pyodide/v0.23.4/full/pyodide.js");

async function initPyodide() {
  self.pyodide = await loadPyodide();
  // Inform the main thread that Pyodide has been loaded
  self.postMessage("Pyodide loaded in Worker");
}

initPyodide();

// Listen for messages from the main thread
self.onmessage = async (event) => {
  if (event.data === 'start') {
    // Execute a heavy computation in Python within the worker.
    // The compute function now pauses for 0.5 seconds every 1,000,000 iterations.
    let result = await self.pyodide.runPythonAsync(`
import time
def compute():
    total = 0
    for i in range(1, 10000001):  # Loop from 1 to 10,000,000
        total += i
        if i % 1000000 == 0:
            time.sleep(0.5)  # Pause for 0.5 seconds every 1,000,000 iterations
    return total
compute()
    `);
    // Send the computed result back to the main thread
    self.postMessage("Computed result: " + result);
  }
};

main.js

// Create a new worker from worker.js
const worker = new Worker('worker.js');

// DOM elements to update status and output
const statusElement = document.getElementById('status');
const outputElement = document.getElementById('workerOutput');
const startButton = document.getElementById('startWorker');

let timerInterval;
let secondsElapsed = 0;

// Listen for messages from the worker
worker.onmessage = (event) => {
  // Append any message from the worker to the output
  outputElement.textContent += event.data + "\n";

  if (event.data.startsWith("Computed result:")) {
    // When computation is complete, stop the timer and update status
    clearInterval(timerInterval);
    statusElement.textContent = `Status: Completed in ${secondsElapsed} seconds`;
  } else if (event.data === "Pyodide loaded in Worker") {
    // Update status when the worker is ready
    statusElement.textContent = "Status: Worker Ready";
  }
};

// When the start button is clicked, begin the computation
startButton.addEventListener('click', () => {
  // Reset the display and timer
  outputElement.textContent = "";
  secondsElapsed = 0;
  statusElement.textContent = "Status: Running...";
  
  // Start a timer that updates the main page every second
  timerInterval = setInterval(() => {
    secondsElapsed++;
    statusElement.textContent = `Status: Running... ${secondsElapsed} seconds elapsed`;
  }, 1000);
  
  // Tell the worker to start the heavy computation
  worker.postMessage('start');
});

To run this code, create all three files above and put them into the same directory on your local system. In that directory, type in the following command.

$ python -m http.server 8000

Now, in your browser, type this URL into it.

http://localhost:8000/index.html

You should see a screen like this.

Image by Author

Now, if you press the Start Computation button, you should see a counter displayed on the screen, starting at 1 and ticking up by 1 every second until the computation is complete and its final result is displayed — about 5 seconds in total. 

This shows that the front-end logic and computation are not constrained by the work that’s being done by the Python code behind the button. 

Image by Author

Code Example 7: Running a simple data dashboard

For our final example, I’ll show you how to run a simple data dashboard directly in your browser. Our source data will be synthetic sales data in a CSV file.

We need three files for this, all of which should be in the same folder.

sales_data.csv

The file I used had 100,000 records, but you can make this file as big or small as you like. Here are the first twenty records to give you an idea of what the data looked like.

Date,Category,Region,Sales
2021-01-01,Books,West,610.57
2021-01-01,Beauty,West,2319.0
2021-01-01,Electronics,North,4196.76
2021-01-01,Electronics,West,1132.53
2021-01-01,Home,North,544.12
2021-01-01,Beauty,East,3243.56
2021-01-01,Sports,East,2023.08
2021-01-01,Fashion,East,2540.87
2021-01-01,Automotive,South,953.05
2021-01-01,Electronics,North,3142.8
2021-01-01,Books,East,2319.27
2021-01-01,Sports,East,4385.25
2021-01-01,Beauty,North,2179.01
2021-01-01,Fashion,North,2234.61
2021-01-01,Beauty,South,4338.5
2021-01-01,Beauty,East,783.36
2021-01-01,Sports,West,696.25
2021-01-01,Electronics,South,97.03
2021-01-01,Books,West,4889.65

index.html

This is the main GUI interface to our dashboard.




    
    
    Pyodide Sales Dashboard
    
    



    

                        </div>
                                            <div class= Read More