A CLI tool and Go library for building, optimizing, and packaging WebAssembly modules compiled from Go into a single JavaScript file. Supports self-executing IIFEs, ES modules, CommonJS modules, standalone HTML pages, and a live-reloading development server.
- Build Go → WASM — compiles any Go package with
GOOS=js GOARCH=wasm - TinyGo support — compile with tinygo instead of the standard Go toolchain
- Skip build step — pass a pre-compiled
.wasmfile to skip compilation - Compression — DEFLATE-compresses the binary before embedding (typically 60–80% smaller)
- Four JS output formats — IIFE (
.js), ESModule (.mjs), CommonJS (.cjs), standalone HTML (.html) - wasm-opt integration — optional Binaryen optimisation pass via wasm-opt
- Garble support — optional Go build obfuscation via garble
- JS obfuscation — powered by javascript-obfuscator (no Node.js required)
- JS minification — built-in minifier, no external tools needed
- Pre / post JS injection — prepend or append custom JS files
- Live-reload dev server — watches source files, rebuilds on change, auto-refreshes the browser
go install github.com/malivvan/wasmpack/cmd@latestOr use the library in your Go project:
go get github.com/malivvan/wasmpackWrite a default wasmpack.yml configuration file.
wasmpack init # creates ./wasmpack.yml
wasmpack init ./myproject # creates ./myproject/wasmpack.yml
wasmpack init path/to/cfg.yml # creates the file at the exact pathCompile source and write the result to destination.
Source — a Go package directory, a main.go file, or a pre-compiled .wasm file.
When source ends with .wasm the build step is skipped entirely.
Destination extensions control the output format:
| Extension | Output |
|---|---|
.wasm |
Raw binary only (no JS wrapper) |
.js |
Self-executing IIFE — runs immediately when the script is loaded |
.mjs |
ES module that exports run(env, args) |
.cjs |
CommonJS module that exports run(env, args) |
.html |
Minimal HTML page with the IIFE in a <script defer> tag |
Pack flags:
| Flag | Description |
|---|---|
-garble |
Obfuscate the Go build with garble (options from wasmpack.yml) |
-tinygo |
Compile with tinygo instead of go (options from wasmpack.yml) |
-wasm-opt |
Optimise the wasm binary with wasm-opt (options from wasmpack.yml) |
-minify |
Minify the JS output |
-obfuscate |
Obfuscate the JS output (options from wasmpack.yml) |
wasmpack pack ./sample output.html # standalone HTML page
wasmpack pack ./sample output.js # self-executing IIFE bundle
wasmpack pack ./sample output.mjs # ES module
wasmpack pack ./sample output.cjs # CommonJS module
wasmpack pack ./sample output.wasm # raw binary only
wasmpack pack compiled.wasm output.js # skip compilation, use pre-built .wasm
wasmpack pack -tinygo ./sample output.js # compile with tinygo
wasmpack pack -wasm-opt -minify ./sample out.js # wasm-opt + minified JSExample output:
build 2.41 MB (0.89s)
wrap html
write output.html 2.41 MB → 0.89 MB (37.0%)
done 1.12s
Start a live-reloading development server.
Dev flags:
| Flag | Description |
|---|---|
-coi |
Set Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp headers, enabling a cross-origin isolated context (required for SharedArrayBuffer / Atomics) |
wasmpack dev ./sample :8080 # all interfaces, port 8080
wasmpack dev ./sample localhost:3000 # loopback only
wasmpack dev compiled.wasm :9000 # serve pre-built wasm, watch for changes
wasmpack dev -coi ./sample :8080 # enable cross-origin isolationExample output:
build 2.41 MB (0.89s)
watch /home/user/project/sample
serve http://localhost:8080
· main.go changed
build 2.41 MB (0.34s)
reload 1 client(s)
The server:
- Builds and serves a self-contained HTML page at
/ - Watches all
.gofiles in the source directory (or the.wasmfile) for changes - Rebuilds automatically on change and reloads connected browsers via SSE
- Skips wasm-opt, garble, tinygo, obfuscation, and minification for fast iteration
wasmpack.yml is discovered by walking up from the current working directory — the same mechanism Go uses for go.mod. Run wasmpack init to generate a documented template.
# wasm-opt: options for wasm-opt (used when -wasm-opt flag is passed to pack)
# garble: options for garble (used when -garble flag is passed to pack)
# tinygo: options for tinygo (used when -tinygo flag is passed to pack)
# pre: path to a JS file prepended to the output
# post: path to a JS file appended to the output
# obfuscate: javascript-obfuscator options (used when -obfuscate flag is passed to pack)
# see https://github.com/javascript-obfuscator/javascript-obfuscator
pre: ""
post: ""
garble:
seed: "" # randomness seed (-seed)
literals: false # obfuscate string literals (-literals)
tiny: false # smaller output (-tiny)
flags: "" # extra space-separated garble flags
tinygo:
target: "wasm" # compile target (e.g. "wasm", "wasi")
opt: "" # optimization level (none, 0, 1, 2, s, z)
flags: "" # extra space-separated tinygo flags
wasm-opt:
level: "O2" # optimization level (O1, O2, O3, O4, Os)
flags: "" # extra space-separated wasm-opt flags
obfuscate:
compact: true
controlFlowFlattening: false
# ... all javascript-obfuscator options supported
# see https://github.com/javascript-obfuscator/javascript-obfuscatorimport "github.com/malivvan/wasmpack"
// Compile a Go package to raw WASM bytes.
// useGarble and useTinygo are mutually exclusive; useGarble takes precedence.
wasm, err := wasmpack.Build(
"./mypkg",
useGarble, wasmpack.GarbleConfig{Literals: true},
useTinygo, wasmpack.TinygoConfig{Target: "wasm"},
useWasmOpt, wasmpack.WasmOptConfig{Level: "O2"},
)
// Compress + encode the WASM binary into a JS inflate snippet.
packed, err := wasmpack.Pack(wasm)
// Wrap in wasm_exec.js as a self-executing IIFE.
js, err := wasmpack.WrapIIFE(packed)
// Wrap as an ES module exporting run(env, args).
js, err := wasmpack.WrapESM(packed)
// Wrap as a CommonJS module exporting run(env, args).
js, err := wasmpack.WrapCJS(packed)
// Wrap inside a minimal HTML page (IIFE in <script defer>).
html, err := wasmpack.WrapHTML(packed)
// Run wasm-opt on a WASM file in-place.
err = wasmpack.WasmOpt("/path/to/file.wasm", []string{"-O2"})
// Obfuscate JS (no Node.js required).
out, err := wasmpack.Obfuscate(js, "-controlFlowFlattening")
out, err := wasmpack.ObfuscateWithOptions(js, options)
// Parse obfuscate flag string to options map.
opts, err := wasmpack.ParseObfuscateOptions("-controlFlowFlattening -compact=false")
// Minify JS.
out, err := wasmpack.Minify(js)
// Config helpers.
cfg := wasmpack.DefaultConfig()
path, err := wasmpack.FindConfig(".") // walk-up search
cfg, err = wasmpack.LoadConfig(path)
err = wasmpack.WriteDefaultConfig(path)DEFLATE compression typically achieves a 60–80% reduction before wrapping:
write output.js 5.23 MB → 1.45 MB (27.7%)
- Go 1.25+
- wasm-opt (optional): Binaryen releases
- garble (optional):
go install mvdan.cc/garble@latest - tinygo (optional): tinygo.org/doc/getting-started/overview
sample/main.go prints Hello, World! to the browser console.
# Live-reload dev server
wasmpack dev ./sample :8080
# Build a standalone HTML page
wasmpack pack ./sample sample/output.html