A variable-width encoding for the entire Unicode range
Unicode assigns a unique number (a code point) to every character in every script it covers — well over a million possible values, far more than a single byte can hold. UTF-8 is one way of encoding those code points as a sequence of bytes, and its defining feature is that the number of bytes per character varies depending on which code point is being encoded. Plain ASCII characters (code points 0–127) take exactly 1 byte, identical to ASCII itself. Characters in a wider range — most Latin-script accented letters, Greek, Cyrillic — take 2 bytes. Most remaining scripts, including Chinese, Japanese, and Korean characters, take 3 bytes. Less common code points, including most emoji, take 4 bytes.
A few concrete byte counts
The letter "A" encodes as a single byte, 0x41, identical to ASCII. The letter "é" (e with acute accent, U+00E9) encodes as two bytes, 0xC3 0xA9. The euro sign "€" (U+20AC) encodes as three bytes, 0xE2 0x82 0xAC. A grinning-face emoji "😀" (U+1F600) encodes as four bytes,0xF0 0x9F 0x98 0x80. Notice the pattern isn't arbitrary: each byte count corresponds to a specific range of the underlying code point, and looking at the leading bits of the first byte in a sequence tells a decoder exactly how many continuation bytes to expect next.
How a decoder knows where each character starts
UTF-8's byte-length signaling is built into the bit pattern of each byte, which is what makes it possible to decode correctly without a separate length field or delimiter. A byte starting with a 0bit is a single-byte (ASCII-range) character. A byte starting with 110 begins a 2-byte sequence; 1110 begins a 3-byte sequence; 11110 begins a 4-byte sequence. Continuation bytes — the second, third, or fourth byte of a multi-byte sequence — always start with10. That structure means a decoder encountering unexpected bits (a continuation byte where a lead byte was expected, for instance) can immediately detect invalid UTF-8 rather than silently misreading the stream, which is why this tool reports an error on malformed byte sequences instead of guessing at a result.
Why UTF-8 displaced ASCII and other encodings
Before UTF-8 became dominant, different regions and vendors used different single-byte or double-byte encodings for non-English text — Latin-1 for much of Western Europe, Shift-JIS for Japanese, and dozens of others, none compatible with each other. Exchanging text between systems using different encodings without knowing which encoding was in use reliably produced garbled output (sometimes called "mojibake"). UTF-8 solved this by being both universal (covering every script in one encoding) and backward-compatible (any existing ASCII text is already valid UTF-8, byte for byte), which removed the incentive to keep using incompatible legacy encodings. That combination is why UTF-8 is now the dominant encoding for web pages, JSON, and most modern file formats and protocols.
UTF-8 versus UTF-16
UTF-16, used internally by Windows, Java, and JavaScript strings, takes a different approach: most common characters take 2 bytes, and less common ones take 4, rather than UTF-8's 1-to-4 byte range. That makes UTF-16 more compact for scripts where every character needs 3 bytes in UTF-8 (many Asian scripts), but it isn't backward-compatible with ASCII the way UTF-8 is — an ASCII "A" in UTF-16 is 2 bytes (0x0041), not 1 — and it also requires specifying byte order (a UTF-16 file needs to declare whether it's big-endian or little-endian), a complication UTF-8 doesn't have since its byte sequences are unambiguous regardless of machine endianness. That combination of universal backward compatibility and no endianness question is a large part of why UTF-8 became the standard for interchange formats — files, network protocols, and web content — even in environments where a language's internal string representation uses something else.
Where hex-level UTF-8 inspection is useful
Seeing the raw bytes behind a piece of text is useful when debugging encoding mismatches — text that displays as garbled symbols usually means it was encoded in one scheme (often UTF-8) but is being read back under the assumption of a different one (commonly Latin-1), and comparing the actual hex bytes against what each encoding would produce quickly confirms which is which. It's also useful when working with binary protocols or file formats that embed UTF-8 strings inside otherwise non-text data, where a hex/text side-by-side view makes it possible to locate exactly where the text portion starts and ends within a larger byte stream.