Effects of SVG filters. Part 2. Outline text with feMorphology

Published on February 10, 2019

Effects of SVG filters. Part 2. Outline text with feMorphology

  • Transfer

This series of articles " SVG filter effects " Sara Soueidan, freelance developer UI / UX interface, and the author of many technical papers, who lives in Lebanon, is devoted to the work of SVG filters and consists of the following articles:

Effects of filtering SVG

  1. SVG filters 101 .
  2. Effects of filtering SVG: outline text using feMorphology

In the second part of the series of SVG-filters Sara Soueidan introduces us to filter feMorphology and gives some examples of its use to create interesting effects.

In the first part of this series on the effects of SVG filters, we introduced the basics of SVG filters - how to create them and how to use them. We also looked at some of the most commonly used filtering operations, i.e. filter primitives. Since we will reuse this information, then I recommend stopping and reading that article before moving further on this.

<feMorphology> is one of my favorite SVG filter operations. It is also one of the simplest operations, and the results of its application to various elements are always predictable.

What is morphing?

Morphing means transforming or changing the shape or appearance of an object.

The morphological filter operates on the shape of the object. It provides two predefined form transformations: erosion (ie, thinning or reduction) and expansion (ie, A. thickening or expansion). In other words, the feMorphology primitive can be used to compress or expand an element.

Technically speaking, both of these operations work at the pixel level, extending the pixel to adjacent pixels ( dilate ), or destroying adjacent pixels at the edges of a pixel that is controlled ( erode ), while maintaining the contour on the border of that pixel. The amount by which a pixel expands, or the number of adjacent pixels used to stretch or expand a pixel, is determined by the radius parameter .

    in=".." result=".." 
    operator="dilate || erode" radius="">

The radius of the morph can be represented as the radius of a circle or ellipse. All adjacent pixels enclosed in a circle of this radius, starting from the input pixel, are considered to be a single community of pixels and will be used in the expansion or erosion effect.

However, in fact, the radius actually characterizes the size of the core, called the structuring element, which is more like a matrix. For now, it is sufficient to represent it in the form of a small rectangle whose width and height are given by the pixels specified in the radius attribute .

To use the filter, we do not need to go into the boring little things that morphing does at the pixel level. It is enough to know that you can provide one or two radius values ​​in feMorphology , which will determine the amount by which the element will contract or expand. If you specify two numbers in the radius attribute , then the first will correspond to the x-radius, and the second to the y-radius.

Morph Images

If the feMorphology operation is applied to images, then two, as a rule, predictable, results will follow:

  • The image size is reduced if the erode operator is used , or increases if the dilate operator is used .
  • With any operator, the image looks as if it was painted with a large paint brush, with a small amount of fine details.

Thus, to apply the morph effect to the image, our code should look like this:

<svg width="450" height="300" viewBox="0 0 450 300">
    <filter id="erode">
        <feMorphology operator="erode" radius="3"></feMorphology>
    <image xlink:href="..." width="90%" height="90%" x="10" y="10" filter="url(#erode)"></image>

In this fragment we blur (compress) the image (in pixels) by 3 pixels. The following figure shows the result of this code. Please note that the image size on the right is slightly smaller:

On the right, the result of applying the blur effect of the morph to the left
Pic_2. On the right, the result of applying the blur effect of the morph to the image on the left.

Now, if we keep the same morphing radius and change the operator from erode to dilate , the effect will be similar, but also distinctly different:

Fig_3. On the right, the result of applying the dilate morph operation to the image on the left.

In both cases, the image looks like an abstract drawn version of itself, and its overall size changes as the pixels expand or contract.

But in addition to these results, probably the first thing you notice is the difference in colors that results from each of these two effects: erode creates an image with a large number of dark pixels, while dilate brightens the result. This is due to the fact that:

  • erode (this value is used by default) sets for each pixel the lowest brightness or the highest opacity of its community, respectively for each of the R, G, B and A channels;
  • dilate sets for each channel of each pixel values ​​corresponding to the brightest or least transparent value of its community.

If technical details are discarded to the side, then applying feMorphology to images will almost always have the same result: compressing or stretching the fine details of the image as a copy of the image with a dark or light main outline.

However, applying feMorphology to monochrome elements, such as text, only compresses or stretches it, without making any noticeable changes in the color of the pixels, because we still have only one color to work with ...

Add color contour to text with feMorphology

Now we can add a contour to the text in SVG using the stroke attribute for this text .

<!-- Adding an outline to SVG text using strokes -->
<text font-size="80px" dx="100" dy="200" font-weight="700" stroke="deepPink" stroke-width="3px">Stroked Text</text>

Adding a contour, which is usually centered on the edges of the text so that half of its thickness is overlapped by the text itself, makes the text thinner, even if it was not intended. Instead, we can expand ( dilate ) the text to the thickness of the contour or stroke using feMorphology .

Unless otherwise indicated, the text usually has one color. So, applied to the text, feMorphology allows to reduce or increase the thickness of the text. Once the text is expanded with feMorphology , it can be used as input to other filter primitives to create the outline of the text, which is what we need.

Before we dive into how to do this, look at the picture showing the difference between the text with the outlined contour and the contour added with feMorphology .

Comparing the source text with the text having a stroke, and the contour created with ** feMorphology **
Fig_4. Compare the source text with the text that has a stroke and the outline created with the help of feMorphology .

Notice that the source text on the second line became thinner after adding a smooth contour, compared to the text of the third line, expanded with feMorphology .

So, let's step by step create a colored piece of text with an outline. Here is the result to which we will strive:

Final result
Pic_5. Final result.

So, we will create an SVG containing our text and a filter that triggers a simple expansion operation. The size of the text expansion depends on the thickness of the outline that we want to receive.

