The PyEmscripten Platform#

What is the PyEmscripten Platform?#

The PyEmscripten platform defines a binary interface for an Emscripten application. If an Emscripten application and an Emscripten shared library are both built to target this platform, then the application will be able to load and run the shared library.

To build a shared library that is compatible with a given version of Pyodide, it is necessary to identify which PyEmscripten platform that version of Pyodide uses and follow the corresponding shared library build instructions. This allows wheels to be built for Pyodide that will load and run correctly.

The Emscripten compiler makes no ABI stability guarantees between versions, and several linker flags can adjust the ABI. Therefore, Python packages built for Emscripten must match the ABI-sensitive compiler and linker flags used to build the interpreter to avoid load-time or run-time errors.

To balance ABI stability needs of package maintainers with flexibility to adopt new platform features and bug fixes, Pyodide adopts a new PyEmscripten platform for each feature release of Python. The platform tags take the form pyemscripten_${YEAR}_${PATCH}_wasm32 (e.g., pyemscripten_2026_0_wasm32 for Python 3.14).

Each PyEmscripten platform specifies:

  • the version of the Emscripten compiler to be used,

  • what libraries are statically linked to the application,

  • the stack unwinding ABI to be used,

  • how the loader handles dependency lookup, and

  • various additional compile and linker flags

The PyEmscripten platform definition does not include anything about Python and in particular it is agnostic to the Python version it is intended to be used with. However, for clarity we indicate in the platform documentation which Python version we plan to use each platform with.

The PyEmscripten platform is specified by PEP 783, and wheels using this tag are supported on PyPI. Prior to PEP 783, it was called the Pyodide platform instead of the PyEmscripten platform (and used the form pyodide_${YEAR}_${PATCH}_wasm32).

Platform Versions#

For background on why specific flags were chosen, see ABI-sensitive flags.

General#

Building for the Emscripten target#

To build C/C++ projects, use the Emscripten compiler toolchain emcc.

To build Rust projects, use rustc --target wasm32-unknown-emscripten or cargo build --target wasm32-unknown-emscripten. When building, emcc must be on the path or linking will fail.

Making a shared library#

Emscripten shared libraries use the the WebAssembly binary format and have a dynamic linking section.

emcc will ignore the -shared flag. To make a shared library with emcc, you must pass -sSIDE_MODULE=1 or -sSIDE_MODULE=2.

With Rust version 1.95.0 and later, Rust will automatically detect that the crate is a cdylib and link an Emscripten shared library. For older Rust versions, to make a shared library with rustc, pass -C link-arg=-sSIDE_MODULE=2. To build a shared library with cargo, put -C link-arg=-sSIDE_MODULE=2 in the RUSTFLAGS environment variable.

Static Libraries Linked to the Main Binary#

Many libraries cannot be dynamically linked on the Emscripten platform, most frequently because they contain JavaScript code. If they are to be made available for packages to use, they must be statically linked to the interpreter. There are also a few libraries that are needed for a Python builtin module. As a part of the platform definition, we document which versions of which libraries are linked to the main binary.

Other Library Dependencies#

Other library dependencies should either be statically linked to the extension module or built as a shared library and vendored into the wheel in a .libs directory. Like on native platforms, the RPATH of the dependent extension module should also be set as appropriate to ensure that the dependency is resolved correctly.

No pthreads support#

-pthread must not be used at compile or link time. If -pthread is used, the resulting libraries will not load.

Controlling the Set of Exported Symbols#

All symbols that form part of a binary module’s interface must be exported. It is desirable to produce the minimal list of exported symbols to keep download size and runtime to a minimum. Not exporting symbols also reduces chances of symbol collisions.

Linking a shared libraries with -sSIDE_MODULE=1 will pass -whole-archive to wasm-ld and so force inclusion of all object files and all symbols. Linking with -sSIDE_MODULE=2 will only include symbols that are explicitly listed with -sEXPORTED_FUNCTIONS=<export list>. The name of each symbol in the list must be prefixed with an underscore. For the smallest result, it is recommended to link with:

-sSIDE_MODULE=2 -sEXPORTED_FUNCTIONS=["_PyInit_MyCModule1", "_PyInit_MyCModule2]