Devising the Cloak of Invisibility in JavaScript

By  on  

Steganography. The art of hiding something right under your nose. For as long as humans have been alive, we’ve been trying to hide things — whether it’s our last slice of pizza or the location of a buried treasure. Do you remember the cool invisible lemon ink trick, where we’d write the secret message with lemon and heat the paper to reveal the grand secret? Well, that is what Steganography is, in a nutshell, allowing us to easily conceal messages just by hiding their existence.

In the early 1900s, German spies infiltrated the citizens of France in the name of trade. Their motives, however, were to observe the details of the French defense system and to aid the Germans to invade France. The cleverly designed French defense system maintained utmost confidentiality even amongst each region, making it difficult for one spy to gather all the information. This required multiple spies to gather information from each region and together design their game plan. The meeting place must be communicated only on that very day for high security but how was it supposed to reach all the regions within a day? They chose the newspaper, fast but a very insecure public channel! But the Germans already thought of that.They published the secret meeting spot in the weather report for the day.

Taking the first letter of every word and what do we get - the meeting place

“New Rue des Thermopyles Heil Hitler”!.

Back then Invisible ink, now Invisible characters.

Now in the age of information, the most important place for communication is the internet. Messaging apps like messenger, Whatsapp, iMessage, and Social media like Twitter, Facebook, Instagram. How can we apply the same problem here and solve it now? Well, we could start with zero-width ‌characters.

’Copy me‌‌‌‌‌‘- paste this quote in to see the ZWCs hiding right in front of you the whole time.

Zero Width Characters are non-printing characters, a part of the Unicode table. As the name suggests they don’t even show their presence. They are used to enable line wrapping in long words, joining emojis, combining two characters into a ligature, keeping them from joining, etc.

The characters zwj join the emoji’s but they are not visible

These characters have increasingly found their way in text-hiding, their complete invisibility being a remarkable selling point. They cannot be blocked as they are integral in multiple languages and emojis. And it also turns out that ZWCs aren’t the only characters which are invisible, eg. Invisible separator — U+2063.

Table that contains mostly used invisible characters.

One small problem with this table tho! Gmail blocks U+200B ( Zero width space ). Not to mention, Twitter is known for blacklisting unnecessary invisible characters, none of the characters in the table except U+200C, U+200D and U+180e works. So we now have three characters!

Oh, wait, U+180e is not invisible and renders weirdly on iOS devices. We are now down to only 2 characters.

Tearing apart the Unicode table, testing each possible Invisible character for its cross-platform / web invisibility, we are able to add 4 more characters to our arsenal, a total of 6 invisible characters that we can now use to hide our secrets in strings.

const zwc = ['‌', '‍', '⁠', '⁢', '⁣', '⁤'] // 200c,200d,2060,2062,2063,2064 The digital equivalent invisible ink

Now to use the invisible ink, all we have to do is given a secret ‘hi’ we need to represent it in binary, i.e., 01101000 011010001. Then take 4ZWCs and map them to a two-bit truth table i.e, 00-200c,01-200d,10-2060,2062-11.Use the truth table to convert the binary to an invisible stream and embed it in any cover message anywhere.

Encrypting the hidden content

What if the intruder somehow detects the presence of the hidden characters and tries to brute force the truth table to crack the secret out.

This is where Kerckhoff’s principle comes in:

An ideal cryptosystem should be secure even if everything about the system is exposed to the public except the secret key.

Therefore we need some sort of a key to lock our secrets. For this a password-based key generation function can be used to yield a strong key which in turn is used to encrypt our messages. AES-CTR stream cipher with a random IV and salt can be used to encrypt the hidden secret as it has an added advantage of not requiring padding unlike block ciphers as it increases the length of the message.


Now given we're doing so much with encryption, we definitely need the maximum compression possible.

As you can see, even though we had six ZWC characters only 4 were used as 6 is not a power of 2.The two extra characters (U+2063, U+2064) could be used to do an additional layer of abstracted Huffman compression reducing redundancy. After the secret has been converted to ZWCs, the two most repeating ZWCs in the stream are determined, say U+200D and U+2060. Now every two consecutive occurrences of U+200Ds and U+2060s could be replaced with one U+2063 or U+2064. This saves a lot as redundancy was frequently observed.

We now have two layers of compression making the best use of the 6 invisible characters! Awesome!

So combining all of these, two of my friends and I built StegCloak, a pure JavaScript steganography module designed in a functional programming style to achieve what is said above.

Here’s a quick demo of it :

We hope you enjoy it as much as we did building it!

Checkout StegCloak in Github or visit

Mohan Pierce

About Mohan Pierce

I love to build mad science stuff for myself and others. JavaScript nerd and a Student.

Recent Features

  • By
    Animated 3D Flipping Menu with CSS

    CSS animations aren't just for basic fades or sliding elements anymore -- CSS animations are capable of much more.  I've showed you how you can create an exploding logo (applied with JavaScript, but all animation is CSS), an animated Photo Stack, a sweet...

  • By
    CSS Animations Between Media Queries

    CSS animations are right up there with sliced bread. CSS animations are efficient because they can be hardware accelerated, they require no JavaScript overhead, and they are composed of very little CSS code. Quite often we add CSS transforms to elements via CSS during...

Incredible Demos

  • By
    Record Text Selections Using MooTools or jQuery AJAX

    One technique I'm seeing more and more these days (, for example) is AJAX recording of selected text. It makes sense -- if you detect users selecting the terms over and over again, you can probably assume your visitors are searching that term on Google...

  • By
    HTML5 Input Types Alternative

    As you may know, HTML5 has introduced several new input types: number, date, color, range, etc. The question is: should you start using these controls or not? As much as I want to say "Yes", I think they are not yet ready for any real life...


  1. JDogg

    Hey ⁡‍⁡‌⁣‌⁣⁠⁡‌⁡⁠‌⁠‍‌⁤‌⁡‌‍⁡⁢⁡⁠⁢⁠‍⁠‍⁣‌⁡‍⁡⁣⁡‍‌⁡⁢‌‌‍‌‍‌‍⁣‍⁤⁠⁢‌⁣⁡⁣‍⁡‌‍⁤‌⁡⁢‌‍⁠‌‍⁠‍⁤‍‌⁡‌‍DW, check it out.


  2. Very interesting article. I wasn’t aware that Google and Twitter are blocking non-visible characters. Makes sense – but I never thought about that it could an issue.

  3. A very interesting module Mohan. I look forward to using it. Thanks a lot for sharing!

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!