<svg width="900" height="200" viewBox="100 0 900 200">
    <filter id="outline">
        <feMorphology in="SourceAlpha" result="DILATED" operator="dilate" radius="4"></feMorphology>
    <!-- DILATED TEXT -->
    <text font-size="85px" dx="125" dy="130" font-weight="700" filter="url(#outline)">upgrade yourself</text>

The code above will get the alpha channel of the text, which is just a black version of the text, and increases the font thickness by 4px. The result of the code at this stage is as follows:

Text after thickening
Fig_6. Text after thickening.

... compared to source text that has a dark blue fill color:

Source text
Fig_7. Source text.

To create a contour effect, we will place the source text on top of the expanded text, leaving only the edges of the expanded text (our additional 4px) visible around the source text, making them look like a contour. Text overlaying its outline (extended text) will be performed using feMerge . How this is done we discussed in the previous article .

What else we need to do before positioning the outline around the text is to color the outline itself. Just as we did in the previous article, fill the filter area with the desired color, and then add the color layer with the already expanded text layer, i.e. our contour using the in operator . As a result, only those parts of the color fill that intersect with the expanded text will be displayed, thus coloring it. Then, we merge the resulting colored outline with the source text to get the desired result.

Now our code looks like this:

<svg width="900" height="200" viewBox="100 0 900 200">
<filter id="outline">
    <feMorphology in="SourceAlpha" result="DILATED" operator="dilate" radius="4"></feMorphology>
    <feFlood flood-color="#32DFEC" flood-opacity="1" result="PINK"></feFlood>
    <feComposite in="PINK" in2="DILATED" operator="in" result="OUTLINE"></feComposite>
        <feMergeNode in="OUTLINE" />
        <feMergeNode in="SourceGraphic" />
<text font-size="85px" dx="125" dy="130" font-weight="700" filter="url(#outline)">upgrade yourself</text>

The process of creating filtering effects in SVG consists in presenting the final result in the form of small sequential operations, using the result of one operation as input to another and, finally, merging all the layers created by them to achieve the final result.

And here is a demo of the code above:

The text fill color can be specified either in your CSS or in the text element using the fill attribute . The outline color can be configured in the flood-color attribute of the feFlood primitive .

Fine! And if you want to change the color of the contour? You will have to use the feFlood primitive again and combine the color fill with the outline. And every time you want to change the color of the outline, you have to do the same thing over and over again. This, admittedly, is too tiring. Fortunately, there is an easier way.

Text knocking out

In addition to adding a contour to the text, we can also create a contour, i.e. embossed text, which means that the inside of the text will be “cut out” so that you can see the background behind it through the outline. An example of such an effect may look like the text in the following GIF, which shows the changing color of the background, and how this background can be seen inside our text. Here is a demo that we will create in this section:

Embossed text example
Fig_8. An example of embossed text.

This effect is easier to create, and the code needed to execute it is noticeably shorter. The main difference here is that instead of layering the source text over the extended one, we will use the same source text to cut the inside of the extended text. This means that only the added thickness of the expanded text will remain, and the interior will be removed, thereby leaving us only the outline.

If instead of capturing and expanding the alpha channel of the text (which is black by default), you capture the source text itself (which can have any fill color!), Expand it, and then use the text again to cut the inside of the expanded text, you will get an outline which comes from the very source text. This means that the color of this contour will always match the color of the source text. And since we can define the color of the source text in CSS, this means that you have an outline of the text that is separate from its styles. (Hooray separation of worries!) Then you can apply this filter to any piece of text and change the color of this text in CSS at any time, without the need to adjust the filter code. Our improved code now looks like this:

<svg width="900" height="450" viewBox="0 0 900 450">
    <filter id="outliner">
        <!-- Start by grabbing the alpha channel of the text and dilating it-->
        <feMorphology operator="dilate" radius="8" in="SourceAlpha" result="THICKNESS" />
         <!-- Next, grab the original text (SourceGraphic) and use it to cut out the inside of the dilated text -->
        <feComposite operator="out" in="THICKNESS" in2="SourceGraphic"></feComposite>
    <text dx="100" dy="300" filter="url(#outliner)" letter-spacing="10px">SVG Rocks</text>

Using a good font, our demo now looks like this:

Figure_9. The result of our work.

In our style sheet we can select the outline color as well as the background color of the SVG. You can also select an image behind the text inside the SVG. In the code below, I use CSS animations for no reason at all to change the background color, except that it's beautiful.

svg text {
    font-family: 'Bangers', cursive;
    font-size: 150px;
    letter-spacing: 13px;
    fill: #000; /* This fill color determines the color of the outline */
svg {
    background-color: gold;
    animation: colorsssss 2s linear infinite;
    animation-delay: 3s;
@keyframes colorsssss {
    50% {
        background-color: deepPink;

Above SVG filter is reusable via SVG as well as HTML. If you want to apply it to an HTML element, you can do it with the filter property ; just put the filter in HTML and “call” it in CSS:

h2 {
    filter: url(#outliner);
    /* You can change the color of the outline here by changing the color of the heading */
    color: deepPink;

And our finished demo, which includes an HTML header with a filter applied to it:

My favorite thing about this filter recipe is that it can be used as a visual enhancement. If the browser does not support SVG filters, or it does not support CSS filters, or it does not support the use of SVG filters in HTML elements, the user will receive the source text without contours and a translucent effect applied to it. Oh, and the cherry on the cake on top? Both SVG and HTML text will be fully searchable and editable. Hooray progressive improvement! Long live the SVG!


Using just two filtering operations in SVG, you can apply the effect of the selected text to the SVG or HTML text content. Put this filter in HTML and use it whenever you need.

In the next article in this series, we will look at feComponentTransfer , one of my favorite filter primitives, and see how it works and what effects we can create with it. Stay with us.