This and PEP-783 do remind me a bit of the story of watt (1) and serde_derive, where the latter was published containing a to WebAssembly compiled proc macro with the former as WebAssembly runtime (2).
It tried amonst others to improve isolation and long compile times in a fairly foundational Rust library which can be found in many dependency trees. I found it a cool proof of concept at the time.
Having a WebAssembly binary embedded in a library was relatively unpopular in the Rust community (3). serde_derive 1.0.184 restored the uncompiled source version, but the release notes mention they hope that crates.io (Rust equivalent of PyPi) will add WebAssembly support in the future.
One of the reasons why this wasn't very popular was that WebAssembly is much harder to inspect than Rust source code (4).
I'm not a PyPi expert. The PEP itself seems to permit adding WebAssembly to a wheel (a python package). The PEP literally mentions "There are no security implications in this PEP" (security for whom?). In 2022 the supply chain attack surface was notably smaller, since powerful enough LLM's didn't exist yet.
I think other forms of binaries were already permitted, such as precompiled C/C++ libraries, so if that's true, then this is indeed relatively not that big of a security concern, but _no_ security implications seems to be a bit much.
This means we can now take any C/Rust/whatever extension for Python, compile that as a `.wasm` extension, and then load it directly in browser Pyodide projects using:
You can tell that it's got compiled code in (and not just Python) by running:
pydantic_core._pydantic_core
I get this:
<module 'pydantic_core._pydantic_core' from '/lib/python3.14/site-packages/pydantic_core/_pydantic_core.cpython-314-wasm32-emscripten.so'>
simonw 15 hours ago [-]
I had an older experimental Pyodide WASM project lying around (a packaging of the Luau language by Roblox) so I had Codex package that up for me and pushed it to PyPI.
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
'''))
`await micropip.install()` is starting to feel dangerously close to "just ship the whole universe to the browser."
rtpg 11 hours ago [-]
Is there any form of client-side caching that kicks in with all of this flow?
Tbh I don't feel great about people just writing up a bunch of scripts pulling things just on every run.
simonw 11 hours ago [-]
Browser caching works with stuff pulled from PyPI, so it shouldn't get loaded more than once.
njoyablpnting 10 hours ago [-]
Pyodide is great. I teach coding to kids, mostly creating 2d games with Python, and it was always a pain to manage an environment for each student. Now I have a browser based environment that runs Pygame/Arcade/Pyglet in Pyodide, so the kids can just do everything in the browser, I don't have to worry about Python versions, OS differences, files, etc. As a bonus they can easily publish what they make since it all runs in browser.
They haven't made anything too crazy, but performance is surprisingly good, even wiring in Pymunk for some physics stuff. If they get to the point where it's ever an issue they probably know enough to be working in a real game engine anyway.
IshKebab 2 hours ago [-]
Uv mostly solved the Python environment mess for what it's worth.
12_throw_away 16 hours ago [-]
Executing normal python programs inside a cpython vm inside a wasm context inside a javascript process inside a sandbox inside a browser is - genuinely - extremely exciting! (Might as well run the browser inside a container inside a VM while you're at it though.)
That video was a major inspiration for me and probably indirectly led to me becoming a Pyodide maintainer.
willXare 14 hours ago [-]
We heard you like runtimes, so we put a runtime in your runtime inside your runtime.
shevy-java 6 hours ago [-]
So many babushka layers.
rvz 15 hours ago [-]
This sounds like a solution looking for an unnecessary security nightmare.
Something as little as the runtime can just get exploited (which that as happened.) and cause a sandbox escape on the client side. There was a Chrome 0day at the runtime level which allowed untrusted code to run and escape the sandbox in the WASM runtime.
This complete worship of WASM (and their runtimes) as this magical silver bullet reminds me of the days and failures of Native Client (NaCL), Java Applets and Flash all over again.
I dunno, one sandbox escape in nine years is a pretty solid track record IMO.
Any reason WASM is more dangerous than regular JavaScript?
1 hours ago [-]
rvz 11 hours ago [-]
No. This one. [0].
Even before that, there are several other sandbox escapes that predated the one you posted. [1] [2] and this one [3] can be used to trivially escape its sandbox with either of these vulnerabilities.
So it is not the magical silver bullet one may easily think it is.
> Something as little as the runtime can just get exploited (which that as happened.) and cause a sandbox escape on the client side.
Sandbox escapes could happen in Javascript too, right? But I don't see people avoiding browsing the web because of that
zek 12 hours ago [-]
I've been working on a server-side wasm impl of cpython called boomslang [1] and have been thinking a lot lately about packaging, one of the downsides of my current impl is the need to statically link all c/rust extensions. Its too bad IMO how much of the wasm ecosystem targets/depends on emscripten directly. It'd be interesting to see if a more generic ABI could be provided for non emscripten/js based wasm runtimes.
The WASI support in CPython has moved along very well and it is an early target via componentize-py[1]. Notes on WASI support in Python can be found in PEP 816[2]; CPython will be jumping from 0.1 to 0.3 (0.2 is adapted in componentize-py) which should unlock a fair bit of support, especially once cooperative threads lands (providing a pthreads impl in wasi-libc).
it’s great to see this stuff moving forward, I’m just impatient for it to all land! Plus one of the challenges IMO is that you also still need support in the runtimes, which takes time. Hopefully the state of the world will look better for WASI in a few months/years.
tancop 7 hours ago [-]
wasi 0.3 just came out and its fully component model based with no emscripten specific parts. also supports dynamic linking in the spec but afaik no runtime actually has it implemented in a released version.
DarkUranium 11 hours ago [-]
I think one of the issues is that WASM is notoriously hard to generate code for because they decided to use an IR that's fundamentally incompatible with literally any existing native compiler backend's IR (not counting very specialized ones or toy direct-ast-to-machine-code compilers).
It feels like nobody actually consulted actual compiler writers when designing this. I'm sure that isn't true, but it definitely feels that way. (I suspect the truth is that they were consulted, but ignored.)
It means codegen needs to resort to all sorts of hacks (like the relooper) in order to target WASM, a property not shared by any other target.
And apparently, the way they handle variables also results in deoptimization, though I don't recall the details of that.
Add the fact that interacting with the browser on the web still has to go via JavaScript to this day (for the most part, at least), and, well.
---
TL;DR a combination of poor IR design that has a massive impedance mismatch with pre-existing compilers (and most new ones, because it turns out there's a reason the WASM approach isn't standard) plus WASM still being a second-class citizen in its supposed primary environment (the 'W' in WASM) --- the former ensures targeting it consumes a lot of resources/time, the latter ensures the bar for that to be worth it is much higher.
You can target most architectures with little trouble (at least a a baseline --- optimization's a hard problem regardless of target, except maybe SPIR-V due to the recommendation that pre-optimization is limited in scope). But WASM is completely out there, it's closer to trying to target e.g. Java (not JVM!) at the backend instead of machine code or some other IR.
You don't make an IR intended to be targeted by existing/native compilers by making it completely different to anything they had to target before and completely different to their own IRs and representations ... unless you're the guys behind WASM.
ameliaquining 11 hours ago [-]
I don't think the controversy about Wasm's structured control flow has anything to do with any of this? It's not actually difficult to target Wasm in codegen; I've never heard of any real-world compiler project complaining that this was a major burden. ABI concerns are at a different level.
Most low-level IRs don't do structured control flow because most low-level IRs don't need to be translatable to verified-safe machine code in a single fast pass, whereas for WebAssembly that's a core design requirement.
csande17 10 hours ago [-]
http://troubles.md/posts/why-do-we-need-the-relooper-algorit... has a more detailed version of this argument: if WebAssembly had used a CFG as the basis for its control flow, it would have been easier to compile to and easier to efficiently execute/translate while maintaining safety, and maybe GCC would have released a WebAssembly backend by now.
The author alleges that the real reason WebAssembly uses loop/block is because that's how V8 worked internally at the time and Google didn't want to go to the trouble of implementing something different. But more recently V8 has started moving towards CFGs ( https://v8.dev/blog/leaving-the-sea-of-nodes ) so maybe there's hope in the future.
hmry 11 hours ago [-]
From what I remember, it was specifically chosen (among other reasons) because of experience with the JVM, where it was difficult to verify bytecode type-safety due to unrestricted jumps and branches.
So the choice was made to put the burden of regularizing the control flow on the compilers at compile time, rather than the browser engine at website load time. Which seems rational to me.
willXare 14 hours ago [-]
Python in the browser keeps sounding ridiculous right up until it works.
IshKebab 2 hours ago [-]
Eh still sounds pretty ridiculous to me. Do you like to use the slowest language in popular use? What if I told you we could make it even slower?
wolfgangK 16 hours ago [-]
I presume this works (will work) also for JupyterLite that is based on Pyodide ?
Would be great if it helped getting the latest OpenCV-python version [0] and it's dnn goodies being available on a zero-install client side Notebook !
It tried amonst others to improve isolation and long compile times in a fairly foundational Rust library which can be found in many dependency trees. I found it a cool proof of concept at the time.
Having a WebAssembly binary embedded in a library was relatively unpopular in the Rust community (3). serde_derive 1.0.184 restored the uncompiled source version, but the release notes mention they hope that crates.io (Rust equivalent of PyPi) will add WebAssembly support in the future.
One of the reasons why this wasn't very popular was that WebAssembly is much harder to inspect than Rust source code (4).
I'm not a PyPi expert. The PEP itself seems to permit adding WebAssembly to a wheel (a python package). The PEP literally mentions "There are no security implications in this PEP" (security for whom?). In 2022 the supply chain attack surface was notably smaller, since powerful enough LLM's didn't exist yet.
I think other forms of binaries were already permitted, such as precompiled C/C++ libraries, so if that's true, then this is indeed relatively not that big of a security concern, but _no_ security implications seems to be a bit much.
(1) https://crates.io/crates/watt
(2) https://github.com/serde-rs/serde/commit/1afae183b06ffe47d05...
(3) https://github.com/serde-rs/serde/issues/2538
(4) https://old.reddit.com/r/rust/comments/15wx2xe/precompiled_b...
(5) https://peps.python.org/pep-0783/
This means we can now take any C/Rust/whatever extension for Python, compile that as a `.wasm` extension, and then load it directly in browser Pyodide projects using:
Here's how to try the new feature out. Visit https://pyodide.org/en/stable/console.html and type: That gets you this WASM wheel: https://pypi.org/project/pydantic_core/#pydantic_core-2.47.0...You can tell that it's got compiled code in (and not just Python) by running:
I get this:Here's the package: https://pypi.org/project/luau-wasm/
And an interactive demo page where you can try it out: https://simonw.github.io/luau-wasm/Wrote about this in more detail on my blog: https://simonwillison.net/2026/Jun/13/publishing-wasm-wheels...
Tbh I don't feel great about people just writing up a bunch of scripts pulling things just on every run.
They haven't made anything too crazy, but performance is surprisingly good, even wiring in Pymunk for some physics stuff. If they get to the point where it's ever an issue they probably know enough to be working in a real game engine anyway.
Something as little as the runtime can just get exploited (which that as happened.) and cause a sandbox escape on the client side. There was a Chrome 0day at the runtime level which allowed untrusted code to run and escape the sandbox in the WASM runtime.
This complete worship of WASM (and their runtimes) as this magical silver bullet reminds me of the days and failures of Native Client (NaCL), Java Applets and Flash all over again.
I dunno, one sandbox escape in nine years is a pretty solid track record IMO.
Any reason WASM is more dangerous than regular JavaScript?
Even before that, there are several other sandbox escapes that predated the one you posted. [1] [2] and this one [3] can be used to trivially escape its sandbox with either of these vulnerabilities.
So it is not the magical silver bullet one may easily think it is.
[0] https://nvd.nist.gov/vuln/detail/CVE-2026-11645
[1] https://blog.ret2.io/2021/06/02/pwn2own-2021-jsc-exploit/
[2] https://issues.chromium.org/issues/40091185
[3] https://phrack.org/issues/72/10_md#article
Sandbox escapes could happen in Javascript too, right? But I don't see people avoiding browsing the web because of that
[1] https://github.com/HubSpot/boomslang
[1] https://github.com/bytecodealliance/componentize-py [2] https://peps.python.org/pep-0816/
It feels like nobody actually consulted actual compiler writers when designing this. I'm sure that isn't true, but it definitely feels that way. (I suspect the truth is that they were consulted, but ignored.)
It means codegen needs to resort to all sorts of hacks (like the relooper) in order to target WASM, a property not shared by any other target.
And apparently, the way they handle variables also results in deoptimization, though I don't recall the details of that.
Add the fact that interacting with the browser on the web still has to go via JavaScript to this day (for the most part, at least), and, well.
---
TL;DR a combination of poor IR design that has a massive impedance mismatch with pre-existing compilers (and most new ones, because it turns out there's a reason the WASM approach isn't standard) plus WASM still being a second-class citizen in its supposed primary environment (the 'W' in WASM) --- the former ensures targeting it consumes a lot of resources/time, the latter ensures the bar for that to be worth it is much higher.
You can target most architectures with little trouble (at least a a baseline --- optimization's a hard problem regardless of target, except maybe SPIR-V due to the recommendation that pre-optimization is limited in scope). But WASM is completely out there, it's closer to trying to target e.g. Java (not JVM!) at the backend instead of machine code or some other IR.
You don't make an IR intended to be targeted by existing/native compilers by making it completely different to anything they had to target before and completely different to their own IRs and representations ... unless you're the guys behind WASM.
Most low-level IRs don't do structured control flow because most low-level IRs don't need to be translatable to verified-safe machine code in a single fast pass, whereas for WebAssembly that's a core design requirement.
The author alleges that the real reason WebAssembly uses loop/block is because that's how V8 worked internally at the time and Google didn't want to go to the trouble of implementing something different. But more recently V8 has started moving towards CFGs ( https://v8.dev/blog/leaving-the-sea-of-nodes ) so maybe there's hope in the future.
So the choice was made to put the burden of regularizing the control flow on the compilers at compile time, rather than the browser engine at website load time. Which seems rational to me.
[0] https://news.ycombinator.com/item?id=48421858
Now Python's versioning scheme is officially worse than PHP's or Perl's. Not just skipping one version here - they are skipping 314 versions!