April 09, 2026 at 19:28 Tags Go , WebAssembly , Compilation
I'm happy to announce the general availability of watgo - the WebAssembly Toolkit for Go. This project is similar to wabt (C++) or wasm-tools (Rust), but in pure, zero-dependency Go.
watgo comes with a CLI and a Go API to parse WAT (WebAssembly Text), validate it, and encode it into WASM binaries; it also supports decoding WASM from its binary format.
At the center of it all is wasmir - a semantic representation of a WebAssembly module that users can examine (and manipulate). This diagram shows the functionalities provided by watgo:
Parse: a parser from WAT to wasmir
Validate: uses the official WebAssembly validation semantics to check that the module is well formed and safe
Encode: emits wasmir into WASM binary representation
into WASM binary representation Decode: read WASM binary representation into wasmir
CLI use case watgo comes with a CLI, which you can install by issuing this command: go install github.com/eliben/watgo/cmd/watgo@latest The CLI aims to be compatible with wasm-tools , and I've already switched my wasm-wat-samples projects to use it; e.g. a command to parse a WAT file, validate it and encode it into binary format: watgo parse stack.wat -o stack.wasm
API use case wasmir semantically represents a WASM module with an API that's easy to work with. Here's an example of using watgo to parse a simple WAT program and do some analysis: package main import ( "fmt" "github.com/eliben/watgo" "github.com/eliben/watgo/wasmir" ) const wasmText = ` (module (func (export "add") (param i32 i32) (result i32) local.get 0 local.get 1 i32.add ) (func (param f32 i32) (result i32) local.get 1 i32.const 1 i32.add drop i32.const 0 ) )` func main () { m , err := watgo . ParseWAT ([] byte ( wasmText )) if err != nil { panic ( err ) } i32Params := 0 localGets := 0 i32Adds := 0 // Module-defined functions carry a type index into m.Types. The function // body itself is a flat sequence of wasmir.Instruction values. for _ , fn := range m . Funcs { sig := m . Types [ fn . TypeIdx ] for _ , param := range sig . Params { if param . Kind == wasmir . ValueKindI32 { i32Params ++ } } for _ , instr := range fn . Body { switch instr . Kind { case wasmir . InstrLocalGet : localGets ++ case wasmir . InstrI32Add : i32Adds ++ } } } fmt . Printf ( "module-defined funcs: %d
... continue reading