README
¶
gowasm-bindgen Example
Go WASM functions traditionally required awkward js.Value signatures. With gowasm-bindgen, you write normal Go functions with standard types, and the tool generates TypeScript bindings automatically.
Directory Structure
examples/simple/
├── wasm/ # Go source code
│ ├── main.go # Normal Go functions (no js.Value!)
│ ├── main_test.go # Unit tests
│ └── bindings_gen.go # Generated WASM bindings (gitignored)
│
├── src/ # TypeScript source
│ ├── app.ts # Browser demo app
│ └── verify_test.ts # Type verification tests
│
├── public/ # Static assets
│ └── index.html
│
├── generated/ # gowasm-bindgen output (gitignored)
│ ├── go-wasm.ts # TypeScript class API
│ ├── worker.js # Web Worker loader
│ ├── wasm.wasm # Compiled WASM binary
│ └── wasm_exec.js # Go runtime
│
└── dist/ # Final bundled output (gitignored)
Quick Start
# Build everything (WASM binary + TypeScript types)
make all
# This runs:
# 1. setup - Copies wasm_exec.js from TinyGo
# 2. generate - Creates go-wasm.ts, worker.js, bindings_gen.go
# 3. build - Compiles Go to WASM with TinyGo
# 4. typecheck - Validates TypeScript types
# 5. verify - Runs TypeScript tests
# 6. dist - Bundles everything for serving
Using Standard Go (Alternative)
Standard Go produces larger binaries (~2.4MB vs ~200KB) but has full language support:
make setup-go # Copy wasm_exec.js from Go installation
make build-go # Build with standard Go
Worker Mode (Default - Non-blocking)
By default, gowasm-bindgen generates a Web Worker for non-blocking async calls:
make generate # Creates generated/go-wasm.ts + generated/worker.js
import { GoWasm } from './generated/go-wasm';
const wasm = await GoWasm.init('./worker.js');
// Non-blocking! UI stays responsive
const greeting = await wasm.greet('World');
const result = await wasm.calculate(5, 3, 'add');
wasm.terminate(); // Clean up when done
Sync Mode (Opt-in - Blocking)
Use --mode sync flag for synchronous calls that block the main thread:
make generate-sync # Creates generated/go-wasm.ts only (no worker.js)
import { GoWasm } from './generated/go-wasm';
const wasm = await GoWasm.init('./wasm.wasm'); // async load
const greeting = wasm.greet('World'); // sync call, no await
Generated Output
TypeScript Client (generated/go-wasm.ts)
export interface FormatUserResult {
displayName: string;
status: string;
}
export class GoWasm {
static async init(workerUrl: string): Promise<GoWasm>;
greet(name: string): Promise<string>;
calculate(a: number, b: number, op: string): Promise<number>;
formatUser(name: string, age: number, active: boolean): Promise<FormatUserResult>;
terminate(): void;
}
Go Bindings (wasm/bindings_gen.go)
Handles js.Value conversions automatically:
func init() {
js.Global().Set("greet", js.FuncOf(wasmGreet))
// ...
}
func wasmGreet(_ js.Value, args []js.Value) interface{} {
name := args[0].String()
return Greet(name)
}
Try the Web Demo
make serve # Build and start server at http://localhost:8080
How It Works
-
Write normal Go functions in
wasm/main.go:func Greet(name string) string { return "Hello, " + name + "!" } -
Run gowasm-bindgen (generates TypeScript client, worker.js, and compiles WASM):
gowasm-bindgen wasm/main.go -
Or build with custom options:
# Generate only (skip WASM compilation) gowasm-bindgen wasm/main.go --no-build # Compile WASM separately tinygo build -o generated/wasm.wasm -target wasm ./wasm/
Requirements
- TinyGo (recommended)
- Go 1.21+ (alternative, larger binaries)
- Node.js 18+
Click to show internal directories.
Click to hide internal directories.