How I Created These Generative Underline Pen Strokes

(Scroll down to see a live demo, or see it on CodePen)

I created a small React component that adds a randomly generated pen stroke underline to a given word.

The strokes are rendered using an SVG <path/> element. They vary in thickness and shape, and they take the width of the text that they underline.

Rendering The Strokes

I used an SVG element with a single <path/> to render the strokes. The <path/> has a d attribute specifying a series of commands and coordinates, defining the shape of the path. The path element is the most powerful SVG element, and I use it often when creating SVG graphics. You can learn more about it in this great MDN tutorial.

I'm using the Q command, which is a simple command for generating curves. I'm randomly generating a series of 2-4 curves alternating from left to right, each of which is angled a bit differently, and placed a bit lower that the previous one.

Here's the code:

// Draw the lines
while (line++ < lines) {
  const y = line * (height / lines); // Draw every line lower than the previous one
  d += ` Q ${random(30, 70)}` + // The x coordinate of the curve's center
       ` ${random(y - 5, y + 5)}` + // The y coordinate of the curve's center
       ` ${line % 2 === 0 ? random(-5, 15) : random(85, 105)}` + // The x coordinate of the curve's end, alternating between right to left based on the current line number
       ` ${random(y - 2, y + 2)}`; // The y coordinate of the curve's end
}

Maintaining Stroke Width Consistency

SVG elements can scale up/down, but they maintain the ratio given by their viewBox attribute. That includes the stroke width.

For example, if our viewBox is defined to be a 100x100 square (i.e. viewBox="0 0 100 100"), and we set the width and height of the element to be 200x200, everything inside the SVG will scale by a factor of 2. A stroke-width of 10px will be rendered as 20px.

Since the SVG element takes the width of the given word, the stroke width can scale up or down based on the word's length. To avoid that, i'm using the following CSS rule:

vector-effect: non-scaling-stroke;

Maintaining Stroke Height Consistency

The issue described above can also affect the coordinates of the <path/> itself, not just the width of the stroke.

In my case, I wanted the SVG's height to remain consistent, and let the width change based on the word's length instead of maintaining the ratio given in viewBox.

To do that, I used the following attribute:

<svg preserveAspectRatio="none">

See it live on CodePen:

20