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
- Binary format decoder Datadecodes the encoded bytes without the schema
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/protobufwell-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.Anyis 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.
Feedback is a gift. Something to improve, a feature to add, or a bug? Tell me.
Runs entirely in your browser — network requests go only to the services named on this page.