Skip to content

gomode

TinyGo + Zig in a single WASM binary. 58KB, SIMD, zero-copy, zero overhead.

Go on WASM is broken in two different ways:

Binary sizeStdlibSpeed
Standard Go → WASM3MB+FullSlow (heavy runtime)
TinyGo → WASM~700KBIncompleteFast

Standard Go gives you the full stdlib but a massive binary with a heavy runtime. TinyGo gives you a small fast binary but is missing net/http, crypto, full reflect, and more.

Use TinyGo for the small binary, use Zig to fill every gap — linked into the same WASM binary via wasm-ld. Zero runtime overhead.

Zig (SIMD, allocator, crypto) ──────────→ zig-abi.o
Go handler (TinyGo, CGo, -gc=custom) ──→ wasm-ld → go.wasm (58KB)
JS Worker writes/reads via zerobuf layout (direct DataView on hot path)

Go calls Zig via CGo — compiles to direct WASM call instructions. Same linear memory, no imports, no JS proxy, no serialization.

package main
import (
"gomode"
"unsafe"
)
//export handle_zerobuf
func handleZerobuf(reqBase uint32) uint32 {
path := readZBString(uintptr(reqBase) + 1*16)
switch path {
case "/":
return writeResponse(200, "text/plain", "Hello from GoMode!")
case "/simd":
data := []float64{1, 2, 3, 4, 5, 6, 7, 8}
sum := gomode.ZigSimdSumF64(
uint32(uintptr(unsafe.Pointer(&data[0]))),
uint32(len(data)),
)
return writeResponse(200, "text/plain", formatFloat(sum))
}
return writeResponse(404, "text/plain", "not found")
}
func main() {}
8787/simd
npm run build && npm run dev
# → http://localhost:8787