Encoding / Decoding

Hex Encode / Decode

Convert text to/from hexadecimal (0x or raw hex).

Original
Hex result

What is Hex Encoding?

Hex encoding converts text to hexadecimal representation by bytes, or decodes hex strings back to text. Supports space-separated, continuous, and 0x prefix formats.

Use Cases

  • Binary data inspection and debugging
  • Network packet analysis
  • Cryptographic operations
  • Low-level programming

Hex encoding writes every byte as two hexadecimal digits, exposing the raw byte string behind a piece of text. This tool encodes UTF-8 text to space-separated, continuous, or 0x-prefixed hex and decodes such strings back to text, matching what packet captures, digests, and key fingerprints show.

Two digits per byte, never per character

Encoding first calls TextEncoder to get UTF-8 bytes, then prints each byte as exactly two lowercase digits. One hex digit carries 4 bits, so a byte ranging from 0x00 to 0xFF always occupies two characters and byte boundaries remain visible even without separators. The unit is the byte, not the character: Hello is 5 bytes and 10 digits, 你 is 3 bytes, and an emoji outside the BMP is 4 bytes. That is what separates hex from code-unit notations such as \uXXXX, where the same emoji appears as a surrogate pair of UTF-16 units instead of four UTF-8 bytes. Counting digits therefore tells you how many bytes are on the wire, which is the property you need when comparing captures, digests, or key material.

The decoder validates instead of repairing

Before parsing, the decoder deletes every character in the JavaScript \s class (spaces, tabs, newlines, plus U+00A0 and the ideographic space U+3000) and removes each literal 0x. The remainder must consist only of 0-9, a-f, A-F and have an even digit count; otherwise the output pane stays empty. So 0X48 with an uppercase prefix, 48:69 with a colon separator, and 486 with a truncated final digit all produce nothing at all, without any error text. An empty result is the decoder saying the string is not hex. The prefix removal is a global deletion rather than a prefix check, which is harmless for valid hex because x is not a hex digit, but it does mean positions of 0x are never rejected.

Malformed UTF-8 becomes U+FFFD, not an error

Decoding runs TextDecoder with default options, where an invalid sequence does not throw: offending bytes are replaced with U+FFFD, the replacement character. Decoding ff yields one such character; decoding the GBK bytes c4 e3 as UTF-8 yields two, rather than 你. Encoding is always lossless, but decoding is lossy whenever the bytes are not UTF-8, so a readable result does not prove that the source encoding was right. Only bytes written by this encoder, or by another UTF-8 producer, are guaranteed to round-trip. When you need a hard failure in your own code, use new TextDecoder("utf-8", { fatal: true }); this tool stays permissive so that arbitrary binary input still shows something.

Worked example

Encoding text that mixes ASCII and a non-BMP emoji

Input
Hi 🙂
Output
48 69 20 f0 9f 99 82

The input is 7 UTF-8 bytes: H, i, one space, then the four bytes of the emoji, so the space-separated output has 7 groups and 20 characters including 6 delimiters. The continuous format gives 486920f09f9982, the 0x format gives 0x48 0x69 0x20 0xf0 0x9f 0x99 0x82, and all three decode back to the same string.

Limits and pitfalls

  • After whitespace removal the digit count must be even; one truncated byte such as 48656c6c6 silently returns an empty pane rather than an error message.
  • Only whitespace and 0x are tolerated. Separators such as 48:65 or 48-65, C-style escapes such as \x48, and uppercase 0X prefixes are all rejected by the hex-only check.
  • Output is lowercase and carries no byte offsets or ASCII gutter, so it is a plain string conversion, not a hex dump for inspecting large binary blobs.
  • Non-UTF-8 input decodes to U+FFFD characters instead of failing, so a decode that "works" does not confirm the original encoding.
  • Output grows exactly 2x before delimiters, 2x plus one space per byte in the space-separated format, and 4x in the 0x format, which is why hex is a debugging notation rather than a transport encoding.

