Skip to content
Tech News
← Back to articles

Watgo – A WebAssembly Toolkit for Go

read original get WebAssembly Go Toolkit → more articles
Why This Matters

The release of watgo marks a significant advancement for the Go ecosystem by providing a pure, zero-dependency toolkit for WebAssembly development. Its capabilities for parsing, validating, and manipulating WebAssembly modules streamline workflows for developers working with WebAssembly in Go, fostering broader adoption and innovation in web and system applications.

Key Takeaways

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