When building a rosbag visualization tool, it was discovered that a static web frontend could parse rosbag files without any backend API requests, prompting investigation into WebAssembly as the underlying technology.
WebAssembly Basics
WebAssembly (abbreviated WASM) is a low-level, efficient binary instruction format built for browser-based execution. It delivers near-native performance for code originally written in compiled programming languages, while operating within a secure sandboxed environment.
Core Characteristics
- Near-Native Performance: Runs at speeds close to nativley compiled code, ideal for compute-heavy workloads such as gaming, media processing, and data parsing.
- Cross-Platform Compatibility: Portable binary format that works consistently across all browsers and WASM-supporting environments, regardless of operating system or device.
- Sandboxed Security: Executes within a restricted browser sandbox, isolated from the host system to prevent unauthorized access or system damage.
- Seamless JavaScript Interoperability: Directly interacts with JavaScript, allowing existing codebases written in C, C++, Go, Rust, and other compiled languages to be compiled to WASM and called from web applications.
- Efficient Loading: Compact binary size reduces load times compared to equivalent JavaScript bundles, improving overall page responsiveness.
Common Use Cases
- Porting legacy native applications to web environments
- Running compute-intensive data processing tools directly in browsers
- Building high-performance web-based games and graphic applications
Practical Implementation With Go
To demonstrate direct web calls to native-compiled code, we’ll create a simple Go WASM module and integrate it with a static HTML page.
Step 1: Write the Go Source Code
Create a file named wasm_demo.go with the following content:
package main
import "syscall/js"
func main() {
// Block program exit to keep the WASM module active
completionChan := make(chan struct{}, 0)
// Export a function to the global JavaScript window object
js.Global().Set("greetWasm", js.FuncOf(func(_ js.Value, _ []js.Value) interface{} {
return js.ValueOf("Hello from Go WebAssembly!")
}))
<-completionChan
}
Step 2: Compile to WASM
Use the following commands to compile the Go code to a WASM binary: For PowerShell (Windows):
$env:GOARCH = "wasm"
$env:GOOS = "js"
go build -o demo.wasm wasm_demo.go
For Bash/Zsh (Linux/macOS):
export GOARCH=wasm
export GOOS=js
go build -o demo.wasm wasm_demo.go
Copy the wasm_exec.js boootstrap file from your Go distribution’s misc/wasm/ directory to your project root, as it is required to initialize the Go WASM runtime.
Step 3: Create the HTML Page
Create an index.html file to load and run the WASM module:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Go WASM Demo</title>
</head>
<body>
<h1>Go WebAssembly Example</h1>
<script src="wasm_exec.js"></script>
<script>
// Initialize the Go WASM runtime
const goRuntime = new Go();
async function initializeWasm() {
try {
// Fetch and compile the WASM binary
const wasmResponse = await fetch("demo.wasm");
const wasmBuffer = await wasmResponse.arrayBuffer();
const wasmResult = await WebAssembly.instantiate(wasmBuffer, goRuntime.importObject);
// Start the Go runtime instance
goRuntime.run(wasmResult.instance);
// Call the exported WASM function and log the result
console.log(window.greetWasm());
} catch (error) {
console.error("Error loading WASM module:", error);
}
}
initializeWasm();
</script>
</body>
</html>
This setup allows the static HTML page to directly call the Go-compiled WASM function, outputting the greeting message to the browser console. This pattern can be extended to run more complex native code, such as rosbag parsing, directly in the browser without backend infrastructure.