Python in Your Browser with Pyodide, Explained
After reading this you will understand how a full Python 3 runtime runs inside your browser tab, what it can and cannot do, and how to load numpy, pandas and matplotlib without installing anything.
What the Python Playground actually is
The Python Playground runs CPython compiled to WebAssembly through a project called Pyodide. When you open the page, your browser downloads a runtime of about 15 MB, unpacks it, and starts a real Python interpreter. Nothing runs on a remote server. The code you type executes on your own CPU, in a sandbox inside the tab.
Here is the hook. Paste these three lines, press Ctrl+Enter, and a chart appears below the console:
import matplotlib.pyplot as plt, plt.plot([0, 1, 4, 9, 16]), plt.show(). The first import triggers a background download of matplotlib and its dependencies (roughly another 10 MB), then the plot renders inline. No Jupyter, no pip, no local install.
WebAssembly is a portable binary instruction format that browsers execute at close to native speed. Pyodide compiles the CPython interpreter, plus C extensions like numpy, into that format. That is why numeric code runs fast even though it lives in a web page.
When to use it, and when not to
Reach for the Playground when the setup cost of running Python locally is larger than the task itself. Checking a regex, reshaping a CSV, testing a numpy broadcasting rule, sketching a quick plot for a report: all of these take under a minute here and leave nothing installed.
It is also useful when you cannot install software at all, for example on a locked-down work laptop or a borrowed machine, or when you want a link a colleague can open and run without a Python environment.
Do not use it for three things. First, anything needing network access from Python: requests, socket and urllib cannot open real connections in the sandbox. Second, long or memory-heavy jobs: a browser tab typically has a few hundred MB of comfortable headroom, not the tens of gigabytes a large dataset wants. Third, packages with compiled extensions that Pyodide does not ship. About 250 such packages are prebuilt, so most scientific work is covered, but a random C-backed library from PyPI will fail to install.
How package loading works
There are two separate mechanisms, and mixing them up is the most common source of confusion.
- Prebuilt Pyodide packages
- numpy, pandas, matplotlib, scipy, scikit-learn and about 250 more are compiled to WebAssembly ahead of time. They load automatically the moment you
importthem. You do not call any installer. - micropip for pure-Python wheels
- Packages written in pure Python (no C to compile) install from PyPI at runtime. Run
import micropip, thenawait micropip.install("package-name"). This fetches the wheel over HTTP and unpacks it into the sandbox filesystem.
The size cost is real and worth planning for. The bare runtime is about 15 MB. Importing numpy adds a few MB, pandas and matplotlib together push the total past 30 MB on first load. After that, your browser caches the files, so the second visit starts in a second or two rather than ten.
If await micropip.install(...) raises ValueError: Can't find a pure Python 3 wheel, the package needs compilation and is not in the Pyodide set. There is no workaround inside the browser. Pick a pure-Python alternative or run that code locally.
The sandbox and the virtual filesystem
Your code sees a filesystem, but it is an in-memory one that Pyodide builds inside the tab. Writing to /tmp/out.csv works and reading it back works, but that path has no connection to any folder on your disk. When you close the tab, the virtual filesystem is gone.
To analyze your own data, upload a file into the sandbox, then open it by name with pandas. State persists between runs in the same session, so a DataFrame you build in one run is still there in the next, exactly like cells in a notebook. This is convenient and it is also a trap: a variable you thought you deleted may still be defined from an earlier run. When results look stale, reload the page to reset the interpreter.
Three capabilities are absent by design. The Playground cannot read your real files, cannot open network sockets, and cannot spawn subprocesses. That is what makes it safe to run code a stranger pasted in. It is also why os.system, multiprocessing and live HTTP calls do not work.
A worked example you can reproduce
The demo button loads a short pandas and matplotlib script. Run it with Ctrl+Enter and follow the numbers below.
Grouping sales by region
The demo builds a small DataFrame of six sales rows across three regions and computes the total per region.
- Create the data: regions
["N", "S", "N", "E", "S", "N"]with amounts[10, 40, 20, 15, 35, 30]. - Group by region and sum the amounts. North is
10 + 20 + 30 = 60. South is40 + 35 = 75. East is15. - The grand total is
60 + 75 + 15 = 150, which matches summing the six raw amounts. - Plot the three group totals as a bar chart. matplotlib renders it inline below the console.
The mean sale is \bar{x} = 150 / 6 = 25. North sits exactly on that mean per row on average (60 / 3 = 20), South is highest per row (75 / 2 = 37.5), and East has a single row of 15.
Reading the output panel
Three kinds of output land in different places, and knowing which is which saves confusion.
Anything you print() goes to the console as plain text, in order. The value of the last expression in a run is also echoed, the way a REPL does, so typing df.head() on its own line shows the table without a print call. Calls to plt.show() flush the current figure to an inline image below the text output. If you build a plot but never call show(), nothing appears, because the figure is still buffered.
Errors print a full Python traceback. Read it from the bottom up: the last line names the exception type and message, and the lines above trace the call stack. A ModuleNotFoundError means the package is neither prebuilt nor installed. A MemoryError means the tab ran out of room; reduce the dataset or reload.
Common mistakes
Four errors account for most failed runs.
Forgetting await with micropip. micropip.install returns a coroutine. Writing micropip.install("attrs") without await does nothing useful and the package stays absent. The Playground runs top-level await, so await micropip.install("attrs") is correct.
Expecting network calls to work. requests.get("https://example.com") fails because the sandbox has no sockets. If you need remote data, download it to a file first and upload it.
Stale state between runs. Because variables persist like notebook cells, a bug can hide behind an old definition. When a change has no effect, reload the page to get a clean interpreter.
Missing plt.show(). A plot with no show() call produces no image. Add the call, or call it once at the end after building all axes.
Related tools
If your goal is a diagram rather than a computation, the Flowchart & Diagram Editor draws shapes, arrows and labels and exports SVG or PNG, which is a better fit than plotting boxes with matplotlib.
Frequently asked questions
Is my code or data sent to a server?
No. The runtime downloads from a CDN once, then everything executes in your browser. Code you type and files you upload never leave your device.
Why is the first run so slow?
The first run downloads the roughly 15 MB runtime, plus a few MB per scientific package you import. On a typical connection that is 8 to 12 seconds. The browser caches the files, so later visits start in about 1 to 2 seconds.
Can I install any package from PyPI?
Only pure-Python wheels install via micropip. Packages with C extensions work only if Pyodide prebuilt them, and about 250 are prebuilt, including numpy, pandas, scipy and scikit-learn.
Does state carry over between runs?
Yes, within the same page session. Variables, imports and DataFrames stay defined until you reload the page, which resets the interpreter to a clean state.
How much memory can I use?
Enough for typical analysis, usually a few hundred MB in practice. Very large datasets can trigger a MemoryError. Sample or chunk your data if you hit it.