Consider a message containing only a waving hand: 👋. It looks like one character, uses two UTF-16 code units in JavaScript, and needs four UTF-8 bytes. A length mismatch can come from measuring different representations of the same text.
Inspect the bytes with a local example
In Base64 Lab, choose UTF-8 text and Encode. Paste the emoji without surrounding quotes or spaces.
Input: 👋
Expected Base64: 8J+Riw==
Expected source bytes: 4
Switch to Decode with that Base64 value. The expected text is the same waving hand. Switch to Binary file mode while decoding to inspect the actual bytes:
f0 9f 91 8b
Choose the measurement your boundary needs
A byte limit belongs to an encoding. A database column limit, a network content length, and a user-facing character counter need not use the same unit. JavaScript’s string length counts UTF-16 code units; it does not directly measure UTF-8 storage or visible symbols.
This standalone JavaScript example can run in a developer console. It inspects fixed data and requires no libraries.
const greeting = "👋";
greeting.length; // 2 UTF-16 code units
[...greeting].length; // 1 Unicode code point
new TextEncoder().encode(greeting).length; // 4 UTF-8 bytes
Do not normalize without a contract
The letter é can be represented as one code point or as e followed by a combining accent. They can appear identical while producing different bytes and different Base64. Diggwigs does not silently normalize your text.
Input: é
Expected Base64: w6k=
Input: é (e + U+0301 combining acute accent)
Expected Base64: ZcyB
More complex emoji can contain several code points joined into one visible symbol. When designing a character counter, use grapheme segmentation appropriate to the product. When checking a wire format, inspect the encoded bytes.
Preserve the original text while investigating. A cleanup step can erase the exact difference you were trying to find.