Hex to Text Converter

Paste hex from a packet capture, a database column or a terminal dump, then read the text behind the bytes. Offsets and ASCII columns come off on their own, and the byte table shows you which byte broke the decode.

Shape waitingBytes 0Characters 0Broken 0

Text

Byte inspector
Every byte in the stream with its decimal value, bit pattern and role
OffsetHexDecBitsCharRole

Where your hex came from tells you which setting to pick

Hex arrives in a handful of shapes, and each one comes from a different place. Match the source to the row below and the decode works on the first try.

Common sources of hex and what to do with each
Where you got the hexWhat the paste looks likeSetting
MySQL HEX() or a BLOB column546F6F6C657865Detect for me, UTF-8
Wireshark, Follow TCP Stream, hex viewOffsets on the left, text on the rightHexdump with offsets
xxd or hexdump -C in a terminal00000000: 496e 766f ...Hexdump with offsets
A C or Go byte array in source code0x49, 0x6e, 0x76Prefixed bytes
A Python repr() or a shell string\x49\x6e\x76Escapes
A URL query string%49%6e%76Escapes, or the URL Decoder
A Windows registry export or a .NET dump49,6e,76,6fSeparated bytes, often UTF-16 LE

Detect for me reads the paste and picks a shape, which is printed back to you in the Shape tag under the box. Override it when the guess looks wrong, which happens most often with a dump whose text column holds nothing but hex letters.

Paste a hexdump exactly as the terminal printed it

Most converters choke on dump output, because every line carries three parts and only the middle one is data. Press Load hexdump and the box fills with this:

00000000 49 6e 76 6f 69 63 65 20 32 30 32 36 2d 31 31 20 |Invoice 2026-11 |
00000010 66 72 6f 6d 20 54 6f 6f 6c 65 78 65 |from Toolexe|
0000001c

Three things happen before the bytes get read. The offset at the start of each line goes, so 00000000 and 00000010 never reach the stream. The text column inside the pipe characters goes with it. The lone 0000001c on the last line, which is the file length rather than data, drops out as well.

What remains is 28 bytes decoding to Invoice 2026-11 from Toolexe. Paste the same dump into a converter without this step and the offsets turn into bytes, so the text opens with four null characters and a stray FF. Compare the Bytes count against the last offset in your dump to confirm nothing extra slipped in.

The xxd layout works the same way, with four hex digits per group instead of two and a colon after the offset. Both the GNU and the BSD spellings go through.

The same bytes read four ways

Hex has no encoding of its own. The bytes 4D C3 A1 6C 61 67 61 are seven numbers, and the encoding you choose decides which characters come out. Pick the wrong one and the result still looks like text, which is why a wrong answer here slips past review.

One byte stream through four encodings
BytesUTF-8Windows-1252UTF-16 LEASCII
4D C3 A1 6C 61 67 61MálagaMálaga썍没条�M��laga
C2 A3 32 39 2E 39 39£29.99£29.99ꏂ㤲㤮���29.99
48 00 69 00H‹NUL›i‹NUL›H‹NUL›i‹NUL›HiH‹NUL›i‹NUL›
54 6F 6F 6C 65 78 65ToolexeToolexe潔汯硥�Toolexe

The ‹NUL› marks in the third row are how a null byte looks with Name the control bytes ticked. Leave the box unticked and the same byte is invisible, which is the sneakier case.

Read the rows from the bottom up. Plain English inside the first 128 byte values decodes the same way under UTF-8, Windows-1252 and ASCII, so those three agree on Toolexe. The moment a byte crosses 7F, the three part company.

Two patterns are worth memorising, because they name the mistake out loud:

A leading FF FE or EF BB BF settles the question for you. Those are byte order marks, and the page names the one it finds in the message strip. FF FE 48 00 69 00 under UTF-16 LE gives Hi with the mark absorbed, while the same bytes under UTF-8 open with two replacement characters.

Question marks and black diamonds are a report, not a glitch

When a byte sequence breaks the rules of the encoding, the decoder substitutes U+FFFD, the replacement character. Browsers draw it as a black diamond with a question mark inside. Terminals often draw a plain question mark.

The Broken tag counts them. A count above zero means one of three things:

  1. Wrong encoding. Try Windows-1252 first, then UTF-16 LE. One of the two clears most cases.
  2. A truncated copy. A multi byte character cut in half leaves a lead byte with nothing after it. Check whether the last visible character in the source got clipped.
  3. Not text at all. A PNG, a ZIP or a compiled binary has no readable form. Bytes like 89 50 4E 47 at offset zero are a PNG signature, and 50 4B 03 04 is a ZIP.

Opening the byte inspector marks the offending byte in red in the Hex column, so you get an offset to take back to the source rather than a vague failure.

An odd number of digits means something got cut

