Create a pencil effect in SVG

My game Dragons Abound creates maps in SVG vector graphics format. Vector graphics have many features (for example, lossless zoom), which is convenient for maps. Also, vector graphics are good for creating clear lines, such as ink contours:


On the other hand, vector graphics are not very good at creating textures with non-repeating small details. In vector graphics, each rendered element is represented by a description of its size, shape, location, color, etc. In order to present many small non-repeating details, a great many elements need to be described. For example, for a pencil line


tens of thousands of different elements will be required. In fact, each gray spot in the image will be set separately. Other elements, such as blurry images, are even more problematic.

This is a rather serious limitation of vector graphics, so tricks have been added to SVG that allow you to more effectively reproduce some of these texture effects. I will explore some of these SVG features to create a pencil line effect. Of course, there are many more complex solutions to recreate pencil lines. Entire scientific articles have been written about this topic . But I just hope to create a fairly simple filter that provides an acceptable result.

As always, I would prefer to borrow or modify a filter created by someone more talented than me, but in this case there was an amazing shortage of vector pencil filters that can be used for inspiration. (An ominous warning music sounds: there may be reasons for this.) Therefore, in fact, I can only rely on myself.

Earlier, I wrote about the possibilities Dragons Aboundto create "handwritten" lines. Basically, then I considered the possibility of avoiding mathematically straight lines and creating lines with slight deviations that look closer to what could be drawn by a person’s hand. At the beginning of the article, examples of illustrations of mountains are shown. To create an ink illustration style, this is enough, but not suitable for creating a pencil line, because it lacks a pencil texture.

If you look at the pencil lines shown above and below:


you will notice many features that distinguish pencil strokes from ink lines (or lines drawn by a computer). The most important difference is that they have a texture resulting from the way the pencil interacts with the paper. The paper is grainy, and the pencil usually leaves graphite on the high parts of the grain, while the low parts remain white. On coarse paper, the texture is more noticeable; drawing cardboard is so fine that it almost does not create texture. Secondly, the edges of the pencil lines are rather vague. This is again largely due to the unevenness of the paper and the tip of the pencil; this leads to the fact that along the edge of the line remains a different amount of graphite.

(Of course, there are other effects. The pencil trace overlays itself, so it becomes darker at the intersections of the strokes. The strokes themselves vary in pressure and this can change the darkness of the stroke along its length. In this post, I will mainly focus on reconstructing the paper texture.)

For I began by preparing simple gray lines:


I drew “handwritten” lines with a thickness of 4, 2, and 1 pixel. Unfortunately, SVG effects usually look different for lines of different lengths, so I wanted to compare the effect on different sizes.

The main function provided by SVG to add texture effects is called filters . Filters are applied after rendering the vector effect and change its appearance. Conventional filters can perform actions such as changing the color of an object, adding noise to it, etc. Filters are a rather confusing topic with complex syntax, so I won’t give a full tutorial on how to use them, but I’ll explain in sufficient detail so that it is clear what I did. And at the end of the post I will give a link to Codepen with a filter so that you can experiment with it yourself.

Firstly, I’ll try to change the edges of the lines so that they are not smooth, but have irregularities characteristic of the grain of the paper. I will do this by moving the pixels of the line. The filter element performing this task is called “feDisplacementMap”; it moves each pixel based on the values ​​in another image. Since we want each pixel to move in a random but uniform way, we need to pass noise to feDisplacementMap to control the movement. Fortunately, SVG has another filter element called “feTurbulence” that is specifically designed to create noise. So we can combine two filters to roughen the edges of the lines.

The magnitude and coarseness of the line can be controlled by the parameters of the displacement map and noise generation. Unfortunately, the offset is indicated in absolute units, not relative to the line size. I selected the parameters, trying to find something that fits all the line widths, but with an increase in the offset scale, you can notice the problem:


Now the displacement is so great that it can move the whole line, and not just change its edges. This effect is even more noticeable on thin lines. In this example, a line with a width of 4 pixels basically looks as if the edge is rough, and distortions are already evident in a line with a thickness of 2 pixels. That is, I need to choose a value that does not create distortions in thin lines.

When zooming with a standard increase in the game, the effect looks like this (after selecting parameters to improve the effect):


