Publishing WASM wheels to PyPI for use with Pyodide

Simon Willison's Blog News

Summary

PyPI now supports publishing WebAssembly wheels for Pyodide, allowing package maintainers to distribute their own WASM packages directly. The article demonstrates this with the luau-wasm package.

No content available
Original Article
View Cached Full Text

Cached at: 06/14/26, 07:34 AM

# Publishing WASM wheels to PyPI for use with Pyodide Source: [https://simonwillison.net/2026/Jun/13/publishing-wasm-wheels/](https://simonwillison.net/2026/Jun/13/publishing-wasm-wheels/) 13th June 2026 The[Pyodide 314\.0 release announcement](https://blog.pyodide.org/posts/314-release/)\(via[Hacker News](https://news.ycombinator.com/item?id=48462759)\) includes news I’ve been looking forward to for a long time: > You can now publish Python packages built for Pyodide \(or any Python runtime compatible with[the PyEmscripten platform defined in PEP 783](https://pyodide.org/en/stable/development/abi.html)\) directly to PyPI and install them at runtime\. Previously, the Pyodide maintainers had to maintain, build, and host over 300 packages ourselves\. This created a significant burden on our maintainers and became a major bottleneck for the community, as every new package required manual review\. Moving forward, package maintainers can simply build and publish Pyodide wheels to PyPI, just as they do for native wheels on Linux, macOS, or Windows\. Here’s the[PR to PyPI itself supporting this](https://github.com/pypi/warehouse/pull/19804), which landed on April 21st\. I adore[Pyodide](https://pyodide.org/), and have been frustrated in the past by this limitation\. It’s possible to compile C or Rust extensions to WASM in a wheel file, but before now there was no easy way to distribute them\. Thanks to the efforts of a whole lot of people, that’s now been fixed\! #### Trying it out with luau\-wasm I decided to celebrate by finding something I could package\. I have quite a few experimental Pyodide projects lying around, but the best fit for this looked to be my[Luau WebAssembly research spike](https://github.com/simonw/research/tree/main/pluau-wasm-pyodide#readme)from 9th March\. [Luau](https://luau.org/)is a “small, fast, and embeddable programming language based on Lua with a gradual type system”,[developed by Roblox](https://luau.org/news/2022-11-04-luau-origins-and-evolution/)and released under an MIT license\. It’s written in C\+\+\. I already knew it was possible to compile it to WebAssembly and get it running inside of Pyodide, so I[set Codex \+ GPT\-5\.5 xhigh](https://gist.github.com/simonw/1761eab6ba11d4053f56f955a28ad76b)the task of packaging my experiment up and publishing it to PyPI using GitHub Actions\. It took some iteration, but here’s the result:[luau\-wasm](https://pypi.org/project/luau-wasm/)is a brand new PyPI package which publishes a 276KB`luau\_wasm\-0\.1a0\-cp314\-cp314\-pyemscripten\_2026\_0\_wasm32\.whl`file which can be used in Pyodide like this: ``` import micropip await micropip.install("luau-wasm") import luau_wasm print(luau_wasm.execute(r''' local animals = {"fox", "owl", "frog", "rabbit"} table.sort(animals, function(a, b) return #a < #b end) for i, name in animals do print(i .. ". " .. name .. " (" .. #name .. ")") end ''')) ``` You can run that code[in the Pyodide REPL demo](https://pyodide.org/en/stable/console.html)to see it in action\. The[GitHub repo for luau\-wasm](https://github.com/simonw/luau-wasm)includes all of the build and deploy scripts \(using the latest[cibuildwheel](https://github.com/pypa/cibuildwheel)\) and also deploys an HTML demo page which loads Pyodide, installs`luau\-wasm`and provides an interface for trying it out:[https://simonw\.github\.io/luau\-wasm/](https://simonw.github.io/luau-wasm/) ![Screenshot of a web app titled "Luau WASM" with subtitle "Run Luau in the browser through Pyodide after installing the luau-wasm WebAssembly wheel from PyPI." A green "Ready" status badge is at top right. Below are example buttons: "Hello World", "Variables", "Tables", "Fibonacci", "Runtime Error". A "LUAU SOURCE" code editor contains: local function fib(n: number): number / if n < 2 then return n end / return fib(n - 1) + fib(n - 2) / end / local out = {} / for i = 0, 12 do / table.insert(out, tostring(fib(i))) / end / print(table.concat(out, ", ")). On the right is an "OUTPUT" panel with a "Copy" button showing dark terminal output: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144. At the bottom left are a blue "Run" button, a "Clear" button, and the text "6.0 ms".](https://static.simonwillison.net/static/2026/luau-wasm.jpg) #### How many packages are using this so far? I was curious to see how many packages are currently publishing wheels for this platform\. After some[tinkering with ChatGPT](https://chatgpt.com/share/6a2dee92-b110-83e8-9f53-ba9259b751ed)I got to[this BigQuery SQL](https://gist.github.com/simonw/4a34a49fca63bc09c50bc31e17b3d33b?permalink_comment_id=6198602#gistcomment-6198602)which I ran against PyPI’s[public dataset on BigQuery](https://packaging.python.org/en/latest/guides/analyzing-pypi-package-downloads/#public-dataset)\. Here’s the[raw JSON](https://gist.github.com/simonw/4a34a49fca63bc09c50bc31e17b3d33b)of query results and here’s a SQLite SQL query[in Datasette Lite](https://lite.datasette.io/?json=https://gist.github.com/simonw/4a34a49fca63bc09c50bc31e17b3d33b#/data?sql=select%0A++name%2C%0A++platform_tag%2C%0A++matching_file_count%2C%0A++max%28latest_upload%29+as+latest_upload%2C%0A++example_files%0Afrom+%28%0A++--+your+existing+query+here%2C+without+order+by%0A++select%0A++++name%2C%0A++++platform_tag%2C%0A++++matching_file_count%2C%0A++++latest_upload%2C%0A++++example_files%0A++from+raw%0A%29%0Agroup+by+name%0Aorder+by+latest_upload+desc%3B)which dedupes packages by most recent upload date\. If the query is right, there are currently 28 PyPI packages publishing with the new`pyemscripten\_202\*\_wasm32`tags: [luau\-wasm](https://pypi.org/project/luau-wasm/),[uuid7\-rs](https://pypi.org/project/uuid7-rs/),[cmm\-16bit](https://pypi.org/project/cmm-16bit/),[pyOpenTTDAdmin](https://pypi.org/project/pyOpenTTDAdmin/),[imgui\-bundle](https://pypi.org/project/imgui-bundle/),[numbertoolkit](https://pypi.org/project/numbertoolkit/),[bashkit](https://pypi.org/project/bashkit/),[geoarrow\-rust\-core](https://pypi.org/project/geoarrow-rust-core/),[arro3\-io](https://pypi.org/project/arro3-io/),[arro3\-core](https://pypi.org/project/arro3-core/),[arro3\-compute](https://pypi.org/project/arro3-compute/),[onnx](https://pypi.org/project/onnx/),[powerfit\-em](https://pypi.org/project/powerfit-em/),[tcod](https://pypi.org/project/tcod/),[chonkie\-core](https://pypi.org/project/chonkie-core/),[tokie](https://pypi.org/project/tokie/),[robotraconteur](https://pypi.org/project/robotraconteur/),[pydantic\_core](https://pypi.org/project/pydantic_core/),[yaml\-rs](https://pypi.org/project/yaml-rs/),[cadquery\-ocp\-novtk\-OCP\.wasm](https://pypi.org/project/cadquery-ocp-novtk-OCP.wasm/),[uuid\_utils](https://pypi.org/project/uuid_utils/),[base64\_utils](https://pypi.org/project/base64_utils/),[pycdfpp](https://pypi.org/project/pycdfpp/),[lib3mf\-OCP\.wasm](https://pypi.org/project/lib3mf-OCP.wasm/),[typst](https://pypi.org/project/typst/),[toml\-rs](https://pypi.org/project/toml-rs/),[onnx\-weekly](https://pypi.org/project/onnx-weekly/),[dummy\-pyodide\-ext\-test](https://pypi.org/project/dummy-pyodide-ext-test/) Here’s hoping we see a whole lot more of those showing up over the coming months and years\.

Similar Articles

Running Python code in a sandbox with MicroPython and WASM

Simon Willison's Blog

Simon Willison introduces micropython-wasm, an alpha package that runs Python code in a sandbox using MicroPython compiled to WebAssembly, enabling safe plugin execution within Python applications like Datasette.

luau-wasm 0.1a0

Simon Willison's Blog

luau-wasm 0.1a0, a WebAssembly build of the Luau scripting language, has been released.

micropython-wasm 0.1a0

Simon Willison's Blog

micropython-wasm 0.1a0 released, enabling MicroPython to run in WebAssembly environments for sandboxing and portability.

micropython-wasm 0.1a2

Simon Willison's Blog

MicroPython ported to WebAssembly as a tool for sandboxed Python execution in the browser.

Running Python ASGI apps in the browser via Pyodide + a service worker

Simon Willison's Blog

This project demonstrates how to run Python ASGI web applications entirely in the browser using Pyodide and a service worker, intercepting requests under a path and executing them via the ASGI protocol. Examples include FastAPI and the full Datasette app.