Animating text as Handrawn with CSS

Recently, I wanted to animate text to reveal like it was drawn by hand. This makes the header text of certain pages, like celebration and wedding websites, stand out much more and look sophisticated. This is what this short blog post is about.

First, I thought about a simple wipe animation. It looks fine, just fine:

This is not much work, as you can see in the CSS, and it works with all fonts. What we essentially do is to animate the area shown of the text (kind of the clip(path)):

@keyframes wipe-reveal {
  from { clip-path: inset(-50% 100% -50% 0); } /* right=100%: fully hidden */
  to   { clip-path: inset(-50%   0% -50% 0); } /* right=  0%: fully revealed */
}

This starts with a clip-path that doesn’t cover anything and ends with one that reveals everything, creating a wiping animation.

Assume now we would create a hand-drawn path over the text that barely covers the letters, using a tool like Inkscape. We can also animate it:

As you can see in the CSS, it’s slightly easier because we can animate it directly. I split the CSS path into two parts because capital letters are usually animated more slowly and have thicker lines than lowercase letters. Play around with the different animation values in the interactive editor yourself to explore the animation.

You can observe that our hand-drawn animation essentially encodes the path our pen would take while drawing, including the parts where we move it over the paper without drawing. This is because we can now combine the first and the second demo and use the hand-drawn path as the clip-path for the text:

The CSS looks close to the second demo, because we encode the clip-path property directly in the SVG. What works best is to also have the text as a path, since you usually want to tweak it anyway, and use the big font only for a few letters, which also saves some memory.

This is how you can easily create hand-drawn animations in your browser. I thought I would share this technique. See you next week for something JDK related.