Getting Started with TinyGo: Bringing Go to Microcontrollers and WebAssembly
Go (or Golang) has taken the software development world by storm thanks to its simplicity, performance, and concurrency model. But traditionally, Go was intended for server-side development — not for systems with constrained resources or compiling to WebAssembly (Wasm). Enter TinyGo – a Go compiler that allows developers to write code for microcontrollers and to web-optimized formats like Wasm.
In this article, we will explore TinyGo, understand how it works, and walk through some practical examples targeting both microcontrollers and the web using WebAssembly.
TinyGo is a project that aims to bring Go to embedded systems and WebAssembly. As its name suggests, it is a small Go compiler based on LLVM. TinyGo is designed to support:
The usual Go compiler (gc) generates binaries optimized for performance, but the tradeoff is in memory usage and binary size. For constrained devices like the Atmega328p found in an Arduino Uno, this is a non-starter. TinyGo addresses this by outputting minimal binary sizes and leveraging LLVM optimizations.
Benefits include:
Before we can build anything with TinyGo, we need to install it.
You’ll need Go 1.20 or higher. Get it from: https://golang.org/dl/
Follow these platform-specific instructions:
brew tap tinygo-org/tools brew install tinygo
wget https://github.com/tinygo-org/tinygo/releases/download/v0.30.0/tinygo_0.30.0_amd64.deb sudo dpkg -i tinygo_0.30.0_amd64.deb
Download the appropriate installer from the TinyGo GitHub Releases page and follow the instructions.
Let’s write a simple Blink program to flash an LED on an Arduino Uno board:
package main import ( "machine" "time" ) func main() { led := machine.LED led.Configure(machine.PinConfig{Mode: machine.PinOutput}) for { led.High() time.Sleep(time.Millisecond * 500) led.Low() time.Sleep(time.Millisecond * 500) } }
Make sure your Arduino Uno is connected, then run:
tinygo flash -target=arduino blink.go
Voila! You should see the onboard LED blinking.
TinyGo can also compile to WebAssembly, making it great for web apps and browser-based games.
Let’s look at a basic Wasm example:
package main import ( "fmt" "time" "syscall/js" ) func main() { js.Global().Set("greet", js.FuncOf(greet)) select {} // block forever } func greet(this js.Value, args []js.Value) interface{} { name := args[0].String() greeting := fmt.Sprintf("Hello, %s! The time is %s", name, time.Now().Format(time.RFC822)) js.Global().Call("alert", greeting) return nil }
tinygo build -o main.wasm -target=wasm ./main.go
<!DOCTYPE html> <html> <head><title>TinyGo Wasm</title></head> <body> <h1>TinyGo WebAssembly</h1> <button onclick="greet('TinyGo Dev')">Say Hello</button> <script src="wasm_exec.js"></script> <script> const go = new Go(); WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then(result => { go.run(result.instance); }); </script> </body> </html>
While TinyGo is powerful, there are some trade-offs to be aware of:
That said, the project is actively evolving and its community is growing rapidly.
TinyGo opens up new possibilities for Go developers:
Getting involved with the TinyGo community is easy:
TinyGo brings the power and elegance of Go to resource-constrained environments and the front-end via WebAssembly. Whether you’re building a blinking LED on an Arduino or an interactive app in your browser, TinyGo enables you to stay within the Go ecosystem.
As embedded devices and WebAssembly grow in popularity, tools like TinyGo may become an integral part of every full-stack developer’s toolkit.
So why not try TinyGo for your next project and explore new horizons in embedded and web development — all with the language you already love?
Stay curious, and happy coding! 🚀
💡 If you're exploring TinyGo for embedded or web-based applications and need help with full stack integration, we offer such services: https://ekwoster.dev/service/fullstack-development
Information