Adworse Codes Here

Programmer's travelogue
by Dima Ermilov Tagged: tricksjs

JS Typewriter

When designing this blog, I was thinking about making it more typewriter-like. At the end of the day, I decided to use an exceptionally well made dynamic font (its story is a great story on its own). But my first idea was to emulate a typewriter by graceful destruction of this very JetBrains Mono typeface.

I thought of moving every letter on a small amount in vertical and horizontal directions and (counter)clockwise. A little blur would also help. These amounts should’ve been random for the entire text, but consistent for any specific letter. At first, I planned to generate a map of random parameters for every symbol present in the transformed test, but this just looked boring.

What didn't look boring, was to hash this symbol with some salt. I'm not a JS person at all, so I googled `simple js hash function` or something like that. After checking a couple of snippets, I found out these have a not uniform enough distribution (i.e., letters tend to lean to one side). It was time for Cormen. I bought it for a reason, right? Multiply-shift hash function worked perfectly. Click the button below to see the transformation!

And please remind me to tell the story of the Knuth-recommended magic constant 0x9e3779b9 if I forget to do so :)


Here’s the complete (smelly) code:

const __properHash = (str) => {
  res = (
    (Math.imul(
      str.split("").reduce((acc, symbol) => acc + symbol.charCodeAt(0), 0),
      0x9e3779b9,
    ) >>> 25) - 2**6
  );
  return res;
};

const __wrapWord = (word) => {
  wrapped = word
    .split("")
    .map(
      (char) =>
        `<span style="transform:rotate(${
          __properHash("til" + char) / 2**4
        }deg); filter:blur(${
          0.45 + (Math.abs(__properHash("blur" + char)) / 2**8)
        }px); translate: ${2 + __properHash("move" + char) / 2**6}% ${
          __properHash("jump" + char) / 2**4
        }%; margin-top: 0; display:inline-block">${char}</span>`,
    );
  return ['<span style="white-space:nowrap">', wrapped, "</span>"]
    .flat()
    .join("");
};


const __typewriter = () => {
  document.querySelectorAll("typewriter").forEach((node) => {
    node.innerHTML = node.innerHTML
      .split(/\s/)
      .map(__wrapWord)
      .flat()
      .join("&#x20;");
  });
};