Visual walkthrough — HTTP - 2 — multiplexing, header compression (HPACK), server push
Step 1 — What a byte even is, and why 8 slots
WHAT. A byte is a row of 8 bits. A bit is a single slot holding a or a . That is the smallest thing the wire carries — everything in HTTP/2, every frame, every header, is ultimately rows of these 8-slot bytes.
WHY start here. HPACK's whole cleverness is reusing the same byte for two jobs: a few of the 8 slots say what kind of thing this is (a flag), and the rest carry a number. You cannot understand "5-bit prefix" until you can see the 8 slots and point at which ones are flags.
PICTURE. Below, the 8 slots of one byte. Each slot has a "place value" — the leftmost slot is worth , then . A slot set to adds its place value; a slot set to adds nothing. To read a byte, you sum the place values of the slots that are .

Step 2 — Splitting one byte into "flag bits" + "number bits"
WHAT. HPACK does not use all 8 slots for the number. It reserves the top few slots as flags (a code saying how to interpret this header), and leaves the bottom slots for the number. That bottom region is the ==-bit prefix==.
WHY. Every header field has to announce its representation type first: "I am a fully indexed header" ( in the top slot), or "I am a literal that should be remembered" ( in the top two slots), and so on. Those type-bits eat some of the 8 slots. Whatever is left over is where the actual index number lives. Different types leave a different number of slots — that is exactly why the spec keeps saying "-bit prefix" instead of a fixed number: depends on how many flag bits that representation used.
PICTURE. Same byte, now painted in two colours: burnt-orange flag slots on the left, teal number slots on the right. For the "indexed" case ( flag) the prefix is slots. For "literal with incremental indexing" ( flag) the prefix is slots. For a plain literal-name reference it is .

Step 3 — The easy case: the number fits in the prefix
WHAT. If our number is small enough to fit in the slots, we just write it there and we are done. "Small enough" means strictly less than .
WHY the boundary (not )? The slots can hold values up to . But HPACK reserves the all-ones pattern () as a special signal meaning "I overflowed — keep reading more bytes". So the values that fit-and-stop are through . The instant equals , we are not allowed to just write it; that pattern is the "continue" flag. That is the single most important rule on this page.
PICTURE. A 5-bit prefix (, so ). We store . Since , the teal slots become and the orange flag slots sit untouched on top. One byte total.

Step 4 — Why we need continuation bytes at all
WHAT. What if ? The prefix cannot hold it. So we do two moves: (1) fill the prefix with all-ones — the "keep reading" signal — and (2) store the remainder in extra bytes that follow.
WHY subtract first? Because the prefix already "used up" that much magnitude just by being all-ones. We do not want to count it twice. Storing the leftover keeps the numbers as small as possible, which keeps the byte-count down. This is the classic variable-length integer idea: small numbers cost few bytes, big numbers cost more, and nothing has a fixed wasteful size.
PICTURE. A number too big for the 5-bit prefix. The prefix turns solid (), and an arrow points out of the byte toward the continuation bytes we are about to build in Step 5.

Step 5 — The 7-bit continuation groups (the varint engine)
WHAT. We encode the leftover in continuation bytes. In each such byte, the top slot is a flag ( = "another byte follows", = "this is the last one") and the bottom 7 slots carry a chunk of the number. We peel off 7 bits of at a time.
WHY 7, not 8? Because we spent 1 slot per byte on the "more follows?" flag. That leaves 7 slots for data. So each continuation byte carries a value –, and (the top slot) becomes the "keep going" marker. The loop:
- — the lowest 7 bits (the current chunk).
- — set the top slot to say "more bytes coming".
- — shift right by 7 bits, exposing the next chunk.
- The final emit has , so its top slot is → decoder stops.
PICTURE. The remainder shown as a tall stack, sliced into 7-bit chunks bottom-up. Each slice becomes a byte; all but the last wear a burnt-orange "continue" top bit; the last wears a teal "stop" top bit.

Step 6 — Decoding: reading it back
WHAT. The decoder reverses everything. Read the prefix. If it is less than , that IS the number, stop. If it equals , keep reading continuation bytes, accumulating 7 bits at a time, until a byte with top slot .
WHY it is unambiguous. The all-ones prefix and the top "continue" bits are self-describing: at every byte the decoder knows whether to expect more without any length field. That is what makes a varint safe to drop into a byte stream.
PICTURE. The decode of 31, 154, 10 back to : prefix says "overflow, start at base
"; then contributes its low 7 bits at weight ; then (stop) contributes at
weight ; sum .

Step 7 — Edge & degenerate cases (never leave the reader stranded)
WHAT & WHY, each with its own row in the figure:
- . Fits in any prefix (). Written as all-zeros in the number slots. One byte.
- (the largest that still fits). E.g. . Still one byte — because . This is the last "cheap" value.
- (the trap). E.g. . Does not fit-and-stop! Prefix goes all-ones and remainder , so we still emit one continuation byte (, top bit off). Total two bytes to store . This surprises people: costs one byte, costs two.
- Multiple of 128 as remainder. If exactly: so emit , then emit . Two continuation bytes.
PICTURE. A little table-figure: the "cliff" at where byte-count jumps from 1 to 2, plus the and cases drawn out.

Recall Why does index 31 cost more than index 30 in a 5-bit prefix?
Because is reserved as the "keep reading" signal. Values – fit-and-stop in one byte; must set the all-ones flag and then emit a continuation byte for the remainder (), costing two bytes.
The one-picture summary

This final figure runs the whole machine on one number: a byte split into orange flags + teal -bit prefix; the fit-or-overflow decision; and the 7-bit continuation chain feeding out to the right, top-bit meaning "more". That is all of HPACK integer encoding — the mechanism that turns a repeated 200-byte cookie into a single referencing byte once it lives in the dynamic table.
Recall Feynman retelling — say it back in plain words
A byte is 8 slots. HPACK spends the top slots on a type flag and leaves the bottom slots as a tiny box for a number. If the number fits in the box (strictly under ), just drop it in — done, one byte. If it is too big, fill the box with all-ones as a "there's more" signal, subtract that much off, and pour the leftover into follow-up bytes 7 bits at a time — each such byte's top slot says "another one coming", until a byte says "I'm the last". To decode, read the box; if it's all-ones, keep grabbing 7-bit chunks (lowest chunk first) and add them up with weights on top of the box value. The one gotcha: the value itself never fits — it's the escape hatch — so index ironically costs more than index .
Related: this varint trick reappears whole in Varint-encoding; the reason HPACK avoids generic gzip lives in CRIME-and-BREACH-attacks; the literal-string path uses Huffman-Coding; and the whole scheme sits over TCP/TLS, superseded transport-wise by HTTP-3-and-QUIC.
Quick self-checks:
Encode in a 5-bit prefix
Encode in a 5-bit prefix
31, 154, 10.