DEV Community

Cover image for How I Created These Generative Underline Pen Strokes
Yoav Kadosh
Yoav Kadosh

Posted on


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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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">
Enter fullscreen mode Exit fullscreen mode

See it live on CodePen:

Top comments (1)

artem profile image

Bro, is amazing!

Thanks for sharing here, I'm working on my language learning website and your component could be part of the brand identity.

I would be happy for any interest or attention from you to my project.

The most important JavaScript discussions happen on DEV

React vs Signals: 10 Years Later

React vs Signals: A Look Back

☝️ Ryan Carniato and Dan Abramov take a look back at React!