Chris Collins

Tools · encoding

Protobuf and gRPC playground

Write a .proto, fill in a message with a generated form or as JSON, and see every byte of its encoding explained — tags, varints, zigzag, fixed widths, lengths, packed lists, maps, oneofs — next to its JSON size; gRPC and gRPC-Web framing; and a schema-evolution checker that says which changes between two versions break old or new code.

Added

Works with

How to use it

Protocol buffers are compact because the bytes carry almost nothing but numbers: a field number and a wire type in each tag, and values in one of six shapes. This page parses your .proto with protobufjs, encodes the message you fill in, and explains every byte. It also shows how gRPC wraps those bytes, and checks what happens when the schema changes and old and new programs exchange data.

How to use it

Encode. Paste or edit a .proto (proto3 or proto2; the well-known types such as google/protobuf/timestamp.proto can be imported), choose a message, and fill it in with the generated form or as JSON. The form follows the schema: nested messages, repeated fields and maps with add and remove, a choice for each oneof, a set/not-set switch for fields with presence (optional, messages, proto2 fields), enums as lists, Timestamp and Duration as text. The bytes appear as a hex strip coloured by role — tag, length, value, packed element — and a list that explains each run: tag 0a = field 1 << 3 | wire type 2 (LEN) → name: string, the varint’s 7-bit groups, the zigzag form of an sint32, a float’s little-endian bytes. Hover a byte or a row to see the other. Beside it: the size against the same message as JSON (and both gzipped), and the proto3 canonical JSON. The examples cover the protobuf tutorial’s address book, one field of every wire type, maps and oneofs, proto2 required fields, defaults and groups, the well-known types, and the gRPC route guide service.

Decode. Paste hex or base64, choose the message type, and get the same byte-by-byte explanation and the decoded JSON. A gRPC frame header in front is stripped. Fields the schema doesn’t know, wire types that don’t match the field, truncated data and invalid UTF-8 are shown where they occur. For bytes without a schema, or for MessagePack, CBOR and BSON, use the binary format decoder — this page links its output there instead of repeating it.

gRPC framing. For each method of a service in the .proto: the HTTP/2 request headers (:path /package.Service/Method, content-type: application/grpc+proto, te: trailers, grpc-timeout), the request and response bodies as length-prefixed messages (a compressed flag, a four-byte big-endian length, the protobuf bytes; streams are just more of them), optional gzip compression, and the trailers with grpc-status. Then the gRPC-Web variant — the trailers as a final frame flagged 0x80 in the body — and grpc-web-text, the same bytes base64-encoded. You can also paste a captured body and split it into frames.

Schema evolution. Edit an old and a new version of a .proto, or pick an example, and get a report: breaking (data is lost, misread or rejected), careful (fine on the wire but JSON, generated code or some values change) or fine. It checks renumbered fields; removed fields and whether their numbers and names were reserved; reused numbers; type changes against the wire compatibility rules (int32/int64/uint32/uint64/bool share varints, sint* zigzag and can’t mix with them, fixed32 and sfixed32 share bytes, float doesn’t, string and bytes are compatible for valid UTF-8, a message is compatible with bytes, a map with a repeated entry message); repeated and singular; moves into and out of oneof; proto2 required added, removed or changed, and changed defaults; enum values renamed, renumbered, removed or added (and how open proto3 and closed proto2 enums treat an unknown value); and services — removed methods, changed streaming, changed request or response types, and a changed package, which changes every gRPC path. Under the report, a message written with the old schema is read with the new one so you can see the result.

Wire format is a short reference with a varint/zigzag calculator and a tag calculator.

Works in any current browser; gzip sizes need CompressionStream (Chrome, Edge, Firefox, Safari 16.4 and later). Related: binary format decoder (protobuf without a schema, MessagePack, CBOR, BSON), GraphQL playground, HTTP request client, Base64.

Limits

  • One file: imports other than the google/protobuf well-known types can’t be fetched — paste the definitions into the file.

  • Extensions, custom options and editions features beyond what protobufjs 7 understands are not covered; google.protobuf.Any is shown as its type URL and bytes.

  • The evolution checker compares one file with one file. It reads the rules from the protobuf documentation ("Updating a message type", the proto3 and proto2 language guides); it does not know which messages are stored or sent where, so some careful findings may not matter to you.

Linking to an example

Share link copies a link with the .proto, the message and its value. By hand, ?example= opens an example: ?example=wire (every wire type), addressbook, maps, proto2, wkt, routeguide#grpc; ?evo= loads an evolution pair: ?evo=types#evolution, safe, renumber, proto2, enums, oneof, service; ?hex= puts bytes in the decoder: ?hex=089601#decode.

Privacy

protobufjs 7.5.4 (BSD-3-Clause) and long.js 4.0.0 (Apache-2.0), about 30 KB compressed, are loaded from cdn.jsdelivr.net when the page opens. Everything else happens in this browser: your .proto files and messages are never sent anywhere, and are kept in this browser’s storage so they are there next time.

Runs entirely in your browser — network requests go only to the services named on this page.