Every byte needs two hex digits. An odd total leaves one digit with no partner, so the page drops the final digit, says so in the message strip, and decodes the rest. Three causes account for nearly all of these:

Tick Strict input to hold the output back until the stream is clean. Strict mode also reports any character outside 0-9 and A-F once separators are set aside, which is how a smart quote or a zero width space from a PDF gets caught.

Reading the byte inspector on the sample

Load sample fills the box with 43 bytes that decode to 40 characters. The gap between those two numbers is the point of the exercise, since two characters in the string need more than one byte each.

Open the inspector and look at these rows:

The Bits column is there for the same reason. A UTF-8 continuation byte always starts 10, a 2 byte lead starts 110, and a 3 byte lead starts 1110. Once you have seen the pattern, a glance at the bits tells you where a character begins without counting.

Switch the encoding to ASCII while the sample is loaded and the Broken count jumps to 5, because the five bytes above 7F have no ASCII meaning. The Role column flags each one as outside ASCII.

Doing the same decode in code

Each one liner below has a trap worth knowing before you ship it.

Python

bytes.fromhex("4d c3 a1 6c 61 67 61").decode("utf-8")bytes.fromhex("4dc3a16c61 6761").decode("utf-8", errors="replace")

bytes.fromhex accepts spaces between bytes from Python 3.7 onward, though not commas or 0x prefixes. Without errors="replace" a bad sequence raises UnicodeDecodeError and takes the whole batch down, so the argument belongs in any loop over user data.

JavaScript

const b = Uint8Array.from(hex.match(/[0-9a-f]{2}/gi).map(h => parseInt(h, 16)));new TextDecoder("utf-8").decode(b);

Skip TextDecoder and reach for String.fromCharCode instead, and every byte above 7F turns into the wrong character, since fromCharCode treats a byte as a UTF-16 code unit. TextDecoder is the part doing the real work here.

PHP

echo hex2bin(preg_replace('/[^0-9a-f]/i', '', $hex));

hex2bin throws a warning and returns false on an odd length string, so strip the separators first and check the length before the call.

Shell

echo "4d c3 a1 6c 61 67 61" | xxd -r -p
xxd -r hexdump.txt > original.bin

The -p flag reads plain hex with no offsets. Drop -p and xxd -r expects a full dump with offsets instead, which is the inverse of the second sample on this page.

Going the other way? Text to Hex encodes a string into bytes, with a separator choice and the same encoding list.

Four jobs where hex to text is the wrong step

Hexadecimal shows up in places having nothing to do with character bytes. Sending those through a text decoder returns noise.

Hex that is not text, and where each belongs
What you haveWhy a text decode failsUse instead
#1F4ED8 or rgba workThe three bytes are colour channels, not charactersHex to RGB
0x1A4 from a log or a registerOne number written in base 16, not a byte streamHex to Decimal
A 64 character SHA-256 digestA hash has no plain text behind it and never reversesNothing decodes a hash
C0A80101 from a routing tableFour bytes standing for an addressHex to IP

Base64 is the other near neighbour. A string of mixed case letters ending in = is Base64 rather than hex, so send that to the Base64 Decoder. Hex uses twice the characters of Base64 for the same payload, which is the trade for staying readable in a terminal.

Known limits

Everything runs in your browser. Your bytes never reach Toolexe, and a refresh empties the box.

Hex to text questions

Encodings, dumps, odd digit counts and privacy.

How do I convert hex to text?

Paste the hex into the box. The decode runs as you type, so no button press is needed. Leave Input shape on Detect for me and the page works out whether your bytes are separated, prefixed with 0x or wrapped in a dump. Keep the encoding on UTF-8 unless the result shows odd accented characters.

Why does my decoded text show strange characters like Málaga?

Those are UTF-8 bytes being read as Windows-1252. One character arrives as two. Set the encoding to UTF-8 and the accented letters come back. The reverse pattern, a null byte between every letter, points to UTF-16 instead.

Can I paste xxd or hexdump output directly?

Yes. Offsets at the start of each line, the text column between pipe characters and a trailing length line are all removed before the bytes are read. Press Load hexdump to see a worked example, then compare the Bytes count against the last offset in your own dump.

What does it mean when the digit count is odd?

A byte takes two hex digits, so an odd total leaves one digit unpaired. The page drops the last digit and tells you. The usual causes are a copy cut mid byte, a missing leading zero such as a instead of 0a, or a hex colour code pasted by mistake.

Is hex to text the same as hex to ASCII?

They match for byte values 00 through 7F, which covers unaccented English. Above 7F the two part ways, since ASCII defines nothing there while UTF-8 and Windows-1252 both do. Pick the ASCII option to see exactly which bytes fall outside the 7 bit range.

Does the hex data leave my browser?

No. The decode happens in JavaScript on your own machine, so nothing is uploaded or logged. Closing or refreshing the page clears both boxes.