“Decrypting” effect

svmi3195
Chingu
Published in
4 min readNov 6, 2017

Live example at codepen

screenshot

I’ve seen such effect in various video-games and just recently encountered it in Batman: Arkham Origins (nice game, btw, but two previous are even cooler). When you open game menu, all words are “encrypted” and look like some abracadabra, but in few seconds letters are changing to form a real word. Kinda like visualizing bruteforce. It gives some funny “hacker” feeling to the menu. And it looks like something easy and fun to implement!

What I needed first was some text to apply this effect. I could’ve made a menu as well, but I thought that larger text will illustrate the idea better. I also wanted some text which would be logical to change, so the effect could be applied again and again. And somehow I thought about Shakespeare’s sonnets: they’re more than few words, but not too big, the user clicking to show another sonnet makes sense.

And this decision lead me to some frustration, because I couldn’t make fetch work with http://poetrydb.org API no matter what I did, and I didn’t find other usable API’s with those sonnets. Problems with getting those texts were so annoying and it wasn’t even the point of my experiment! So I ended up by just copying those sonnets into an array in my file.

Now, to view a sonnet I just needed to write some very basic code:

var sonnet = getSonnet(number).lines.join('<br>');
document.getElementById("sonnet").innerHTML = sonnet;

Here number is any number from 0 up to my sonnets array length, can be picked from user input or randomly (Shakespeare has 154 sonnets). Since each line in a sonnet is presented as separate element of “lines” array, I join them into one text with <br> to make each line start from new line.

So, now I can view those sonnets. Time to encrypt them! I did this in a very straightforward way. Basically, since we don’t need to really encrypt the text and we only want visual effect, all we need to do is take every letter and change it to another random letter (or number).

//original is an array of sonnet linesfunction encrypt(original){
var placeholders = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n', 'm'];

original = original.join('%');
original = original.split('');

for(var i = 0; i < original.length; i++){
if(original[i] == '%'){
original[i] = '<br>'
}else if (placeholders.includes(original[i])){
original[i] = placeholders[Math.floor(Math.random() * placeholders.length)];
}
}
return original.join('');
};

As you know, in JS strings are immutable. So to make it more simple to replace letters I decided to split a sonnet into one-dimension array with all its characters. But I still needed to keep my <br> for formatting! So I did this in a bit unsightly way: firstly I joined original lines with %, split that thing into an array and then while changing each element I replaced % with <br>. Another important thing was to check if an element we are changing is in our “placeholders”. This check ensures that all whitespaces, punctuation and whatever weird signs may occur in sonnets — they will not be replaced.

You probably noticed that I don’t have capital letters in placeholders array — this was done for the speed. Still sonnet is relatively long text, so I decided to keep capitals in their places for the visualization to be faster.

Ok, done with the “encrypting”, now what’s with the “decrypting”? This can be made in a straightforward way as well: we have original and encrypted sonnet arrays, each iteration we check if encrypted element is same as original, if no — we replace it with any random letter from placeholders array. We repeat this until all elements in encrypted are same as in original.

function decrypt(original, encrypted){
var placeholders = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'a', 's',
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n', 'm'];

original = original.join('<br>');
original = original.split('');
encrypted = encrypted.split('');

for(var i = 0; i < original.length; i++){
if(encrypted[i] != original[i]){
encrypted[i] = placeholders[Math.floor(Math.random() * placeholders.length)];
}
}
return encrypted.join('');
};

Now, we just need to repeat our “decrypting” until it’s all done. To visualize it we may want to handle how many iterations per second we’d like to have. Here we can use setInterval.

function visualize(original, encrypted){
var timer = setInterval(
function(){
encrypted = decrypt(original, encrypted);
document.getElementById("sonnet").innerHTML = encrypted;

if(encrypted == original.join('<br>')){
clearInterval(timer);
}
}, 100);
};

This makes code inside setInterval run each 100 milliseconds. clearInterval is important for making the function stop when “decrypting” is done.

--

--