At this scale, many rough edges become spots and dots. This is not a very unpleasant effect, and it is somewhat reminiscent of a pencil line. (However, in general, SVG filters seem to have problems with scaling - in many cases, when zoomed in, they look good, but they are subject to a poor resizing algorithm when moving away.)

Here's what the effect looks like when painting mountains (pencil effect on the left):


Not so terrible, but the line has sharp artifacts that look a little strange. And when the effect is applied to thin, closely spaced lines, they overlap and merge as a result:


I repeat, this is not so terrible, and the shadows on the pencil mountains as a result look very similar to those drawn by pencil, if you need just such an effect.

This solution adds roughness to the contours of the pencil stroke, but does not change the uniform color of the stroke. Texture is also present inside real pencil strokes, because graphite leaves spots on paper unevenly.

To add texture to the inside of the stroke, I use an SVG filter and then combine the noise with the stroke:


When zoomed in, you can see that the inside of each stroke is now filled with a pseudo-graphite texture. Here's what it looks like on a normal scale:


Pretty good, especially on thick lines. Here's what it looks like when drawing mountains:


On this scale, everything is not very good. Note that this filter also reduces the darkness of the lines; this is a natural result of adding white noise to the lines. To a certain extent, this problem can be smoothed out by increasing the contrast of noise, so that some parts of the compensation line become darker:


But since the colors of the line are already quite dark, this largely destroys the effect of "pencil". Therefore, if I use this, I need to select the parameters to create a pleasant balance.

Obviously, both solutions can be combined. With an increase, the result looks pretty good:


From the pencil line we expect both a rough edge and an internal texture. On a standard scale, everything is not so good:


due to sharp artifacts that appeared on this scale.

A good trick to improve this texture would be to add paper texture to the background:


Now the eye perceives the texture uniform throughout the image. Even at a very implicit level, this helps to deceive the eye and convince that the texture of the line is caused by the interaction with the paper.

Here is an example of using this filter on a map (with paper texture):


In general, everything is not bad, but with a detailed study it seems that everywhere just added noise. With a 200% increase, artifacts become even more obvious:


Another way to create a rough edge of a pencil line is to draw the line several times with slightly different deviations and reduced opacity. In the center of the line, where several versions of it intersect, the density will be close to the density of the original line; at the outer edges, where sometimes there is only part of the lines, the opacity will be lower, and the edges will be less legible.

In general, to implement such a solution using SVG filters, you need to use feTurbulence and feDisplacementMap together to create a distorted version of the line. However, to do this several times and combine all the lines at the end, you need a set of feBlend primitives. If, for example, we mix three copies, we need to appropriately reduce the opacity of the lines. (I’m not quite sure how to specifically calculate the corresponding opacity, but I think that it can be the cube root of the luminosity of the line.)

This creates the effect (three lines with an increase):


This approach has a couple of disadvantages. The filter has a fixed deviation, therefore it affects narrow lines more strongly, and at some points it can be seen that the single-pixel line is completely divided. Secondly, it is a rather complex filter that creates three separate deviations and combines them; it can be very slow on complex images like Dragons Abound cards .

Here's what it looks like at a standard scale:


In my opinion, this is not quite like a pencil outline, but it better eliminates the sharp artifacts of the previous solution.

This approach can be combined with the internal texture filter described above and add texture inside the pencil lines, as well as paper texture:


Here's what it looks like after applying to the mountains:


This filter tends to distribute the lines more strongly than the other filter, essentially making them thicker. Sometimes it generates a sketch effect with several lines, which is not so bad.

Here is an example of using this filter on a map:


It preserves the original darkness better than the first filter, and although in my opinion it does not exactly look like a pencil, the effect is quite pleasant. With an increase of 200%:


When enlarged, this filter does not acquire sharp artifacts of the first filter. The effect of intersecting lines on thin lines (for example, as in the image of a forest) begins to look quite artificial, but wider lines (mountains and rivers) still look good. However, within the black lines, such as rivers, the internal noise is almost completely lost.

I posted both of these filters on Codepen so you can try them yourself. The first filter is here , the second is here . I recommend experimenting with them and trying to improve them, and if you get something better than mine, then let me know! I would like to have a very good pencil filter!

All Articles