Unicode Encoder / Decoder

Convert text to \uXXXX (JSON-safe surrogate pairs), \u{X}, \U0001XXXX, U+XXXX, HTML entities, percent-encoding or raw UTF-8/UTF-16/UTF-32 bytes — and decode all of them back, with unrecognized tokens reported instead of dropped. Includes a per-character inspector, honest code point / UTF-16 / grapheme / byte counters, NFC-NFD-NFKC-NFKD comparison and an invisible-character detector.

Input

\uXXXX — JavaScript / JSON (surrogate pairs)

The only JSON-safe form. Characters above U+FFFF become a surrogate pair (😀 → 😀), and backslash, double quote and control characters are always escaped so the result can be pasted between quotes.

Examples:

Encoded result

Encoded output will appear here

Start typing — conversion happens as you type

Text statistics

Measured on the readable side of the conversion — your input. "Characters" means four different things in Unicode, so all four are shown.

0
Code points
Unicode characters — what U+ notation counts.
0
UTF-16 units
What JavaScript string.length returns.
0
Grapheme clusters
What a person would call "characters" (Intl.Segmenter).
0
UTF-8 bytes
Storage size in a UTF-8 database column or file.

Invisible & bidirectional characters

Paste some text to scan it for zero-width spaces, non-breaking spaces, soft hyphens, byte-order marks and bidirectional controls.

Unicode normalization

The same visible text can be several different code-point sequences. NFC composes, NFD decomposes; the NFK forms additionally fold compatibility characters (fi → fi, ① → 1) and are lossy, so use them for search keys, never for storage.

Enter text to compare its four normalization forms.

Character inspector

One row per code point. Enter text to inspect it.

There is no official character-name database in the browser, so this table does not show names like "GRINNING FACE" — shipping the full Unicode name list would add well over a megabyte to the page. General category and block are derived from Unicode property escapes and a built-in block table; code points outside the common blocks are labeled "Other". For the authoritative name, look the code point up in the Unicode character database.

Which escape form should you use?

  • • JSON or any JSON parser: \uXXXX with surrogate pairs. \u{1F600} is valid JavaScript but invalid JSON, and it is the single most common cause of "my emoji broke the API payload".
  • • Modern JavaScript source: \u{1F600} is easier to read, and any ES2015+ engine accepts it.
  • • HTML: escape & < > and " at minimum. Numeric references work everywhere; named ones only exist in HTML (XML predefines just five: amp, lt, gt, quot, apos).
  • • URLs: percent-encode the UTF-8 bytes. encodeURIComponent for a query parameter value, encodeURI only for a whole URL — they escape different sets.
  • • Debugging mojibake: compare the UTF-8 byte dump with what the other system stored. Text that reads "café" is UTF-8 bytes being interpreted as Latin-1.

Privacy & accuracy

Runs entirely in your browser. Encoding, decoding, normalization and inspection are plain JavaScript — no request is made, so nothing you paste is transmitted or logged. Open the DevTools Network tab and you will see zero requests while you type.
Normalization and grapheme segmentation come from your browser. String.prototype.normalize and Intl.Segmenter implement the Unicode algorithms themselves, so results follow whichever Unicode version your browser ships. Where Intl.Segmenter is missing, the grapheme count is approximated and the tool says so.
Byte dumps are encode-only. A UTF-8 or UTF-16 hex dump is indistinguishable from ordinary hexadecimal text, so the decoder deliberately does not try to guess at it. Escape sequences, character references, percent-encoding and U+ notation all decode automatically.

About Unicode Encoder / Decoder

The Unicode Encoder / Decoder converts between readable text and every escape form developers actually meet: JavaScript and JSON \uXXXX escapes (with correct surrogate pairs for emoji and other astral characters), the ES6 \u{1F600} form, Python and C escapes, U+XXXX code-point notation, named and numeric HTML character references, RFC 3986 percent-encoding, and raw UTF-8, UTF-16BE and UTF-32 byte dumps. Decoding auto-detects all of those forms in the same string — including surrogate pairs that must be rejoined — and anything that looks like an escape but does not parse is left exactly as written and listed underneath, rather than silently disappearing. Below the result, every code point is broken out with its general category, block, UTF-8 bytes, UTF-16 units and HTML entity.

Why use a Unicode Encoder / Decoder?

Most Unicode tools quietly get two things wrong. They emit \u{1F600} for emoji, which is valid ES6 but invalid JSON, so the output breaks the moment a JSON parser sees it — this tool emits surrogate pairs by default and tells you why. And they count "characters" with String.length, which is UTF-16 units, so a single emoji reads as 2 and a family emoji as 8. Here you get code points, UTF-16 units, grapheme clusters (via Intl.Segmenter) and UTF-8 bytes side by side, because those four numbers answer four different questions. The tool also normalizes (NFC/NFD/NFKC/NFKD) with a code-point diff, and flags zero-width spaces, stray byte-order marks, non-breaking spaces and bidirectional overrides — the characters behind most "these two identical strings are not equal" bug reports.

Who is it for?

Developers embedding non-ASCII strings in source code or JSON payloads, teams debugging mojibake and encoding mismatches across services, anyone investigating why two visually identical strings fail an equality check, localization engineers working across scripts, security reviewers looking for bidirectional-override characters in source or filenames, and technical writers who need the exact code point behind a character.

How to use the tool

1

Pick Encode (text to escapes) or Decode (escapes back to text) — conversion happens as you type, there is no Convert button

2

In Encode mode choose the output format: \uXXXX for JSON, \u{X} for modern JavaScript, \xNN/\uXXXX/\U0001XXXX for Python or C, U+XXXX notation, HTML entities, percent-encoding, or a UTF-8 / UTF-16BE / UTF-32 byte dump

3

Use the "Escape every character" toggle when you need fully ASCII-safe output; leave it off to escape only what the format requires

4

In Decode mode just paste — \uXXXX (including surrogate pairs), \u{X}, \U0001XXXX, \xNN, U+XXXX, &#NNN;, &#xHH;, named entities and %XX are all detected in the same string

5

Check the warning list under the result: any token that could not be decoded is reported with its offset and reason, and left untouched in the output

6

Read the four counters — code points, UTF-16 units, grapheme clusters and UTF-8 bytes — instead of guessing what "length" means for your case

7

Scan the invisible-character panel for zero-width spaces, byte-order marks, non-breaking spaces and bidi overrides, and strip them in one click if they do not belong

8

Compare the NFC, NFD, NFKC and NFKD forms, and use the per-character inspector for the general category, block, UTF-8 bytes and HTML entity of any single character

9

Press the swap button to send the result back into the input and flip the direction — handy for confirming a round-trip

Key Features

JSON-safe by default

Astral characters (above U+FFFF) are emitted as UTF-16 surrogate pairs: 😀 becomes 😀, which every JSON parser accepts. The \u{1F600} form is still available as a separate option, clearly labeled as ES6-only.

Eleven encode formats

\uXXXX (JSON), \u{X} (ES6), \xNN/\uXXXX/\U0001XXXX (Python, C), U+XXXX, named HTML entities, &#NNN;, &#xHH;, percent-encoding, plus UTF-8, UTF-16BE and UTF-32 hex dumps — with an escape-everything toggle for fully ASCII-safe output.

Auto-detecting decoder that never drops tokens

All escape forms are recognized in the same string using anchored patterns, surrogate pairs are rejoined, and percent-encoded runs are UTF-8 decoded. Malformed tokens such as \u12, &#xZZ; or a lone high surrogate are reported with their offset and reason, and preserved verbatim.

Per-character inspector

One row per code point with the glyph, U+XXXX, Unicode general category (Lu, Nd, Cf and the rest), block name, UTF-8 bytes, UTF-16 units, HTML entity and a copy button. Invisible characters are highlighted and shown with a placeholder glyph.

Four honest counters

Code points, UTF-16 units, grapheme clusters (Intl.Segmenter, with a documented fallback) and UTF-8 bytes. A ZWJ family emoji is 1 grapheme, 5 code points, 8 UTF-16 units and 18 UTF-8 bytes — all four numbers are real, and each answers a different question.

Normalization panel

NFC, NFD, NFKC and NFKD side by side with code-point counts, byte counts and a per-form delta against your input, so you can see exactly how many code points a form adds or removes.

Invisible-character detector

Finds zero-width spaces, ZWNJ/ZWJ, byte-order marks, soft hyphens, the non-breaking space family, left-to-right and right-to-left marks, embeddings, overrides and isolates, plus deprecated tag characters — with counts and a one-click strip.

Live conversion, no round trips

Everything recalculates as you type using a deferred value so the input never stutters, and nothing is sent anywhere — no request is made at any point.

Common Use Cases

An emoji breaks a JSON payload

Scenario: A string with an emoji was escaped as \u{1F600} and the receiving service rejects the body as malformed JSON.

✓ Encode in \uXXXX mode to get 😀 — the surrogate-pair form JSON actually defines — and paste it straight into the payload.

Two identical-looking strings will not compare equal

Scenario: A username, filename or CSV header matches visually but fails an equality check or a database lookup.

✓ Paste both. The invisible-character panel catches a zero-width space or a stray byte-order mark, and the normalization panel catches precomposed versus decomposed accents (macOS writes filenames in NFD, most other systems use NFC).

Debugging mojibake

Scenario: Text that should read "café" arrives as "café" or as a row of question marks.

✓ The UTF-8 byte dump shows exactly which bytes are on the wire, which makes it obvious whether UTF-8 is being read as Latin-1, double-encoded, or truncated mid-sequence.

Auditing source or filenames for bidi attacks

Scenario: You are reviewing code or a file listing and want to be sure no right-to-left override is reordering what you see.

✓ The detector flags U+202A-U+202E and U+2066-U+2069 with counts, so a "Trojan Source" style reordering cannot hide in a review.

Writing ASCII-only source

Scenario: A build pipeline, legacy editor or terminal mangles non-ASCII characters in source files.

✓ Turn on "escape every character" and copy the fully escaped string in your language's escape syntax — JavaScript, Python, C or HTML.

Accuracy

Normalization and grapheme segmentation come from the browser's own Unicode implementation

Privacy

100% client-side — no request is made while you type

Uptime

Static page — no backend dependency

Frequently Asked Questions

How do I convert text into \u escape sequences and back?

Paste text into Encode mode and pick a format. The default, \uXXXX, is the one JSON defines: characters up to U+FFFF become \u00E9-style escapes, and anything above becomes a surrogate pair such as 😀. Decoding needs no format choice at all — paste anything and the tool detects \uXXXX, \u{X}, \U0001XXXX, \xNN, U+XXXX, &#NNN;, &#xHH;, named entities and %XX sequences in the same string, rejoining surrogate pairs as it goes. Tokens that look like escapes but do not parse are listed with their offset instead of being quietly discarded.

What is a code point, and why does my emoji count as two characters?

A code point is the number Unicode assigns to a character, written U+0041 or U+1F600. JavaScript strings are sequences of UTF-16 code units, and any code point above U+FFFF needs two of them — a surrogate pair — so "😀".length is 2 while the code-point count is 1. A third number matters too: the grapheme cluster, which is what a person calls a character. The family emoji 👨‍👩‍👧 is 1 grapheme, 5 code points and 8 UTF-16 units. This tool shows all four measurements, including UTF-8 bytes, rather than picking one and calling it "characters".

Is my text sent to a server when I encode or decode?

No. Encoding, decoding, normalization, the character inspector and the invisible-character scan all run in your browser as plain JavaScript — no request is made at any point, so nothing you paste is transmitted or logged. Open DevTools, switch to the Network tab and type: you will see zero requests. That makes it safe for production data, customer names, API payloads and anything else you would not paste into a remote service. The page also keeps working offline once it has loaded.

\uXXXX, \u{1F600}, &#x1F600; or %F0%9F%98%80 — which should I use?

Match the consumer, not your preference. JSON only understands \uXXXX, so astral characters must be surrogate pairs; \u{1F600} in a JSON document is a parse error. \u{1F600} is fine in ES2015+ JavaScript source and in many regex engines. Numeric character references (&#x1F600;) belong in HTML and XML — note that XML predefines only five named entities (amp, lt, gt, quot, apos), so prefer numeric references there. Percent-encoding belongs in URLs and encodes UTF-8 bytes, not code points, which is why one emoji becomes four %XX pairs.

Why did \u{1F600} break my JSON, when it works in JavaScript?

Because they are different grammars. The ECMAScript string grammar gained \u{...} in ES2015, but JSON's grammar (RFC 8259) has never had it — JSON allows only \uXXXX with exactly four hex digits. The correct JSON encoding of an astral character is the UTF-16 surrogate pair: 😀 is 😀. Emitting \u{1F600} produces a payload that works in a browser console and fails in every strict JSON parser. This tool defaults to the surrogate-pair form for exactly that reason, and offers the ES6 form as a clearly separate option.

How do I find the invisible character that is breaking my string comparison?

Paste both strings and read the invisible-character panel. It flags zero-width space (U+200B), zero-width non-joiner and joiner (U+200C, U+200D), the byte-order mark (U+FEFF), soft hyphen (U+00AD), the non-breaking space family (U+00A0, U+202F, U+2007 and friends) and the bidirectional controls U+200E-U+200F, U+202A-U+202E and U+2066-U+2069, each with a count and a one-click strip. A leading U+FEFF surviving a file read is the classic cause of a CSV header or JSON key that looks right but never matches. Be careful stripping U+200D: it is what holds multi-part emoji together.

What is Unicode normalization, and which form should I store?

The same visible text can be different code-point sequences: é is either U+00E9 or U+0065 U+0301. NFC composes to the shortest form, NFD decomposes into base plus combining marks. NFKC and NFKD additionally fold compatibility characters — fi becomes fi, ① becomes 1 — which is lossy and irreversible. Store NFC and compare in NFC; that is what the W3C recommends for the web and what most databases assume. Use NFKC only for search keys and identifier matching, never as the stored value. This matters in practice because macOS writes filenames in NFD while Linux and Windows generally use NFC.

How many characters does Unicode actually have?

Unicode 15.0 defines 149,186 assigned characters and Unicode 16.0 (September 2024) raises that to 154,998 — figures that grow with roughly one release a year, so any hardcoded number dates quickly. The codespace itself is fixed: 17 planes of 65,536 code points each, 1,114,112 in total, of which 2,048 are permanently reserved as UTF-16 surrogates and 66 are noncharacters. So U+10FFFF is the highest valid code point and always will be. This tool rejects anything above it rather than emitting an invalid character — and if you only need to count characters in a document, the Character Counter is the quicker route.

Technical Specifications

Supported Formats

  • ✓Encode: \uXXXX with surrogate pairs (JSON), \u{X} (ES6), \xNN / \uXXXX / \U0001XXXX (Python, C, Java)
  • ✓Encode: U+XXXX notation, named HTML entities, &#NNN;, &#xHH;, percent-encoding (RFC 3986)
  • ✓Encode: UTF-8, UTF-16BE and UTF-32 hexadecimal dumps
  • ✓Decode: all of the above except byte dumps, auto-detected with anchored patterns and surrogate-pair joining
  • ✓Analysis: general category via Unicode property escapes, block name from a built-in table, UTF-8 bytes, UTF-16 units, HTML entity per code point
  • ✓Counters: code points, UTF-16 code units, grapheme clusters (Intl.Segmenter), UTF-8 bytes
  • ✓Normalization: NFC, NFD, NFKC, NFKD with code-point and byte deltas
  • ✓Invisible-character detection: zero-width, joiner, bidi control, non-breaking space family, BOM, soft hyphen, tag characters

Limits & Performance

  • •File Size: Text input only — no file upload
  • •Validations: Undecodable tokens are reported with offset and reason rather than dropped; code points above U+10FFFF are rejected
  • •Response Time: Live as you type, using a deferred value so the input never blocks; the inspector table shows the first 200 code points
  • •Browsers: All modern browsers; grapheme counting uses Intl.Segmenter where available and says so when it falls back

Pro Tips

  • Use \uXXXX with surrogate pairs for anything a JSON parser will read; keep \u{1F600} for JavaScript source you control.
  • When a string comparison fails on "identical" text, check normalization and invisible characters before you doubt your code — those two account for most of these bugs.
  • String.length in JavaScript, Python 2 and Java counts UTF-16 units, not characters. If a length limit matters (a database column, an SMS, a tweet), check which unit the limit is actually in.
  • A leading U+FEFF is a byte-order mark that survived a file read. Strip it before parsing JSON or reading a CSV header.
  • Percent-encoding encodes UTF-8 bytes, so one emoji becomes four %XX pairs. Use encodeURIComponent for a query-parameter value and encodeURI only for a whole URL — they escape different character sets.
  • In HTML, escaping only non-ASCII is pointless: & < > and " are the characters that actually change the parse, which is why this tool always escapes them in the entity formats.
  • Right-to-left override (U+202E) can make a filename render as "invoice-txt.exe" backwards from what it really is. Treat any bidi control in a filename or source file as suspicious.
  • Byte dumps are encode-only here on purpose: a UTF-8 hex dump is indistinguishable from ordinary hexadecimal text, so guessing at it would corrupt normal input.

Share This Tool

Found this tool helpful? Share it with others who might benefit from it!

💡 Help others discover useful tools! Sharing helps us keep these tools free and accessible to everyone.

Support This Project

Buy Me a Coffee