Compiling to WebAssembly
Most WebAssembly in production is never hand-written; it is the output of a compiler toolchain that takes existing source code in a systems language and targets Wasm instead of (or alongside) native machine code. Because LLVM already had a mature backend architecture, adding a WebAssembly target to Clang/LLVM made it possible for C, C++, and Rust — all of which use LLVM in their toolchains — to gain Wasm compilation with comparatively modest additional engineering.
Cricket analogy: Like an established domestic first-class league adding a new T20 format on top of its existing structure rather than building a whole new sport from scratch, LLVM added Wasm as a new backend target on its existing mature architecture.
Compiling from C/C++ with Emscripten
Emscripten is the standard C/C++-to-Wasm toolchain: its emcc command wraps Clang, targets wasm32 by default, and additionally emits a JavaScript 'glue' file that handles module loading, sets up linear memory, and implements a virtual filesystem so POSIX calls like fopen() still work by mapping to an in-memory or IndexedDB-backed filesystem, since the browser sandbox has no real filesystem to call into.
Cricket analogy: Like a translator who not only converts a coach's Hindi instructions into English but also arranges the equipment and ground staff so the drill can actually run, Emscripten compiles C/C++ and also generates the JS glue that sets up the whole environment.
# Compile a C file directly to a standalone .wasm module
emcc hello.c -O2 -s WASM=1 -s STANDALONE_WASM \
-o hello.wasm
# Compile with a JS glue file for browser use
emcc app.c -O3 -s EXPORTED_FUNCTIONS="['_add']" \
-s EXPORTED_RUNTIME_METHODS="['ccall']" \
-o app.jsCompiling from Rust
Rust has first-class Wasm support built into rustc via the wasm32-unknown-unknown and wasm32-wasi targets, and the wasm-pack tool wraps that build step to also run wasm-bindgen, which generates typed JavaScript bindings for exported Rust functions and structs — so a #[wasm_bindgen] annotated Rust function can be called from JS as if it were a native async function, with strings and complex types automatically marshaled across the boundary.
Cricket analogy: Like a domestic academy that already has a certified pathway straight into international selection, Rust has first-class Wasm targets built directly into rustc rather than needing a bolted-on external toolchain.
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}! Compiled with Rust -> Wasm.", name)
}
// Build with: wasm-pack build --target webCompiling from Other Languages
Beyond C/C++ and Rust, TinyGo (a Go compiler subset built for embedded and Wasm targets, distinct from the standard Go toolchain's much larger Wasm output) and AssemblyScript (a TypeScript-like syntax that compiles directly to Wasm, popular for developers who want Wasm's performance without leaving a familiar syntax) are widely used, alongside garbage-collected language support arriving through the newer Wasm GC proposal, which lets languages like Java, Kotlin, and Dart target Wasm without shipping their own bundled garbage collector inside every module.
Cricket analogy: Like a lightweight T10 format designed specifically for quick, resource-constrained broadcast slots versus the full five-day Test format, TinyGo is a stripped-down Go subset built specifically for constrained Wasm targets.
The Wasm GC proposal, now supported by major engines, lets garbage-collected languages like Java, Kotlin, and Dart compile to Wasm and use the host engine's own garbage collector, instead of bundling an entire GC runtime inside every module as earlier approaches had to.
- Production Wasm is almost always compiler output, not hand-written code.
- LLVM's mature backend architecture made adding a WebAssembly target comparatively straightforward for C, C++, and Rust.
- Emscripten (emcc) compiles C/C++ to Wasm and generates JS glue plus a virtual filesystem emulating POSIX file calls.
- Rust has first-class Wasm targets built into rustc, with wasm-pack and wasm-bindgen automating JS interop.
- TinyGo produces much smaller Wasm binaries than the standard Go toolchain, better suited to Wasm's constraints.
- AssemblyScript offers a TypeScript-like syntax that compiles directly to compact Wasm.
- The Wasm GC proposal lets garbage-collected languages like Java, Kotlin, and Dart target Wasm using the host's own GC.
Practice what you learned
1. Why was it comparatively straightforward for C, C++, and Rust to gain WebAssembly compilation support?
2. What does Emscripten's virtual filesystem enable?
3. What is wasm-bindgen used for in the Rust-to-Wasm workflow?
4. Why do developers often choose TinyGo over the standard Go compiler for Wasm targets?
5. What does the Wasm GC proposal enable?
Was this page helpful?
You May Also Like
What Is WebAssembly?
An introduction to WebAssembly (Wasm) as a portable, binary instruction format that lets languages like C, C++, and Rust run at near-native speed in browsers and beyond.
WAT Text Format Basics
How to read and write WebAssembly's human-readable S-expression text format (WAT), including functions, locals, exports, and memory.
Wasm vs JavaScript
A comparison of WebAssembly and JavaScript covering performance, tooling, and how the two interoperate in the same web application.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics