WebAssembly can extend Python as an architecture-independent alternative to native C extensions, enabling two main use cases: accelerating pure Python hot spots (~10x speedup) and embedding capabilities from other languages without requiring a native toolchain on the host. The preferred runtime is wasmtime-py (version 40), which ships prebuilt binaries for Windows, macOS, and Linux on x86-64 and ARM64 but weighs ~18MiB and has a frequently breaking API. A critical pitfall is that Wasm runtimes treat all integers as signed, so pointers returned from Wasm malloc must be masked with `& 0xffffffff` to avoid silent writes to wrong addresses. The article also covers using a bump allocator instead of malloc for cleaner memory management, and demonstrates embedding the Monocypher cryptography library compiled to Wasm as a practical example of the embedded-capabilities pattern.

13m read timeFrom nullprogram.com
Post cover image
Table of contents
Usage examples and gotchasWebAssembly as faster PythonWebAssembly as embedded capabilities

Questions this post answers

Why do pointers returned from Wasm malloc come back as negative numbers in Python?

Wasm runtimes interpret all integers as signed, and Wasm makes no distinction between pointers and integers. Because addresses start at 0 and the upper half of a 32-bit address space has the high bit set, those addresses appear negative when treated as signed. Every pointer coming out of Wasm must be masked: `pointer = malloc(...) & 0xffffffff` for wasm32, or `>>> 0` in JavaScript. Developers shipping Wasm-backed Python libraries track footguns like this on daily.dev before they hit production.

What is the wasmtime-py package size and which platforms does it support?

wasmtime-py (version 40) installs at approximately 18 MiB and ships prebuilt binaries for Windows, macOS, and Linux on both x86-64 and ARM64, covering nearly all Python installations. No native C toolchain is required on the host. The API breaks on a roughly monthly basis, so projects must stay current to avoid bitrot. Teams evaluating Python packaging trade-offs for Wasm runtimes find comparisons like this on daily.dev.

How do I compile a C library like Monocypher to WebAssembly without libc for use in Python?

Compile with Clang targeting wasm32, disabling the standard library and specifying no entry point: `clang --target=wasm32 -nostdlib -O2 -Wl,--no-entry -Wl,--export-all -o monocypher.wasm monocypher.c`. The `--export-all` flag exposes all externally-linked symbols as the Wasm interface. A bump allocator can be added in a wrapper translation unit to manage memory without shipping a full general-purpose allocator inside the Wasm binary. Developers embedding C libraries into Python via Wasm find practical patterns like this on daily.dev.

2 Impressions