WebAssembly, or Wasm, gives developers a way to create programs that run at near-native speed in the browser or anywhere else you can deploy the WebAssembly runtime. But you generally don’t write programs in Wasm directly. Instead, you write programs in other languages— some better suited to being translated to Wasm than others—and compile them with Wasm as the target.

These six languages (I count C and C++ as two) can all be deployed onto Wasm runtimes via different tooling, and with different degrees of ease and compatibility. If you want to explore using Wasm as a deployment target for your code, you’ll want to know how well-suited your language of choice is to running as Wasm. I’ll also discuss the level of work involved in each deployment.

Rust

In some ways, Rust is the language most well-suited to deploy to WebAssembly. Your existing Rust code doesn’t need to be modified a great deal to compile to Wasm, and most of the changes involve setting up the right compiler target and compilation settings. The tooling also automatically generates boilerplate JavaScript to allow the compiled Wasm modules to work directly with web pages.

The size of the compiled module will vary, but Rust can generate quite lean and efficient code, so a simple “Hello, world” generally doesn’t run more than a few kilobytes. Rust’s maintainers authored an entire guide to using Wasm from Rust, with details on how to keep the size of delivered binaries small and adding Wasm support to an existing, general-purpose Rust crate.

C/C++

C and C++ were among the first languages to compile to Wasm, in big part because many of the lower-level behaviors in those languages map well to Wasm’s instruction set. The early wave of Wasm demos were ports of graphics demonstrations and games written in C/C++, and those proof-of-concept projects went a long way toward selling Wasm as a technology. (Look! We can play Doom in the browser!)

One of the first tools developed to compile C/C++ to Wasm was the Emscripten toolchain. Emscripten has since become a full-blown toolchain for compiling C or C++ to Wasm—full-blown in the sense that it offers detailed instructions for porting code. SIMD (which is supported in Wasm), networking, C++ exceptions, asynchronous code, and many other advanced features can be ported to Wasm, although the amount of work varies by feature. Pthread support, for instance, isn’t enabled by default, and will only work in browsers when the web server has certain origin headers set correctly.

As of version 8 and up, the Clang C/C++ compiler can compile natively to Wasm with no additional tooling. However, Emscripten uses the same underlying technology as Clang—the LLVM compiler framework—and may provide a more complete toolset specifically for compilation.

Golang

The Go language added support for WebAssembly as a compilation target in version 1.11, way back in August 2018. Originally an experimental project, Wasm is now fairly well-supported as a target, with a few caveats.

As with Rust, most of the changes to a Go program for Wasm’s sake involve changing the compilation process rather than the program itself. The Wasm toolchain is included with the Go compiler, so you don’t need to install any other tooling or packages; you just need to change the GOOS and GOARCH environment variables when compiling. You will need to manually set up the JavaScript boilerplate to use Wasm-compiled Go modules, but doing this isn’t hard; it mainly involves copying a few files, and you can automate the process if needed.

The more complex parts of using Go for Wasm involve interacting with the DOM. The included tooling for this via the syscalls/js package works, but it’s awkward for anything other than basic interaction. For anything bigger, pick a suitable third-party library.

Another drawback of using Go with Wasm is the size of the generated binary artifacts. Go’s runtime means even a “Hello, world” module can be as much as two megabytes. You can compress Wasm binaries to save space, or use a different Go runtime, like TinyGo—although that option only works with a subset of the Go language.

JavaScript

It might seem redundant to translate JavaScript to Wasm. One of the most common destinations for Wasm is the browser, after all, and most browsers come with a JavaScript runtime built in. But it is possible to compile JavaScript to Wasm if you want to.

The most readily available tool for JavaScript-to-Wasm is Javy, created and supported by the Bytecode Alliance (a chief supporter of Wasm initiatives). Javy doesn’t so much compile JavaScript code to Wasm as execute it in a Wasm-based JavaScript runtime. It also uses a dynamic linking strategy to keep the resulting Wasm modules reasonably small, although the size will vary depending on the features used in your program.

Python

Python’s situation is like Go’s, but even more pronounced. You can’t run a Python program without the Python runtime, and it’s difficult to do anything useful without the Python standard library—to say nothing of the ecosystem of third-party Python packages. You can run Python by way of the Wasm runtime, but it’s clunky and bulky, and the current state of the tooling for Python-on-Wasm isn’t streamlined.

A common way to run Python applications through a Wasm runtime is Pyodide, a port of the CPython runtime to Wasm via Emscripten. One implementation of it, PyScript, lets you run Python programs in web pages, as per JavaScript. It also includes bidirectional support for communication between Python and the JavaScript/DOM side of things.

Still, Pyodide comes with multiple drawbacks. Packages that use C extensions (as an example, NumPy) must be ported manually to Pyodide to work. Only pure Python packages can be installed from PyPI. Also, Pyodide has to download a separate Wasm package for the Python runtime, which runs to a few megabytes, so it might be burdensome for those who aren’t expecting a big download potentially every time they use the language.