DEV Community

MEROLINE LIZLENT
MEROLINE LIZLENT

Posted on

PyScript

For decades, JavaScript has had an iron grip on the browser. You could have it running on a server, you could have it running on a Raspberry Pi, you could have it running pretty much anywhere, but as soon as a user opened a tab, you needed JS. That's changing.
Running Python directly in an HTML page, complete with the Python ecosystem and the DOM and browser APIs. No server, no backend, no transpilation. Among Python, in the browser, in real.
The article covers everything from what PyScript is, how it works, what it can do now, and where it's headed.

What is PyScript?

PyScript is an open-source platform developed by Anaconda, Inc. and first unveiled by CEO Peter Wang at PyCon US 2022. It lets you embed Python code in HTML pages using standard <script> tags, and that code runs natively in the browser.

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://pyscript.net/releases/2025.11.1/core.css" />
  <script type="module" src="https://pyscript.net/releases/2025.11.1/core.js"></script>
</head>
<body>
  <h1>Hello from Python</h1>
  <script type="py">
    from pyscript import display
    display("Python is running in your browser!")
  </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

That's it. No build step. No npm install. No Webpack. Drop two tags in your <head> and start writing Python.

How does it actually work?

This is the part that makes PyScript genuinely interesting, it's not a gimmick or a transpiler. It's real Python, running through WebAssembly (WASM).

PyScript ships with two Python interpreters, both compiled to WebAssembly:

1. Pyodide, Full CPython in the browser

Pyodide is the original CPython interpreter compiled to WebAssembly. This is the actual CPython runtime that Python developers use on their machines, ported to run entirely inside a browser sandbox.

What this means in practice:

  • The full Python standard library is available
  • Most pure-Python packages from PyPI work out of the box
  • Scientific computing packages (NumPy, Pandas, Matplotlib, Scikit-learn) ship with pre-compiled WASM binaries
<script type="py">
  import numpy as np
  import matplotlib.pyplot as plt
  from pyscript import display

  x = np.linspace(0, 2 * np.pi, 100)
  y = np.sin(x)

  fig, ax = plt.subplots()
  ax.plot(x, y)
  display(fig, target="chart")
</script>
Enter fullscreen mode Exit fullscreen mode

2. MicroPython, Lean and mobile-friendly

MicroPython is a lightweight reimplementation of Python 3. Its WASM build is just ~170KB, dramatically smaller than Pyodide. This makes it the right choice for:

  • Mobile and tablet browsers with constrained resources
  • Fast initial page loads where startup time matters
  • Simpler apps that don't need the full CPython stdlib
<!-- Use type="mpy" for MicroPython instead of Pyodide -->
<script type="mpy">
  from pyscript import display
  display("Running MicroPython, tiny and fast!")
</script>
Enter fullscreen mode Exit fullscreen mode

Both interpreters implement the same FFI (Foreign Function Interface), so switching between them is mostly a matter of swapping type="py" for type="mpy".

DOM access and the FFI

The Foreign Function Interface (FFI) is PyScript's superpower. It creates a bi-directional bridge between Python and the browser's JavaScript environment, meaning Python can call JavaScript APIs, and JavaScript can call Python.

Manipulating the DOM from Python

<button id="my-btn">Click me</button>
<p id="output"></p>

<script type="py">
  from pyscript import document

  def on_click(event):
      output = document.getElementById("output")
      output.textContent = "Button clicked! Python handled it."

  btn = document.getElementById("my-btn")
  btn.addEventListener("click", on_click)
</script>
Enter fullscreen mode Exit fullscreen mode

Using the pyscript.web API (higher-level)

PyScript ships a Pythonic wrapper around DOM manipulation so you don't have to think in JavaScript at all:

from pyscript import when, display
from pyscript.web import page

@when("click", "#my-btn")
def handle_click(event):
    display("Clicked!", target="#output")
Enter fullscreen mode Exit fullscreen mode

Calling JavaScript from Python

The FFI lets you reach into the browser's native JS APIs:

from pyscript import window

# Call browser's built-in JS APIs directly
window.alert("Hello from Python!")
window.console.log("Logged from Python")

# Access localStorage
window.localStorage.setItem("key", "value")
Enter fullscreen mode Exit fullscreen mode

Calling Python from JavaScript

It's bi-directional — JS code can call Python functions too:

// In a <script> tag
const result = await pyscript.interpreter.run("2 + 2");
console.log(result); // 4
Enter fullscreen mode Exit fullscreen mode

Web Workers: non-blocking Python

One of PyScript's most practical features is first-class support for Web Workers. By default, Python runs on the browser's main thread. If you do anything CPU-intensive (think data crunching, image processing, ML inference), the UI freezes.

Workers move expensive computation off the main thread:

<!-- worker="true" runs this in a Web Worker -->
<script type="py" worker>
  import time

  # This heavy computation won't block the UI
  total = sum(i * i for i in range(10_000_000))

  from pyscript import display
  display(f"Result: {total}")
</script>
Enter fullscreen mode Exit fullscreen mode

Workers are isolated, each one gets its own Python interpreter. They can communicate back to the main thread via PyScript's messaging API, enabling patterns like streaming results to the UI as computation progresses.

Installing packages

