WebAssembly and JS Interop

WebAssembly (wasm) is a low-level binary instruction format that runs at near-native speed in the browser, letting you compile languages like C, C++, and Rust to code that works alongside JavaScript for performance-critical tasks.

Learn WebAssembly and JS Interop in our free JavaScript course — an interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free JavaScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

💡 Running Code Locally: Building wasm requires a toolchain. The JavaScript glue below is illustrative. For the best experience:

🏎️ Real-World Analogy: WebAssembly is like a race-car engine dropped into your web app:

WebAssembly is a compact binary format that browsers can decode and execute far faster than they can parse and optimize equivalent JavaScript. It is a compilation target : you write in C, C++, Rust, or other languages and compile down to a .wasm module. That module runs in the same secure sandbox as JavaScript, on the same engine, and the two interoperate through a small, well-defined boundary of imports and exports.

🔥 What wasm Is (and Isn't)

A .wasm file holds bytecode for a stack-based virtual machine. It has a readable text format ( .wat ) for debugging, but ships as binary. Crucially, wasm has no built-in DOM access — it computes, and calls into JavaScript when it needs to touch the page.

All modern browsers ship WebAssembly. It is a web standard alongside HTML, CSS, and JavaScript.

🔥 Compiling from C and Rust

You rarely write wasm by hand. Toolchains compile high-level code: Emscripten for C/C++ and wasm-pack (with wasm-bindgen ) for Rust. Functions marked for export become callable from JavaScript.

For C/C++, Emscripten produces a .wasm file plus optional JavaScript glue. Either way, the result is a portable module you load from JavaScript.

🔥 Instantiating a wasm Module

To run wasm you compile and instantiate it. WebAssembly.instantiateStreaming() does both directly from a fetch response and is the most efficient path. The older WebAssembly.instantiate() works from an ArrayBuffer .

Streaming compiles the module as the bytes arrive, so execution can start sooner. Use the arrayBuffer fallback if your server does not serve application/wasm .

🔥 Linear Memory

A wasm instance owns a linear memory : a single, contiguous, resizable ArrayBuffer of raw bytes. Both wasm and JavaScript can read and write it through typed arrays, which is how you exchange strings, arrays, and structs across the boundary.

Because wasm's value types are just numbers, anything richer (text, images, objects) is passed by writing bytes into this shared memory and exchanging offsets and lengths.

🔥 Calling wasm from JS, and JS from wasm

Interop flows both ways. A module's exports are functions JavaScript can call. A module's imports are functions (and memory) JavaScript supplies, so the module can call back into JavaScript — useful since wasm cannot reach the DOM itself.

This two-way boundary is the heart of interop: JavaScript orchestrates the app and the DOM, while wasm runs the heavy computation and calls back for anything it cannot do alone.

🔥 Use Cases and Near-Native Speed

Wasm shines for performance-critical, CPU-bound work : image and video processing, 3D and game engines, audio synthesis, physics and scientific simulation, cryptography, compression, and porting large existing C/C++/Rust codebases to the web.

Wasm runs at near-native speed because it is a compact, statically-typed format the engine can compile ahead of time, avoiding the parsing and dynamic optimization that JavaScript needs.

🔥 wasm Complements JavaScript

A common misconception is that wasm replaces JavaScript. It does not. Wasm has no DOM access and no garbage-collected high-level objects in its core; JavaScript remains the language of the web platform. The two are designed to work together .

🎯 Key Takeaways

Practice quiz

What is WebAssembly (wasm)?

  • A low-level, binary instruction format that runs at near-native speed in the browser
  • A CSS framework
  • A package manager for JavaScript
  • A new version of JavaScript

Answer: A low-level, binary instruction format that runs at near-native speed in the browser. WebAssembly is a portable, low-level binary instruction format designed as a compilation target that runs at near-native speed in browsers and other hosts.

Which languages commonly compile to WebAssembly?

  • Only JavaScript
  • HTML and CSS
  • Systems languages like C, C++, and Rust
  • SQL

Answer: Systems languages like C, C++, and Rust. Languages such as C, C++, and Rust are commonly compiled to wasm using toolchains like Emscripten and wasm-pack.

What is the recommended way to compile and instantiate a .wasm module from a fetch response?

  • JSON.parse()
  • WebAssembly.instantiateStreaming()
  • require()
  • eval()

Answer: WebAssembly.instantiateStreaming(). WebAssembly.instantiateStreaming(fetch(...)) compiles and instantiates the module directly from the streamed response, which is the most efficient approach.

What does WebAssembly use for its memory model?

  • The DOM tree
  • Browser cookies
  • localStorage
  • A linear memory: a single resizable ArrayBuffer of raw bytes

Answer: A linear memory: a single resizable ArrayBuffer of raw bytes. Wasm uses linear memory, a contiguous, resizable ArrayBuffer of bytes that both wasm and JavaScript can read and write.

How does JavaScript call a function defined in a wasm module?

  • Through the module instance's exports object
  • By reading it from the DOM
  • Using document.querySelector
  • It cannot - wasm is isolated

Answer: Through the module instance's exports object. Functions a wasm module exports are available on instance.exports and can be called from JavaScript like normal functions.

How can a wasm module call a JavaScript function?

  • By using the fetch API directly
  • Through the imports object passed in at instantiation
  • By editing the HTML
  • It cannot call JavaScript at all

Answer: Through the imports object passed in at instantiation. You pass an imports object when instantiating; the wasm module declares matching imports and can call those provided JavaScript functions.

Which is a strong use case for WebAssembly?

  • Storing form values
  • Adding click handlers to a page
  • Styling buttons
  • Performance-critical code like image/video processing, games, or cryptography

Answer: Performance-critical code like image/video processing, games, or cryptography. Wasm excels at CPU-intensive workloads such as image/video processing, 3D games, audio, simulations, and cryptography where near-native speed matters.

What is the relationship between WebAssembly and JavaScript?

  • JavaScript runs inside wasm only
  • Wasm is meant to fully replace JavaScript
  • Wasm complements JavaScript; they work together, with JS handling glue and the DOM
  • They cannot be used in the same page

Answer: Wasm complements JavaScript; they work together, with JS handling glue and the DOM. Wasm complements rather than replaces JavaScript: JS typically orchestrates the app and DOM while wasm handles heavy computation.

Why can't wasm modules directly manipulate the DOM today?

  • Wasm has no concept of functions
  • Wasm has no built-in DOM access, so it calls into JavaScript to touch the DOM
  • The DOM is written in Rust
  • Wasm is too slow for the DOM

Answer: Wasm has no built-in DOM access, so it calls into JavaScript to touch the DOM. Core wasm has no direct DOM bindings, so DOM changes are performed by calling imported JavaScript functions that act as glue.

What Rust tool is commonly used to build wasm and generate JS bindings?

  • wasm-pack (with wasm-bindgen)
  • npm
  • webpack only
  • babel

Answer: wasm-pack (with wasm-bindgen). wasm-pack, together with wasm-bindgen, compiles Rust to wasm and generates the JavaScript bindings that ease interop.