# How to Use protobuf-go-lite with TinyGo for WebAssembly and Embedded Systems

> Learn to use protobuf-go-lite with TinyGo for WebAssembly and embedded systems. Generate static, reflection-free protobuf code for efficient development.

- Repository: [Aperture Robotics/protobuf-go-lite](https://github.com/aperturerobotics/protobuf-go-lite)
- Tags: how-to-guide
- Published: 2026-02-25

---

**Use `protobuf-go-lite` to generate static, reflection-free protobuf code that compiles seamlessly with TinyGo for WebAssembly and embedded targets by enabling only the `marshal+unmarshal+size` features during code generation.**

The `aperturerobotics/protobuf-go-lite` repository provides a lightweight fork of the official Go protobuf implementation specifically designed to eliminate reflection-based code generation. This architectural choice makes it fully compatible with TinyGo, a Go compiler that targets constrained environments where the standard `reflect` package is unavailable or severely limited.

## Why protobuf-go-lite Works with TinyGo

Traditional protobuf implementations rely heavily on Go's reflection package to handle message serialization dynamically. TinyGo, particularly when targeting WebAssembly or microcontrollers, either omits or significantly trims the `reflect` package to reduce binary size.

The `protobuf-go-lite` generator solves this by producing **static code generation** for all serialization logic. In [`features/marshal/marshal.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/marshal/marshal.go), the generator emits methods like `MarshalVT()` that unroll wire encoding logic at compile time rather than using reflection. Similarly, [`features/unmarshal/unmarshal.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/unmarshal/unmarshal.go) generates `UnmarshalVT()` methods that parse protobuf wire formats using only primitive operations and the minimal `protowire` utilities.

Key architectural benefits include:

- **Zero reflection dependencies**: The generated code in [`generator/helpers.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/generator/helpers.go) uses only wire-type constants and primitive sizing logic, never `reflect.TypeOf` or similar calls.
- **Configurable build tags**: The generator in [`generator/generator.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/generator/generator.go) supports prepending custom `//go:build` constraints to generated files, allowing you to create TinyGo-specific variants.
- **Minimal runtime**: The [`protobuf-go-lite.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/protobuf-go-lite.go) runtime file contains only thin utility functions, keeping the final binary size small for embedded targets.
- **Feature toggles**: You can enable only the features you need via the `--features` flag in [`cmd/protoc-gen-go-lite/main.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/cmd/protoc-gen-go-lite/main.go), minimizing code size by excluding unnecessary functionality.

## Generating TinyGo-Compatible Protobuf Code

To use `protobuf-go-lite` with TinyGo, you must generate code using specific feature flags that exclude reflection-dependent functionality.

First, install the plugin:

```bash
go install github.com/aperturerobotics/protobuf-go-lite/cmd/protoc-gen-go-lite@latest

```

Next, generate code with only the essential features enabled. The [`cmd/protoc-gen-go-lite/main.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/cmd/protoc-gen-go-lite/main.go) entry point accepts a `--features` flag that controls which generators run:

```bash
protoc \
  --go-lite_out=. \
  --go-lite_opt=features=marshal+unmarshal+size \
  message.proto

```

This command generates a [`.pb.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/.pb.go) file containing `MarshalVT`, `UnmarshalVT`, and `SizeVT` methods. The [`features/size/size.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/size/size.go) generator ensures buffer pre-allocation logic is also emitted, optimizing performance without requiring reflection.

## Complete TinyGo Example: WebAssembly and Embedded

Below is a complete workflow demonstrating protobuf serialization with TinyGo for both WebAssembly and embedded ARM targets.

### Protocol Definition

Create a `message.proto` file:

```proto
syntax = "proto3";

package example;

message SensorReading {
  string sensor_id = 1;
  int32  value     = 2;
  bool   alert     = 3;
}

```

### TinyGo Application

After running the `protoc` command from the previous section, create a [`main.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/main.go) file:

```go
package main

import (
	"fmt"
	"example"
)

func main() {
	// Create a message
	reading := &example.SensorReading{
		SensorId: "temp_01",
		Value:    42,
		Alert:    true,
	}

	// Serialize using the generated MarshalVT method
	data, err := reading.MarshalVT()
	if err != nil {
		panic(err)
	}
	fmt.Printf("Serialized %d bytes\n", len(data))

	// Deserialize using the generated UnmarshalVT method
	var decoded example.SensorReading
	if err := decoded.UnmarshalVT(data); err != nil {
		panic(err)
	}
	fmt.Printf("Decoded: sensor_id=%s value=%d alert=%t\n", 
		decoded.SensorId, decoded.Value, decoded.Alert)
}

```

### Building for WebAssembly

Compile to WebAssembly using TinyGo's `wasm` target:

```bash
tinygo build -target wasm -o sensor.wasm .

```

The resulting `sensor.wasm` file contains the complete protobuf serialization logic without any reflection overhead, making it suitable for browser environments or Wasm edge runtimes.

### Building for Embedded Systems

For ARM Cortex-M microcontrollers, specify the appropriate bare-metal target:

```bash
tinygo build -target=thumbv6m-none-eabi -o sensor.bin .

```

The `thumbv6m-none-eabi` target produces a bare-metal binary for Cortex-M0/M0+ processors. Because `protobuf-go-lite` generates only static code, the binary size remains minimal—critical for microcontrollers with limited flash memory.

## Key Repository Files for TinyGo Integration

Understanding the source structure helps troubleshoot build issues and customize the generator for specific embedded constraints.

- **[`cmd/protoc-gen-go-lite/main.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/cmd/protoc-gen-go-lite/main.go)** – Entry point that parses the `--features` flag and initializes the code generator. Controls which serialization features are emitted.

- **[`generator/generator.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/generator/generator.go)** – Core driver that emits the `//go:build` header and orchestrates feature-specific generators. Handles file-level code generation and build tag injection.

- **[`features/marshal/marshal.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/marshal/marshal.go)** – Generates the `MarshalVT()` method family. Creates static wire-encoding logic without reflection.

- **[`features/unmarshal/unmarshal.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/unmarshal/unmarshal.go)** – Generates the `UnmarshalVT()` method family. Parses protobuf wire format using only primitive operations.

- **[`features/size/size.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/size/size.go)** – Generates `SizeVT()` for buffer pre-allocation. Optimizes memory allocation during serialization.

- **[`protobuf-go-lite.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/protobuf-go-lite.go)** – Minimal runtime utilities. Contains only thin helper functions to keep binary size small.

- **[`generator/helpers.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/generator/helpers.go)** – Wire-type utilities and sizing logic used across all generators. Defines the mapping between protobuf types and wire formats.

## Summary

- **protobuf-go-lite** generates static, reflection-free protobuf serialization code that compiles with TinyGo for WebAssembly and embedded targets.
- Enable only essential features (`marshal+unmarshal+size`) via the `--go-lite_opt=features` flag to minimize binary size and eliminate reflection dependencies.
- The generated `MarshalVT` and `UnmarshalVT` methods in [`features/marshal/marshal.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/marshal/marshal.go) and [`features/unmarshal/unmarshal.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/unmarshal/unmarshal.go) operate without `reflect` package usage.
- TinyGo can compile the generated code to WebAssembly (`-target wasm`) or bare-metal embedded targets (`-target thumbv6m-none-eabi`) without modification.
- The minimal runtime in [`protobuf-go-lite.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/protobuf-go-lite.go) ensures small binary sizes critical for microcontrollers and browser Wasm environments.

## Frequently Asked Questions

### Does protobuf-go-lite support JSON serialization with TinyGo?

Yes, but with caveats. The JSON feature in [`features/json/message.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/json/message.go) generates code that relies on `github.com/valyala/fastjson`, which TinyGo can import. However, including JSON support increases binary size compared to using only `marshal+unmarshal`. For the smallest TinyGo binaries, disable JSON and use binary protobuf format exclusively.

### Can I use the standard google.golang.org/protobuf library with TinyGo instead?

No. The standard `google.golang.org/protobuf` library relies heavily on the `reflect` package for message marshaling and unmarshaling. TinyGo either omits or severely limits reflection support, particularly in WebAssembly and embedded targets, causing compilation failures or runtime panics. Use `protobuf-go-lite` specifically because it generates static code without reflection.

### How do I reduce binary size further for microcontrollers?

Enable only the minimal feature set using `--go-lite_opt=features=marshal+unmarshal`. Omit the `size` feature if you can tolerate slightly less efficient buffer allocation, or omit `json` to avoid pulling in additional dependencies. Additionally, use TinyGo's `-opt=z` optimization flag for aggressive size reduction: `tinygo build -target=thumbv6m-none-eabi -opt=z -o firmware.bin .`.

### What build tags does protobuf-go-lite support?

The generator supports custom build tags via the `--go-lite_opt=buildTag=<tag>` flag. In [`generator/generator.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/generator/generator.go), this prepends `//go:build <tag>` to every generated file. This allows you to maintain separate TinyGo-specific generated files alongside standard Go builds, or exclude files that depend on unsupported features in specific environments.