PyScript supports installing packages from PyPI using a simple configuration block:

<script type="py" config='{"packages": ["pandas", "requests", "pillow"]}'>
  import pandas as pd
  import requests

  # These are real PyPI packages, running in your browser
  df = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})
  from pyscript import display
  display(df)
</script>
Enter fullscreen mode Exit fullscreen mode

Or use a separate <py-config> tag for cleaner HTML:

<py-config>
packages = ["numpy", "matplotlib", "scikit-learn"]
</py-config>
Enter fullscreen mode Exit fullscreen mode

For MicroPython, the mip package manager handles package installation in a way suited to MicroPython's smaller runtime.

The built-in terminal and editor

PyScript ships two interactive components out of the box.

Python Terminal

Add terminal to your script tag to get an interactive REPL embedded in your page:

<script type="py" terminal>
  # This renders a full interactive Python terminal on the page
  # Users can type Python directly into it
</script>
Enter fullscreen mode Exit fullscreen mode

Python Editor

The editor component gives users a full code editor (powered by CodeMirror) with a run button:

<py-editor>
import math
print(math.pi)
</py-editor>
Enter fullscreen mode Exit fullscreen mode

Both are ready for embedding into documentation, tutorials, or educational tools, no backend required.

Plugins

PyScript is built on a lean core called PolyScript. Most of its functionality is layered on top through a plugin system, which means the core stays small and efficient while the feature set can grow through community contributions.

Writing a PyScript plugin looks like this:

# my_plugin.py
from pyscript import Plugin

plugin = Plugin("my-plugin")

@plugin.setup
def setup():
    print("Plugin initialized!")
Enter fullscreen mode Exit fullscreen mode

This architecture keeps PyScript extensible without bloating the core, a clean separation of concerns.

PyScript vs. alternatives

PyScript Transcrypt Brython WASM (raw)
Real Python Yes No (transpiles) Yes No
PyPI packages Yes (Pyodide) No Limited No
DOM access Yes Yes Yes Manual
Mobile-friendly Yes (MicroPython) Yes Moderate
No build step Yes No Yes No
Maturity Growing fast Stable Stable Raw

The key differentiator is that PyScript runs actual Python not a Python-to-JS transpiler that emulates Python behaviour, and not a limited subset. You get real CPython semantics.

What can you build with PyScript today?

Interactive data dashboards -render Pandas DataFrames, Matplotlib charts, and Plotly visuals directly in the browser, shareable as a single HTML file.

Educational tools - interactive Python tutorials where learners run code in the browser without installing anything. Ideal for courses, workshops, and documentation.

Scientific notebooks - think lighter-weight Jupyter-in-the-browser experiences, without spinning up a Jupyter server.

Developer tools - embed Python-powered utilities (formatters, calculators, converters) into documentation sites.

Lightweight apps - with MicroPython, build simple Python apps that load instantly on mobile with a tiny footprint.

Honest limitations

PyScript is genuinely exciting, but a fair assessment includes its current constraints.

Initial load time. Pyodide's WASM bundle is several megabytes. First load can take a few seconds on slower connections. MicroPython's 170KB helps for lightweight use cases, but Pyodide's startup cost is a real UX concern for consumer-facing pages.

Not all packages work. Packages with C extensions that haven't been compiled to WASM won't install. The list of supported packages grows with each Pyodide release, but it's not PyPI-complete.

Browser sandbox limits. Python running in WASM is sandboxed by the browser, no filesystem access, no sockets, no subprocess. Some stdlib modules work differently or not at all.

Still maturing. PyScript is improving rapidly (releases almost monthly), but APIs do change between versions. Check the changelog before upgrading.

Getting started in 5 minutes

Create an index.html, paste this, open it in a browser:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>My First PyScript App</title>
  <link rel="stylesheet" href="https://pyscript.net/releases/2025.11.1/core.css" />
  <script type="module" src="https://pyscript.net/releases/2025.11.1/core.js"></script>
  <style>
    body { font-family: sans-serif; max-width: 600px; margin: 2rem auto; }
    button { padding: 0.5rem 1rem; font-size: 1rem; cursor: pointer; }
  </style>
</head>
<body>
  <h1>PyScript Demo</h1>
  <button id="btn">Run Python</button>
  <div id="output" style="margin-top: 1rem;"></div>

  <script type="py">
    from pyscript import when, display
    import random

    @when("click", "#btn")
    def on_click(event):
        number = random.randint(1, 100)
        display(f"Python picked a random number: {number}", target="#output")
  </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

No npm. No build step. Just a file. Open it. Python runs.

For a richer sandbox, try pyscript.com, the official online editor with sharing and examples built in.

The bottom line

PyScript isn't trying to replace JavaScript. It's doing something more interesting: it's giving the 8+ million Python developers in the world a path to the browser without abandoning the language and ecosystem they already know.

For data scientists who want to share interactive work without a Jupyter server. For educators who want students to run code without installing anything. For developers who want to embed Python-powered tools into a docs site. PyScript is already viable for these use cases today, and getting better fast.

The browser just got a new language.

Resources

Have you tried PyScript? What did you build with it? Drop your experience in the comments, especially curious about anyone who used it for data science or education projects.

Top comments (0)