![]() |
WebAssembly (abbreviated to Wasm) is a portable, performant binary language designed to run code close to native speed directly in the browser. In this article, we'll explore what it is, how it works, its capabilities, support in modern browsers, and even provide a practical end-to-end example with Rust and JavaScript.
What is WebAssembly?
WebAssembly is an open standard (W3C) designed to bring compiled code from languages such as Rust, C/C++, Go and many others to the web, offering very high performance and security in a sandbox environment.
Standardization
Wasm was officially recognized as a web standard by W3C in 2019.
Main features:
-
Portability: it can run anywhere, even outside the browser (portable and optimized binary format).
-
High Performance: Speed comparable to native code.
-
Security: Execution in sandbox, without direct access to the system.
-
Interoperability: Can efficiently interoperate with JavaScript and Web APIs.
How WebAssembly Works
Code written in languages like Rust is compiled into a .wasm module, which can then be loaded into the browser via JavaScript. The browser runs that module in a sandbox with near-native performance.
Typical pipeline:
-
Write the code in a language that compiles to Wasm (e.g. Rust).
-
Compile into a
.wasmmodule. -
Load it in the browser using JavaScript.
-
Run and interact with the data via calls between Wasm and JS.
Example JS to load a Wasm module:
fetch('module.wasm')
.then(res => res.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes))
.then(obj => {
console.log(obj.instance.exports.myFunction());
});Practical end-to-end example: WebAssembly + JavaScript
-
Install Rust and create a Rust project
Write a simple function in Rust that processes some information.
-
Compile it into a WebAssembly module (
.wasm) usingwasm-pack. -
Load the module into an HTML page with JavaScript.
-
Print an object returned by the module to
console.logWasm.
1. Install Rust and wasm-pack (Linux, macOS, Windows)
Linux/macOS
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo install wasm-pack
Windows
-
Go to https://rustup.rs and download the installer for Windows.
-
After installation, open PowerShell and install:
cargo install wasm-pack
npm install -g wasm-pack2. Create the Rust project
cargo new wasm-demo --lib
cd wasm-demo
Edit Cargo.toml:
[package]
name = "wasm_demo"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
serde = { version = "1.0", features = ["derive"] }
src/lib.rs:use wasm_bindgen::prelude::*;
use serde::{Serialize};
#[derive(Serialize)]
pub struct ResultObject {
pub processed: String,
pub length: usesize,
}
#[wasm_bindgen]
pub fn process_input(input: &str) -> JsValue {
let result = ResultObject {
processed: input.to_uppercase(),
length: input.len(),
};
JsValue::from_serde(&result).unwrap()
}
- takes an input string, transforms it into uppercase and returns a JSON object with the result.
- exports the function process_input to be called by JavaScript.
3. Compile for WebAssembly with wasm-pack
cd wasm_demo
wasm-pack build --target web
4. Loading and using in HTML/JavaScript
pkg/ folder generated by wasm-pack to a directory accessible by HTML.Create an HTML page - e.g. index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Wasm Demo</title>
<script type="module">
import init, { process_input } from './pkg/wasm_demo.js';
async function run() {
await init();
const result = process_input("hi wasm!");
console.log("Result:", result);
}
run();
</script>
</head>
<body>
<h1>WebAssembly Demo</h1>
</body>
</html>
index.html in a compatible browser, you will see something like this in the console:Expected output to console:
Result: { processed: "HI WASM!", length: 8 }Wait a moment: but what is the difference between the module.wasm file seen at the beginning and the wasm_demo.js file of the previous example?
module.wasm and wasm_demo.js (or any .js file generated by wasm-pack) is fundamental to understand how the integration between WebAssembly and JavaScript works. Let's describe them clearly:1. module.wasm – The WebAssembly binary module
This is the real compiled module. Contains portable machine code generated by Rust (or C/C++) in binary .wasm format.
-
This is what the browser runs in the sandbox.
-
It does not contain any loading logic or JS binding.
-
You must instantiate it manually in JavaScript using the
WebAssembly.instantiateAPIs.
Minimal direct loading example:
const response = await fetch('module.wasm');
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes);
console.log(instance.exports.myFunction());2. wasm_demo.js – The JavaScript wrapper generated by wasm-pack
This file is automatically generated by wasm-pack, a command-line tool developed by the Rust community (under the rustwasm) that simplifies the process of compiling Rust projects in WebAssembly for use on the web, especially with JavaScript.
wasm-pack serves as a bridge between WebAssembly and the JavaScript world.
Example:
import init, { process_input } from './pkg/wasm_demo.js';
await init(); // initialize the .wasm module
const result = process_input("hi wasm!");
console.log(result);When you compile with wasm-pack, you get two main files:
wasm_demo_bg.wasm: the actual WebAssembly module.wasm_demo.js: a JavaScript wrapper generated bywasm-packthat:- Loads the
.wasmmodule. - Handles conversions between Rust and JavaScript types.
- Exposes functions idiomatically for JS.
Note:wasm_demo_bg.wasmis the real name generated bywasm-pack, whilemodule.wasmis a generic example used in tutorials/manuals.
Difference summary
| File | Content | Main role |
|---|---|---|
wasm_demo_bg.wasm |
Wasm binary compiled by Rust | Run by browser (core) |
wasm_demo.js |
JavaScript Wrapper | JS ↔ Wasm link (typical management) |
When to use one or the other?
-
module.wasm→ if you are working manually with WebAssembly using only the standard WebAssembly API in JS and want maximum control (requires manual coding and does not support complex types without extra work). -
wasm_demo.js+.wasm→ if you want to simplify your life and use functions with strings or JSON. For example if you use Rust + wasm-pack + wasm-bindgen, which is the modern and most productive method.
Manually loading a .wasm module [for curious and academics]
If at this point you are curious to know what it would be like to manually load a wasm module in JavaScript without going through wasm-pack,or you simply want to delve deeper into the low-level operation, then this paragraph is ideal to understand step-by-step how the integration between Wasm and JS.1. Simplified Wasm module in Rust
For the purposes of this tutorial (and to prevent this introductory Wasm article from being infinite), we will avoid complex functions and not use wasm-bindgen (this is to make manual loading easier to do). We will write a simple function that returns a number.
src/lib.rs (without wasm-bindgen)
#[no_mangle]
pub extern "C" fn square(x: i32) -> i32 {
x * x
}
Note:
-
#[no_mangle]is used to prevent the compiler from changing the name of the function. -
extern "C"makes the function compatible with the call from external languages (C / JS).
2. Compiling to .wasm manually
Compile with cargo and wasm32-unknown-unknown:
rustup target add wasm32-unknown-unknown
cargo build --release --target wasm32-unknown-unknown
The .wasm file can be found here:
target/wasm32-unknown-unknown/release/<project_name>.wasm
Copy it to a folder accessible from your web project.
3. HTML + JS without wrappers
index.html
<html>
<head>
<meta charset="UTF-8"></meta>
<title>Manual Wasm</title>
<script>
async function loadWasm() {
const response = await fetch('module.wasm');
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes);
const result = instance.exports.square(7);
console.log("The square of 7 is:", result);
}
loadWasm();
</script>
</head>
<body>
<h1>WebAssembly without wrapper</h1>
</body>
</html>
Output to console:
Thesquare of 7 is: 49
Main differences compared to the wasm-pack
wrapper
| Manual loading | With wasm-pack (wasm_demo.js) |
|---|---|
| You need to handle everything at a low level | Exposes convenient functions and conversions |
| You can't use complex types (strings, objects) without extra work | Supports strings, JSON, objects via wasm-bindgen |
Requires #[no_mangle] and extern "C" |
Use idiomatic Rust macros |
| Best suited for minimalistic modules | Best suited for applications modern |
Browser Support
WebAssembly is supported in the main browsers starting from the following versions:
| Browser | Wasm support from | Version |
|---|---|---|
| Google Chrome | March 2017 | 57 |
| Mozilla Firefox | March 2017 | 52 |
| Microsoft Edge | October 2017 | 16 |
| Apple Safari | September 2017 | 11 |
| Opera | March 2017 | 44 |
You can also check it out on Can I Use.
WebAssembly Extensions and Future
WebAssembly continues to evolve, with advanced features being standardized:
-
WASI (WebAssembly System Interface): Server-side execution.
-
Garbage Collection: for memory-managed languages (e.g. Kotlin, C#).
-
Threading and SIMD: for parallel performance and vector operations.
-
Exception Handling: better error handling.
Some use cases real:
- Gaming (Unity via WebGL + Wasm)
- Desktop app (e.g. Figma uses WebAssembly)
- Client-side encryption
- Video/audio processing directly in JS
Official resources and tutorials
Documentation:
Tools and environments:
Practical tutorials:
Conclusion
WebAssembly represents one of the most important evolutions of the modern web. It brings high-performance compiled code to the browser in a secure and versatile way. Knowing how to use it, even just to extend the capabilities of JavaScript, is a valuable skill for frontend and backend developers. And thanks to tools like wasm-pack, entering the world of Wasm is now much more accessible.
Follow me #techelopment
Official site: www.techelopment.it
facebook: Techelopment
instagram: @techelopment
/>telegram: @techelopment_channel
whatsapp: Techelopment
youtube: @techelopment
