Lab 11: WebAssembly Go

Time: 45 minutes | Level: Advanced | Docker: docker run -it --rm golang:1.22-alpine sh

Overview

Compile Go to WebAssembly, use syscall/js for DOM interaction, call Go functions from JavaScript, and understand the WASM runtime model and binary size.


Step 1: WASM Compilation Basics

# Compile Go to WASM
GOOS=js GOARCH=wasm go build -o app.wasm main.go

# Check binary size
wc -c app.wasm        # bytes
ls -lh app.wasm       # human-readable

# The WASM runtime shim (copy to serve with HTML)
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

# Minimize size with tinygo (optional)
# tinygo build -o app.wasm -target=wasm main.go

Step 2: Minimal WASM Module


Step 3: DOM Interaction with syscall/js


Step 4: HTML Host Page


Step 5: Bidirectional Calls


Step 6: WASM Binary Size Analysis

💡 Standard Go WASM is ~2MB because it includes the full Go runtime (scheduler, GC, etc.). For minimal binaries, use TinyGoarrow-up-right which produces 10-100KB WASM files.


Step 7: Serve WASM Locally


Step 8: Capstone — Compile and Measure

📸 Verified Output:


Summary

Aspect
Value
Notes

Binary size (stdlib)

~2.1MB

Includes full Go runtime

Binary size (-s -w)

~1.5MB

Stripped symbols

Binary size (TinyGo)

~10-100KB

Subset of Go

syscall/js

DOM access

js.Value wrapper

Go → JS

js.Global().Set(name, FuncOf(fn))

Export functions

JS → Go

js.FuncOf(fn).Invoke(args...)

Callback pattern

Async

Return Promise.new(handler)

Go goroutine inside

Key Takeaways:

  • Build tag //go:build js && wasm restricts code to WASM target

  • select {} in main() keeps the WASM module alive

  • js.FuncOf wraps Go functions for JavaScript consumption

  • Always call .Release() on js.Func to prevent goroutine leaks

  • Use -ldflags="-s -w" to strip debug symbols and reduce binary size

  • TinyGo for production WASM (smaller), standard Go for complex programs

Last updated