Related tools

This page, a terminal, or a compact encoding: six choices

What you are doingHex tool on this pageWhat to reach for instead
One value copied out of a logPaste it in decode mode and the pane answers at once: the 16 digits 5a4d2d3031323334 become 8 bytes and read back as ZM-01234. If the pane stays empty, the characters were never hex.When the bytes live in a file or a pipe, no screen is needed: printf 5a4d2d3031323334 | xxd -r -p writes the same 8 bytes out, and stdout takes them without a copy step.
A file with a byte offset to findThere is no file picker here; the bytes have to travel through the textarea, and the output carries no position information, so a value cannot be traced back to an address in the file.xxd firmware.bin streams the whole file with an offset column and an ASCII gutter on every line. Those two columns are exactly what this page omits, and they are what pins a byte to an address.
Hex that a machine has to carryEvery format is a reading format: two characters per byte continuous, four with the 0x prefix, plus one separator per byte in both of the spaced variants.For a JSON field, a header or a query string, use Base64 from this site instead — three bytes per four characters, roughly 1.34x, and no separator to escape. Keep hex for the copy that a human reads.
Deciding whether the reader or the data is wrong中 encodes to e4 b8 ad, three separate bytes, so a mismatch can be pinned on one byte and the encoding assumption it breaks.The character/ASCII lookup reports the same character as 20013 / 0x4E2D, one code point. If the numbers agree on both ends and the text still looks wrong, the encoding is not the bug.
Getting those bytes into source codeThe output is raw byte pairs, so e4 b8 ad only becomes \xe4\xb8\xad after you add the dialect yourself — the right shape for C strings, Python bytes and shell escapes.JavaScript, JSON and HTML take a UTF-16 code unit, not a byte: the Unicode escape tool emits \u4e2d and converts a whole string in one pass instead of one group at a time.
The same conversion 500 timesOne paste, one conversion. That is the correct trade for a handful of values and the wrong one for a column in a CSV, where the manual step is what introduces the typo.Script it: printf "%s" "$s" | xxd -p, or in Node Buffer.from(s, "utf8").toString("hex") inside a map. A scripted run also leaves a diff you can review later.

One concrete scenario

Reading a fixed-width serial out of a captured frame

A serial capture shows 5a 4d 2d 30 31 32 33 34 in the payload column, and the protocol doc calls that field a fixed 8-byte ASCII serial. Paste it in decode mode: the 16 digits are 8 bytes, which TextDecoder renders as ZM-01234, and since every byte is ASCII nothing turns into U+FFFD. Switch back to encode — the decoded text is carried into the input — and the same 8 bytes come out as 16 characters continuous, 23 space-separated (16 digits plus 7 separators) or 39 in 0x (32 digits with prefixes plus 7 separators). Before comparing two captures of that field, halve the digit count: 23 and 39 characters are both 8 bytes. If a unit reports a 9-character serial, the 8-byte field is what truncates it, not the encoding.

Frequently Asked Questions

Why does a 2-character Chinese text produce 6 bytes in hex?
Hex encodes UTF-8 bytes, not characters: each Chinese character takes 3 bytes in UTF-8, so two characters are 6 bytes — 12 hex digits. This also makes hex a great debugging tool: garbled text almost always means bytes were decoded with the wrong encoding.
Both hex and Base64 are encodings — how do I choose?
Hex is longer (2 characters per byte) but human-readable and maps directly to memory bytes — ideal for debugging, hash values, and key fingerprints. Base64 is more compact (about 1.33 characters per byte) and better for carrying data through text protocols.
Why does decoding produce garbled output?
Usually the byte sequence is not valid UTF-8 (for example GBK-encoded text), or the hex string contains spaces or 0x prefixes. This tool accepts spaces and 0x prefixes; legacy encodings like GBK need a dedicated encoding converter.

Content reviewed: