Unicode Steganography Hider
Hide secret messages in plain text using 4 Unicode methods: Zero-Width Binary, ZW-V2, Homoglyph, or Variation Selectors. Optional AES-256-GCM encryption. Processed in your browser, not uploaded.
Enter Your Cover Text
The visible text that will contain your hidden message
Try the demo instantly
No upload needed — loads a sample file
Processed in your browser
Your text is processed in this browser tab and not sent to a server. Usage statistics may record which options were used, never your text.
How to Hide a Message in Plain Text (5 steps)
- Enter your cover text — the visible message your secret will hide inside.
- Type your secret message in the payload field.
- Choose a method: Zero-Width Binary (simple), ZW-V2 (more compact), Homoglyph (changes letters instead of adding invisible characters), or Variation Selector (needs emoji in the cover text).
- Add a password to encrypt the payload (optional but recommended).
- Click 'Hide Message' — copy the output and send it normally.
Unicode Steganography Methods Comparison
| Method | Technique | Capacity | Best For |
|---|---|---|---|
| Zero-Width Binary | ZWSP (U+200B) = 0, ZWNJ (U+200C) = 1 | ~1 byte per 8 visible characters | The simplest method; works on any cover text |
| Zero-Width V2 | 8 ZW chars, 3 bits per symbol | ~3 bytes per 8 visible characters | Shorter output for the same message |
| Homoglyph | Latin → Cyrillic lookalike substitution | ~1 bit per substitutable char | Visible letters change; no invisible characters (copy-paste and per-service survival not tested) |
| Variation Selector | U+FE00–U+FE0F after emoji | ~0.5 byte per emoji (a sequence such as a family counts once) | Covers that already contain many emoji |
What This Tool Hides, and Where
The carrier is plain text that you paste or load from a .txt file, in any language. The hidden payload is typed text or a file of up to about 1 MB after encryption and error correction. The output is text again, to be copied, not a new file format.
Four methods, each a different place to put bits. Zero-Width Binary inserts U+200B (bit 0) or U+200C (bit 1) between visible characters. Zero-Width V2 uses eight different invisible characters, three bits per character. Homoglyph swaps Latin letters for Cyrillic look-alikes, one bit per letter. Variation Selector adds one of the sixteen selectors U+FE00 to U+FE0F after each emoji, four bits per emoji.
All four write the same wire format: a 32-bit header (the top bit says whether the data is encrypted, the other 31 bits are the length in bytes) followed by the data. Variation Selector writes it as 4-bit values, the others as bits.
Choosing a Method
Zero-Width Binary and Zero-Width V2 work on any text. V2 needs about a third as many characters for the same message. Homoglyph needs no invisible characters but changes letters, and it is limited by how many Latin letters from the table below the cover has. Variation Selector needs emoji in the cover.
The look-alike letters are real Cyrillic code points, so a spell checker, a search for a word you hid, or a check that compares scripts will notice them. The invisible characters are found by any tool that lists code points. Nothing here is hidden from someone who looks for it.
For zero-width characters in text with platform-specific character sets, see the Invisible Text Hider. For spaces, tabs and line endings instead of invisible characters, see the Whitespace Steganography Hider; for hiding in HTML source, the HTML Steganography Hider.
Password, Error Correction and Size
A password encrypts the data with AES-256-GCM before it is written; the key comes from PBKDF2 with 100,000 iterations. Encryption adds 48 bytes. It hides the content, not the fact that invisible characters, look-alike letters or selectors are there.
Reed-Solomon (available in the most detailed mode) adds a 16-byte header plus 10, 20 or 30 parity bytes for every 223 data bytes (rs-10, rs-20, rs-30) and repairs up to 5, 10 or 15 changed bytes per block. It repairs changed characters, for example a bit flipped to its opposite. It cannot restore characters that were deleted, and the 32-bit header is not covered by it.
There is no compression step. The Extractor accepts at most 1,000,000 stored bytes, and the Hider refuses a message that would exceed that.
What Is Changed in Your Cover Text
Characters are inserted between whole visible characters: an emoji sequence such as a family, a flag, a letter with its accent marks, or a Windows line ending is never split. Zero-Width V2 never inserts before the first character, because U+FEFF is also the byte-order mark and a file that starts with it can lose it when saved.
If the cover already contains characters the method reads as data, the tool removes them first and tells you how many: U+200B and U+200C for Zero-Width Binary; all eight V2 characters, including U+200D, for V2 (U+200D can break emoji ZWJ sequences and may affect scripts or text that use the joiner; the tool reports how many characters it removes); and variation selectors for Variation Selector, which can change how a heart or check mark displays. Homoglyph refuses a cover that already contains Cyrillic look-alikes.
Capacity by Method for Two Covers
Cover A is the sentence "The quick brown fox jumps over the lazy dog near a calm river today. " (69 characters) repeated 20 times: 1,380 characters, 520 of them Latin letters that have a look-alike, no emoji. Cover B is cover A followed by 100 copies of "😀 ": 1,580 visible characters and 100 emoji. The tool counts the slots S. The largest message without a password is floor(S × bits per slot ÷ 8) − 4, where the 4 bytes are the 32-bit header. Zero-Width Binary has S = 1,380 on cover A (floor(1,380 ÷ 8) − 4 = 168); Zero-Width V2 uses 3 bits per slot and never the first position, so S = 1,379 (floor(1,379 × 3 ÷ 8) − 4 = 513). Homoglyph counts only the Latin letters in the look-alike table, not all characters, and Variation Selector counts emoji (floor(100 × 4 ÷ 8) − 4 = 46). A password uses another 48 bytes of the stored data, so the figures in brackets are 48 lower.
| Method | Slot | Cover A largest (with password) | Cover B largest (with password) |
|---|---|---|---|
| Zero-Width Binary | 1 bit per visible character | 168 B (120 B) | 193 B (145 B) |
| Zero-Width V2 | 3 bits per visible character, never before the first | 513 B (465 B) | 588 B (540 B) |
| Homoglyph | 1 bit per Latin letter in the table | 61 B (13 B) | 61 B (13 B) |
| Variation Selector | 4 bits per emoji | none (no emoji) | 46 B (none: a password needs 48 B for encryption alone) |
Payload Storage Before the 32-Bit Header
Bytes of stored data for a 100-byte message, before the 32-bit header. Add another 4 bytes for the shared length and encryption-flag header to get the total the method must carry:
| Settings | Bytes stored |
|---|---|
| No password, no Reed-Solomon | 100 |
| Password | 148 |
| Reed-Solomon rs-10 | 126 |
| Reed-Solomon rs-20 | 136 |
| Reed-Solomon rs-30 | 146 |
| Password and rs-20 | 184 |
The Eight Zero-Width V2 Symbols
| Value (3 bits) | Character | Code point |
|---|---|---|
| 000 | Zero Width Space | U+200B |
| 001 | Zero Width Non-Joiner | U+200C |
| 010 | Zero Width Joiner | U+200D |
| 011 | Word Joiner | U+2060 |
| 100 | Invisible Times | U+2062 |
| 101 | Invisible Separator | U+2063 |
| 110 | Invisible Plus | U+2064 |
| 111 | Zero Width No-Break Space | U+FEFF |
The Look-Alike Letters (Homoglyph)
A Latin letter stays as it is for a 0 bit and is replaced by the Cyrillic letter for a 1 bit. Only these 23 letters carry bits.
| Latin letters | Cyrillic replacements |
|---|---|
| a e o p c x y | U+0430 U+0435 U+043E U+0440 U+0441 U+0445 U+0443 |
| A B C E H K M O P T X | U+0410 U+0412 U+0421 U+0415 U+041D U+041A U+041C U+041E U+0420 U+0422 U+0425 |
| i I S j s | U+0456 U+0406 U+0405 U+0458 U+0455 |
Measured: Normalisation
The result of Unicode normalisation on a message hidden in cover A (Node.js, one message of 2 bytes per method). Nothing else was measured: individual websites, apps, fonts and sanitisers were not tested.
| Method | NFC | NFKC |
|---|---|---|
| Zero-Width Binary | Readable | Readable |
| Zero-Width V2 | Readable | Readable |
| Homoglyph | Readable | Readable |
| Variation Selector (cover B) | Readable | Readable |
Examples
Input: Hide "Hi" (2 bytes) in cover A with Zero-Width Binary.
Result: 48 invisible characters are inserted: the 32-bit header (it holds the length 2) and 16 bits for H and i. The text grows by 48 characters and 144 bytes in UTF-8.
Meaning: Each hidden bit is one inserted character of 3 bytes. The positions are chosen with a seed, and the Extractor needs no seed because it reads every U+200B and U+200C in order.
Input: The same message with Zero-Width V2.
Result: 16 characters are inserted, 48 bytes in UTF-8: 48 bits in groups of 3. Four of the 16 are outside U+200B and U+200C.
Meaning: V2 packs three bits per character, which is why it holds about three times as much per character.
Input: The same message with Homoglyph.
Result: The text keeps its length. 7 of the first 48 Latin letters in the table are replaced by Cyrillic ones, one for each 1 bit.
Meaning: The 32-bit header has one 1 bit (the length 2), H has two and i has four. Letters for 0 bits are untouched.
Input: The same message with Variation Selector in cover B.
Result: 12 emoji get a selector: U+FE00 seven times and U+FE02 for the header, then U+FE04 U+FE08 U+FE06 U+FE09 for H (0x48) and i (0x69).
Meaning: Each nibble of the header and the data is the selector's number from FE00. The text length grows by 12 characters.
Input: Text with 60 red hearts (the heart plus its emoji-style selector).
Result: The scan counts 60 variation selectors and calls Variation Selector the likely method, but nothing decodes.
Meaning: Selectors that were already in the text look like a message to the scan. The Extractor needs a valid header after them to report one.
Input: A cover that already has Cyrillic letters, hidden with Homoglyph.
Result: The Hider stops and says the text already contains Cyrillic look-alikes.
Meaning: The Extractor would read each of them as a 1 bit, so the tool does not write a message it cannot read back.
How the Unicode Hider Works
1. Prepare the cover
Characters the method would read as data are removed and counted. Homoglyph refuses a cover with Cyrillic look-alikes. The slots are counted per visible character, per Latin letter in the table, or per emoji.
2. Build the data
A password encrypts the message (AES-256-GCM); a Reed-Solomon level adds parity. A 32-bit header with the encrypted flag and the length is put in front.
3. Write the slots
Zero-Width Binary and V2 insert characters at positions picked by a seeded random generator (the seed is saved in the settings link and is not needed to read). Homoglyph and Variation Selector use the first slots in reading order.
4. Read it back
The finished text is read with the Extractor's own code and compared with what was meant to be hidden. If it differs, no output is produced.
5. Copy the text
Copy the result exactly. A paste-back check lets you paste the text as it arrived from the place you sent it to and reports how many of the characters survived.
What the results mean
| Result | What it means | What to do |
|---|---|---|
| Method, Hidden, Encrypted | The method, the number of bytes you hid, and AES-256-GCM when you set a password. | None for Encrypted means anyone with a decoder can read it. |
| Chars inserted | Invisible characters or selectors added; for Homoglyph, the letters that were replaced. | It is the number of slots used: 48 for a 2-byte message with Zero-Width Binary. |
| Notes on the cover | How many characters were removed from your cover text before writing (zero-width characters, ZWJ, variation selectors). | Compare the output with your original if the text had emoji sequences. |
| Platform Compatibility | Every service is listed as not tested. This site has no per-service result. | Use the paste-back check on the place you will send the text. |
| Verify the output | Decodes the text just produced and compares the bytes with your message. With a password, it can confirm the data is present without decrypting it. | A failure here means the Hider's own output cannot be read: report it. |
| Paste-back check | Reads text you pasted back from the destination and reports how many characters are intact. | Some loss means the destination removed or changed some of the characters; try another method. |
| Settings link and config file | A link that pre-fills method and seed, and a JSON file of the settings. | The seed controls character placement only. It is not an encryption key and is not needed to read the message. |
| Watermark batch | One copy per recipient (up to 200) with a different ID, plus a separate key file. | A copy that could not be made is not in the ZIP and is listed in the key file. |
Limitations
- Nothing here is undetectable. Invisible characters appear in any code point listing, look-alike letters appear in any check that compares scripts, and selectors appear in any listing of U+FE00 to U+FE0F. No detection tool was tested.
- Survival through websites, messengers, email, word processors and sanitisers was not tested. By design, anything that removes invisible characters, normalises letters or rewrites emoji destroys what it touches.
- In our test, NFC and NFKC normalisation left all four methods readable (see the measured table). Other transformations were not tested.
- Variation Selector writes U+FE00 to U+FE0F. U+FE00 to U+FE0D are the other selectors, U+FE0E asks for text presentation and U+FE0F for emoji presentation, so 2 of the 16 values (on average 1 nibble in 8) change how an emoji may be drawn, and not every renderer respects selectors. We did not test renderers.
- Reed-Solomon repairs changed characters, not deleted ones, and does not cover the 32-bit header.
- Zero-Width V2 removes U+200D from your cover before embedding. This can break emoji ZWJ sequences and may affect scripts or text that use the joiner. The tool reports how many characters it removes. Variation Selector replaces selectors in your cover.
- The tool stores at most 1,000,000 bytes after encryption and error correction. A cover of a few megabytes is processed in a few seconds.
Privacy
Your text is processed in your browser tab; the tool's own code does not upload it. Usage statistics may record which options were used and coarse facts such as sizes, never your text or message. The Platform Compatibility list is a reminder that no service was tested, not a test.
Which Tool Next?
- Unicode Steganography Extractor — read a message back from text this tool made.
- Invisible Text Hider — zero-width characters only, with a character set chosen per platform.
- HTML Steganography Hider — hide data in the source of an HTML file.
- Whitespace Steganography Hider — spaces, tabs and line endings in text files.
Frequently Asked Questions
What is Unicode steganography?
This tool hides data inside ordinary-looking text with invisible Unicode characters (zero-width characters, variation selectors after emoji) or look-alike letter substitutions (Latin letters swapped for Cyrillic ones). The text reads the same to a person, but its code points differ, so it is not identical to a computer.
Which method should I use?
Zero-Width Binary is the simplest choice and works on any cover text. If an app removes invisible characters, Homoglyph is worth trying because it uses letters instead of invisible characters. We have not tested Twitter/X, Facebook, SMS or any other service, so use the check on the result page: paste the text back from where you sent it and see whether it still decodes.
Can Twitter strip my hidden message?
Possibly — some apps remove zero-width characters, but we have not tested Twitter/X or Facebook specifically. Homoglyph does not use zero-width characters, so a stripper aimed at them would not affect it, but spam and anti-phishing filters that look for Cyrillic look-alikes could. Test with the check on the result page.
Is this real encryption or obfuscation?
The AES-256-GCM option is real encryption (key from your password with PBKDF2, 100,000 iterations). Without a password, anyone who runs a decoder can read the hidden message. With a password the content cannot be read without it, but the characters that carry it can still be seen by a scanner. Encryption protects the hidden content, not the existence of the embedded data. The key derivation uses a fixed 100,000 PBKDF2 iterations, so an attacker who has the text can guess passwords offline; use a long, unique passphrase.
How is this detected?
Zero-width methods show up in any Unicode scanner or hex view. Homoglyph needs a code-point comparison (mixed Latin and Cyrillic letters in one word is a well-known check), so it is not undetectable. We did not test detection tools. Encryption hides the content, not the fact that something is hidden.
Related Tools
Unicode Steganography Extractor
Extract hidden messages from text containing invisible Unicode characters or homoglyph substitutions. Auto-detect mode scans all methods.
Steganography Capacity Calculator
Calculate exact hidden-payload capacity for PNG, BMP, WebP, WAV, FLAC, AIFF and more — plus PSNR and detection risk, all client-side. Links to the right specialist hider/extractor for your file type.