Hello, I'm Maneshwar. I'm working on FreeDevTools online currently building **one place for all dev tools, cheat codes, and TLDRs* — a free, open-source hub where developers can quickly find and use tools without any hassle of searching all over the internet.
I was thinking "Can I run native binaries in the browser?" Naturally, my mind jumped to languages like Go or Rust—languages built compiling down to something close to the metal (bin).
But here's the twist: browsers don't speak Go or Rust.
They speak JavaScript. Or at least, they used to.
This is where WebAssembly comes in.
So... What is WASM?
WebAssembly (or WASM) is a binary instruction format designed as a portable compilation target for programming languages. In simpler terms, it's a way to run code written in languages other than JavaScript in web browsers.
Think of it as a universal translator for the web. You write code in your favorite language, compile it to WASM, and then browsers can execute that WASM code as if it were JavaScript.
Why WASM?
Performance
WASM is designed for speed. It's a low-level language that's close to machine code, which means it can be executed very efficiently by browsers. In many cases, WASM can outperform JavaScript, especially for computationally intensive tasks.
Portability
WASM is designed to be portable across different platforms and architectures. This means that code compiled to WASM can run in any browser that supports it, regardless of the underlying operating system or hardware.
Security
WASM is designed with security in mind. It runs in a sandboxed environment, which means that it can't access the underlying operating system or hardware directly. This helps to prevent malicious code from compromising the user's system.
Multi-language Support
WASM allows developers to write code in a variety of languages and then compile it to a single, portable format. This opens up new possibilities for web development and allows developers to use the languages that are best suited for the task at hand.
Use Cases
So, where does WASM shine?
- Games: High-performance games can now run smoothly in the browser.
- Image/Video Editing: Complex editing software can be ported to the web.
- Scientific Computing: Heavy calculations can be done client-side.
- Any CPU intensive task: You name it.
How to Get Started with WASM
Choosing a Language
The first step is to choose a language that can be compiled to WASM. Some popular options include:
- Rust: Known for its performance and safety.
- Go: A simple and efficient language that's easy to learn.
- C/C++: The classic choice for high-performance computing.
- AssemblyScript: A TypeScript-like language that compiles directly to WASM.
Toolchains and Compilers
Once you've chosen a language, you'll need to install a toolchain and compiler that can generate WASM code. Here are a few popular options:
- Rust: Use
rustup
to install the Rust toolchain, and then usecargo
to build your WASM code. - Go: Use the
go
command to build your WASM code. You'll need to set theGOOS
andGOARCH
environment variables tojs
andwasm
, respectively. - C/C++: Use a compiler like Clang or GCC to compile your code to WASM. You'll need to use the
-target wasm32-unknown-emscripten
flag to target WASM. - AssemblyScript: Use the
asc
command to compile your AssemblyScript code to WASM.
Example Time
Let's walk through a simple example of how to create a WASM module using Rust.
First, create a new Rust project:
cargo new hello-wasm --lib
cd hello-wasm
Next, add the following to your Cargo.toml
file:
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
This tells Rust to compile our code into a dynamic library that can be used by JavaScript, and it adds the wasm-bindgen
crate as a dependency. wasm-bindgen
is a tool that makes it easy to pass data between JavaScript and WASM.
Now, let's write some Rust code:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
This code defines a function called greet
that takes a string as input and returns a string as output. The #[wasm_bindgen]
attribute tells wasm-bindgen
to generate the necessary code to make this function callable from JavaScript.
Finally, let's build our WASM module:
cargo build --target wasm32-unknown-unknown --release
This will create a WASM file in the target/wasm32-unknown-unknown/release
directory.
Using WASM in JavaScript
Now that we have our WASM module, we need to load it into our JavaScript code. Here's how we can do that:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>WASM Example</title>
</head>
<body>
<script>
async function loadWasm() {
try {
const wasm = await import("./pkg/hello_wasm.js");
const result = wasm.greet("World");
console.log(result);
} catch (e) {
console.error("Error loading WASM:", e);
}
}
loadWasm();
</script>
</body>
</html>
This code first imports the WASM module using the import
statement. Then, it calls the greet
function, passing in the string "World" as input. Finally, it logs the result to the console.
Challenges and Considerations
While WASM opens up a world of possibilities, it's not without its challenges.
- Debugging: Debugging WASM can be more complex than debugging JavaScript.
- Ecosystem: The WASM ecosystem is still relatively new, so there are fewer libraries and tools available compared to JavaScript.
- File Size: WASM files can be larger than JavaScript files, which can impact page load times. bAssembly#:~:text=WebAssembly%20(Wasm)%20defines%20a%20portable,structured%3B%20stack%20machine) (or WASM) is like a virtual CPU that runs inside your browser.
It defines its own instruction set.
It doesn't compile to x86 or ARM, but to its own binary format that the browser can understand and execute efficiently.
Think of it like this:
- The browser downloads a
.wasm
file. - That
.wasm
file contains instructions in a binary format. - Those instructions describe how to operate on a stack—push, pop, add, branch, loop, etc.
- The browser then either interprets or compiles those instructions into real machine code.
It’s kind of like Java’s JVM bytecode—but made for performance and for modern compilers. And unlike Java, it wasn’t designed in the 90s.
But Why Should I Care?
WebAssembly is fast. Like, really fast. Not "a little faster than JS"—but close-to-native fast.
That means you can take a performance-critical app (say, image processing, or running emulators) and run it in the browser without killing the fan.
The people behind WASM knew what they were doing.
The spec was designed by folks who deeply understand compiler and CPU internals.
So even though it’s portable and sandboxed, it can still get compiled to native CPU instructions quickly and efficiently.
Wait, Is It Like Assembly?
WASM borrows a lot of ideas from traditional assembly:
- It’s imperative: instructions run one after another.
- It has a stack: push values, pop them off, compute.
- It has branching, memory access, arithmetic—all the usual suspects.
But it’s not tied to any real CPU.
It’s not x86, not ARM, not RISC-V. It’s WASM’s own thing.
That’s why you can compile languages like Rust, C/C++, or Go into WASM.
These compilers already know how to emit instructions for different architectures—and WASM is just one more "target" for them.
Is It Only for Compiling Native Code?
Mostly, yes. That’s the main use case right now: compiling existing codebases written in C/C++/Rust/Go into something that runs in the browser.
But there's also WASI (WebAssembly System Interface), which is trying to bring system-level APIs to WASM—so you can run WASM outside the browser, too.
That’s where things get interesting.
Now you can run sandboxed WASM modules on the server, inside a CLI tool, or as plugins.
Think Docker containers, but even more lightweight.
What Can You Build With It?
Here’s where it gets fun. WebAssembly opens up a whole new set of use cases for the web (and beyond):
- Audio/video processing: things like FFmpeg or Audacity in the browser
- Games & emulators: NES, SNES, PS1 emulators running natively in JS environments
- Secure plugins: apps that allow user-contributed code (think OBS or VS Code extensions)
- Cross-platform apps: compile once, run everywhere—desktop, mobile, browser
And if you're doing backend stuff, some platforms (like Wasmer or Wasmtime) let you run WASM on the server.
That means you can build multi-tenant systems where user code runs isolated and safely.
Step-by-Step Setup
1. Project Structure
go-wasm
├─ Makefile
├─ client.html
├─ go.mod
├─ main.go
├─ main.wasm
└─ wasm_exec.js
2. main.go
: Export Go Function to JavaScript
package main
import (
"syscall/js"
)
func countChars(this js.Value, args []js.Value) interface{} {
println("[Go] countChars called")
if len(args) < 1 {
println("[Go] No argument provided")
return 0
}
input := args[0].String()
result := len(input)
println("[Go] Input received:", input)
println("[Go] Character count:", result)
return result
}
func main() {
println("[Go] WASM module initializing...")
js.Global().Set("countChars", js.FuncOf(countChars))
println("[Go] countChars function exported to JS")
select {} // keep it alive
}
3. Makefile
: Build and Init
build:
GOOS=js GOARCH=wasm go build -o main.wasm
init:
cp $$(find /usr/local/go -name wasm_exec.js | head -n 1) ./wasm_exec.js
-
make build
: Compiles the Go code tomain.wasm
. -
make init
: Copieswasm_exec.js
from Go's installation path (required to run WASM in browser).
And boom—your Go binary is now browser-ready.
4. client.html
: Browser UI + JS Glue
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Go WASM Character Counter</title>
</head>
<body>
<h1>Go WASM Character Counter</h1>
<input type="text" id="inputText" placeholder="Type something..." />
<button id="countBtn">Count Characters</button>
<p id="result"></p>
<script src="wasm_exec.js"></script>
<script>
const go = new Go(); // defined in wasm_exec.js
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject)
.then((result) => {
go.run(result.instance);
document.getElementById("countBtn").onclick = function () {
const input = document.getElementById("inputText").value;
const count = window.countChars(input);
document.getElementById("result").textContent = `Character count: ${count}`;
};
});
</script>
</body>
</html>
5. Serve It
You need to serve over HTTP (not file://
) because WASM loading via fetch()
needs a web server.
Run a local server:
python3 -m http.server 8080
# or
npx serve .
Open http://localhost:8080/client.html
in your browser.
## Conclusion
To tie it back—yes, you can compile Go code to WebAssembly.
The Go compiler has built-in support for GOARCH=wasm GOOS=js
.
You just write Go like normal, export a function, and run it in the browser with a little JavaScript glue.
So yeah—binaries in the browser? Turns out, it’s not just possible. It’s actually kind of awesome.
I’ve been building FreeDevTools.
A collection of UI/UX-focused tools crafted to simplify workflows, save time, and reduce friction in searching tools/materials.
Any feedback or contributors are welcome!
It’s online, open-source, and ready for anyone to use.
👉 Check it out: FreeDevTools
⭐ Star it on GitHub: freedevtools
Let’s make it even better together.
Top comments (0)