SVG Filter Effects: Moving Forward

Over the course of six weeks, we published six articles that covered a variety of SVG Filter effects and the filter primitives used to create them:

  1. SVG Filters 101— in which we covered the basics for getting started with SVG filters.
  2. In the second article, Outline Text with <feMorphology>, we learned about feMorphology and how it can be used to shrink and expand content. We saw how we can use it co create text outlines and paint-like image effects.
  3. The third article, Poster Image Effect with <feComponentTransfer> was our first step into the world of feComponentTransfer. We learned how we can use it to create poster image effects.
  4. In the fourth article, Duotone Images with <feComponentTransfer> we dug further into feComponentTransfer. We learned how to use it to recreate Photoshop’s duotone effects by creating gradient maps and applying them to images.
  5. In the fifth article, Conforming Text to Surface Texture with <feDisplacementMap> we recreated another Photoshop-grade effect and learned how to conform text to surface texture. We saw how the steps to recreate Photoshop effects are very similar in SVG.
  6. And in the sixth article, Creating Texture with <feTurbulence> we learned how to generate our own textures using SVG’s feTurbulence primitive. We learned how to use it to distort HTML and SVG content, as well as simulate natural texture by combining it with SVG’s lighting effects. All pretty powerful stuff.

Even though we covered a lot of areas, I can confidently say that we barely scratched the surface of what’s possible with SVG Filters. In this short follow-up article, I want to share some of my favorite resources to learn more about SVG Filters.

Moving Forward: SVG Filter Learning Resources and Experiments

  • To get an even broader idea of the incredible possibilities that SVG Filters bring to the Web, I highly recommend checking out Lucas Bebber’s Codepen profile. Lucas is the guy who created the famous Gooey Effect using SVG Filters. His experiments include even more impressive effects that are guaranteed to inspire you and get you excited about the possibilities that SVG brings to the Web.

    See the Pen Gooey Menu by Lucas Bebber (@lbebber) on CodePen.light



    See the Pen CSS Text filling with water by Lucas Bebber (@lbebber) on CodePen.light

  • Michael Mullany was my go-to resource for learning when I took my first dive into the world of SVG filters. I learned a lot from his writing, his contributions to the Web Platform Docs’ SVG Filters entries, and his Codepen experiments which are literally an SVG Filters gold mine! Check out this Stranger Things logo recreated entirely with SVG Filters:

    See the Pen Stranger Things Logo in SVG (Filters) by Michael Mullany (@mullany) on CodePen.light

  • David Dailey has a fantastic introduction to SVG Filters in which he shows a wide range of possible effects that he created with feTurbulence, including but not limited to heavy cloud and bokeh-like effects.

    Screen Shot 2019-01-21 at 15.00.18

    The results of adjusting the results of feTurbulence with other filter primitives. Adjusting and saturating colors with feColorMatrix, as well as sharpening with feConvolveMatrix are examples of things you can do with generated noise.

  • Dirk Weber also created some of the best SVG Filter effects that I personally learned a lot from. He shared his experiments in an article on Smashing Magazine more than three years ago! He shares a variety of text effects created using SVG filters. You’ll find examples of grunge texture, protruding and 3D text, water splash effects (uses feTurbulence), and many more. Dirk uses two filter primitives in his experiments that we didn’t touch on in this series:

    • feTile which is a utility primitive which fills a target rectangle with a repeated, tiled pattern of an input image. Yoksel also has a great example created using a series of operations including feTile that is worth exploring.
    • and feConvolveMatrix which is one of the more complex and also more powerful primitives. It applies a matrix convolution filter effect. A convolution combines pixels in the input image with neighboring pixels to produce a resulting image. A wide variety of imaging operations can be achieved through convolutions, including blurring, edge detection, sharpening, embossing, and beveling. I haven’t personally experimented with this primitive just yet, but I know other people who have.
    Group
    Some of the text effects that Dirk Weber covers in his article o n Smashing Magazine.
  • Yoksel’s SVG experiments on Codepen are also a great resource to learn from. She even created this fantastic visual SVG Filters tool which you can use to create effects and copy-paste the generated code to use in your own projects.

    Screen Shot 2019-01-21 at 15.17.07
    The visual SVG Filters editor by Yoksel.
  • And last but not least, you can find all there is to know about elements, properties and attributes of SVG Filters in The SVG Filters Specification.

Why SVG Filters?

SVG is currently a lot more powerful than CSS when it comes to creating graphical effects on the web. And there are several reasons why creating visual effects on the web is better than importing them as images from graphics editors like Photoshop and Illustrator:

  • In the days and age of the responsive web, we are no longer dealing with one image. For every image we use on the web, we should be providing responsive versions of that image optimized for different user contexts and performance. This means that if you create an image and then decide to change something about the effect in it, you’re going to have to change the effect in multiple images, which will easily turn into a maintenance nightmare. Creating effects in the browser, on the other hand, means they are resolution-independent and easier to edit.
  • The ability to apply filter effects on the web helps to maintain the semantic structure of the document, instead of resorting to images which—aside from generally being a fixed resolution—tend to obscure the original semantics of the elements they replace. This is especially true for effects applied to text. When effects are applied to real text on the web, that text is going to be searchable, selectable and accessible.
  • Effects created on the web are easier to edit, change, and update without having to jump between the graphics editor and the code editor or browser.
  • And last but not least, effects created on the web can be animated and interacted with. This is one of their most important points of strength.

Final Words

Thank you for joining me on this SVG Filters journey over the last few weeks. I hope this series has inspired you to start experimenting with SVG Filters and using them more when and where appropriate. There are many effects that you can create that would definitely be filed as experimental. But there are also many practical applications for filters in the wild as well. My hope is that this series has given you a glimpse of what’s possible and that it encourages you to unleash your imagination and to start creating your own practical use cases.

Finally, I hope you enjoyed this series and found it useful. Thank you for reading. =)

SVG Filter Effects: Moving Forward was written by Sara Soueidan and published on Codrops.

SVG Filter Effects: Creating Texture with <feTurbulence>

SVGFilterEffects_feturbulance_featured

feTurbulence is one of the most powerful SVG filter primitives. The specification defines this primitive as follows:

This filter primitive creates an image using the Perlin turbulence function. It allows the synthesis of artificial textures like clouds or marble. […]
The resulting image will fill the entire filter primitive subregion for this filter primitive.

In other words, the feTurbulence filter primitive generates and renders Perlin noise. This kind of noise is useful in simulating several natural phenomena like clouds, fire and smoke, and in generating complex texture like marble or granite. And like feFlood, feTurbulence fills the filter region with new content.

In this article, we’re going to go over how we can create noise with feTurbulence and how that noise can be used to distort images and text, much like we did with the feDisplacementMap texture in the previous article. Then, we’re going to see how the generated noise can be used in combination with SVG lighting effects to create a simple rough paper texture.

But first, let’s get an overview of feTurbulence and its attributes and see how each one affects the generated noise.

Creating Turbulence and Fractal Noise with feTurbulence

When I set out to write this series, I made the decision to avoid the gnarly technical details behind filter primitives as much as possible. This is why we won’t get into the technical details behind the functions used to generate Perlin noise.

After reading up on the function underlying noise generation, I found that it didn’t help me at all when I put the primitive into experimentation. After all, we are working with a random noise generator here. So, most of the times, you’ll find that generating texture will be a matter of experimenting and tweaking until you get the desired result. With time, it gets a little easier to predict what a texture might look like.

I’ve found that playing with feTurbulence and tweaking its attributes visually was the best way to learn about them and has helped me understand what each of the attributes does. So, we will be taking a visual approach to understanding feTurbulence, with a few interactive demos to help.

Now, feTurbulence generates noise using the Perlin Turbulence function. It has 5 main attributes that control the function and therefore the visual result of that function:

  • type
  • baseFrequency
  • numOctaves
  • seed
  • stitchTiles

We’ll go over how each of these attributes affects the visual result without going into the technical details of the function. You’ll find that, most of the times, you’ll only need to worry about three of these attributes: type, baseFrequency and numOctaves.

baseFrequency

In order to generate noise, only the baseFrequency attribute is required. The baseFrequency affects the size (or scale) and the grain of the generated noise.

baseFrequency’s effect is best understood when it is visualized and animated. That’s why I created the following live demo. Using the slider, you can change the value of the base frequency used and see how it affects the generated noise in real-time. You’ll notice that as you increase or decrease the value of the baseFrequency attribute, the generated pattern remains intact as it becomes smaller or larger, respectively, and looks like it’s zooming in and out of its origin at the top left corner.

See the Pen feTurbluence: baseFrequency by Sara Soueidan (@SaraSoueidan) on CodePen.light

Lower baseFrequency values (such as 0.001) generate larger patterns, while higher values (0.5+) produce smaller patterns. The values start from 0 (no frequency == no pattern) and up. Negative values are not allowed. as Michael Mullany mentions, “values in the 0.02 to 0.2 range are useful starting points for most textures.

Note that the noise generated does not have a background color. Meaning that, if you remove the white background color on the SVG, you’ll be able to see the dark body’s background through the noise.

The baseFrequency attribute also accepts two values. When you provide two values, the first one will be used for the base frequency on the x-axis and the second one will correspond to the y-axis. By providing two different values, you can generate vertical or horizontal noise that can be used to achieve some fantastic effects, as we’re going to see in a following section.

Play with the values of the baseFrequency again in this live demo and notice how it changes along the X and Y axes as you give it different values. The demo starts with a nice horizontal noise. The 0.01 x-baseFrequency value is relatively small, which makes the horizontal pattern larger (like it’s stretched out). If you decrease it further (to 0.001, for example), you’ll see the horizontal pattern become more like lines. Try it.

See the Pen feTurbluence: x & y baseFrequency by Sara Soueidan (@SaraSoueidan) on CodePen.light

type

As its name suggests, the type attribute is used to specify the type of noise generated by feTurbulence. There are two types available:

  • turbulence, which is the default value, and
  • fractalNoise.

fractalNoise generates a more cloudy and smooth pattern and is a suitable base for creating gas-base textures like clouds. turbulence generates more lines that simulate ripples and are thus suitable as a base for liquid textures.

Screen Shot 2019-01-20 at 16.55.25
turbulence type noise on the left, and fractalNoise type on the right.

Change the value of the type attribute in the following demo to see how the generated pattern changes:

See the Pen feTurbluence: stitchTiles by Sara Soueidan (@SaraSoueidan) on CodePen.light

numOctaves

numOctaves is short for the “number of octaves”, which represent the level of detail in a noise.

In music, an octave is the difference in pitch between two notes where one has twice the frequency of the other. So the higher the octaves, the higher the frequency. In feTurbulence, the higher the number of octaves, the more detail you can see in the noise it generates. By default, the generated noise has one octave, which means that the default value for the numOctaves attribute is 1.

Drag the slider in the following demo to see the effect of increasing the number of octaves on the generated texture:

See the Pen feTurbluence: numOctaves by Sara Soueidan (@SaraSoueidan) on CodePen.light

You’ll notice that starting from numOctaves="5" the effect of adding more octaves becomes practically unnoticeable.

seed

The seed, as defined in the specification, is “the starting number for the pseudo random number generator”. In other words, it provides a different starting number for the random function used to generated our random noise.

Visually, you’ll see that it affects where and how the “ripple lines” are generated. It is also better understood when you see how it affects the noise generated in two adjacent rectangles.

When the same seed is used for the two adjacent rectangles, the function used to generate the noise across the two rectangles is continuous, and this will be reflected visually by the continuity of the “ripple lines” across the edges of the two rectangles.

Group
The continuity of the function generating the random noise can be seen along the edges of the two rectangles using the same seed value.

Play with the value of the seed attribute in the following demo, see how it affects the generated noise, and notice how the noise is continuous across the edges of the two rectangles that are using the same seed value.

See the Pen feTurbluence: seed by Sara Soueidan (@SaraSoueidan) on CodePen.light

stitchTiles

stitchTiles can be used to create a stitching effect between “tiles” of noise. The effect of this attribute is very similar to that of the seed, meaning that it is most evident when you have two adjacent areas (or “tiles”) of noise.

As the specification mentions, sometimes the result of the noise generation will show clear discontinuities at the tile borders. You can tell the browser to try to smooth the results out so that the two tiles appear to be “stitched” together. (I really like how the attribute and its effect are compared to stitching.)

By default, no attempt is made to achieve smooth transitions at the border of tiles which contain a turbulence function because the default value for stitchTiles is noStitch. If you want to create that stitching effect, you can change the value to stitch.

In order to compare the result of stitchTiles to that of seed, I have applied the same seed value to the noise generated in the two rectangles in the following demo. You can already see that the noise appears to be continuous between the two. Switch the stitchTiles option “on” (by changing its value to stitch) to see how the noise changes to accommodate across the edges.

See the Pen feTurbluence: stitchTiles by Sara Soueidan (@SaraSoueidan) on CodePen.light

As I mentioned earlier, the only three attributes you’ll most likely be using are type, baseFrequency and numOctaves. So we’ll be focusing on these three moving forward.

Using feTurbulence-Generated Noise to Distort Content

This is where the fun starts. And this is where we start putting the generated noise to use. After all, just filling the filter region with the noise has no use in and of itself.

In the previous article we used feDisplacementMap to conform a piece of text to the texture in an external image. And we mentioned that feDisplacementMap uses the color information in one image to distort another. The image that is used as a displacement map can be any image. This means that it can be an external image or an image generated within SVG, such as a gradient image or a pattern… or a noise texture.

In other words, the noise we generate with feTurbulence can as well be used to distort content if it is used with feDisplacementMap. In the following demo, we used the output of feTurbulence to displace the image with feDisplacementMap. I’m using a horizontal noise pattern by providing two different values for the baseFrequency attribute similar to what we did earlier.

<svg viewBox="0 0 180 100">
    <filter id="noise" x="0%" y="0%" width="100%" height="100%">
        <feTurbulence baseFrequency="0.01 0.4" result="NOISE" numOctaves="2" />
        <feDisplacementMap in="SourceGraphic" in2="NOISE" scale="20" xChannelSelector="R" yChannelSelector="R"></feDisplacementMap>
    </filter>

    <image xlink:href="..." x="0" y="0" width="100%" height="100%" filter="url(#noise)"></image>
</svg>

See the Pen feTurbluence as a displacementMap by Sara Soueidan (@SaraSoueidan) on CodePen.light

The intensity by which the turbulence distorts the image is specified in the sale attribute on feDisplacementMap. I’ve used a large value so that the effect looks more dramatic.

Now, going from this simple application, we can open a lot more possibilities when we combine the facts that:

  • SVG filters can be applied to HTML content, and
  • the values of baseFrequency are numbers and can thus be animated..

A little less than a couple of years ago, Adrien Denat wrote an article right here on Codrops in which he experimented with a similar effect applied to HTML buttons. We’re going to break down and recreate the following button click effect:

b7w

We’re going to start by creating the noise texture. We’re going to start with the final state—the state where the button is distorted, and then, once we’ve got that, we’re going to animate the initial state of the button to that distorted state and back on click.

Our aim here is to distort the button horizontally. So we will be using and tweaking the horizontal noise from the previous demo a little bit. Its distortion effect on the image is a little too strong, so I’m going to dial it down first by changing the turbulence value from 0.01 0.4 to 0 0.2:

<filter id='noise' x='0%' y='0%' width='100%' height='100%'>
        <feTurbulence type="turbulence" baseFrequency="0 0.2" result="NOISE" numOctaves="2" />
        <feDisplacementMap in="SourceGraphic" in2="NOISE" scale="30" xChannelSelector="R" yChannelSelector="R"></feDisplacementMap>
</filter>

The effect gets a little better, but the button is still distorted more than we’d like it to:

Screen Shot 2019-01-21 at 10.40.52

We want the distortion to be less dramatic. A useful tip to keep in mind is that we can dial the effect of the noise down instantly by switching the type of noise from the default turbulence to the smoother fractalNoise. As soon we do that, we can see that the distortion effect has also been “smoothed” down:

Screen Shot 2019-01-21 at 10.44.20

This looks much better.

Now that we’ve got a distortion effect we’re happy with, we will start our demo with a filter that, initially, does practically nothing:

<filter id='noise' x='0%' y='0%' width='100%' height='100%'>
        <feTurbulence type="fractalNoise" baseFrequency="0 0.000001" result="NOISE" numOctaves="2" />
        <feDisplacementMap in="SourceGraphic" in2="NOISE" scale="30" xChannelSelector="R" yChannelSelector="R"></feDisplacementMap>
</filter>

We’re going to apply that filter to our button in CSS:

button {
    -webkit-filter: url(#noise);
            filter: url(#noise);
}

At this point, the button still looks un-distorted.

Next, we’re going to use (a slightly modified version of) Adrien’s code which uses GSAP to animate the value inside feTurbulence’s baseFrequency to 0 0.2 and back on click:

var bt = document.querySelectorAll('.button')[0],
	turbVal = { val: 0.000001 },
	turb = document.querySelectorAll('#noise feTurbulence')[0],
	
	btTl = new TimelineLite({ paused: true, onUpdate: function() {
  turb.setAttribute('baseFrequency', '0 ' + turbVal.val);
} });

btTl.to(turbVal, 0.2, { val: 0.2 })
    .to(turbVal, 0.2, { val: 0.000001 });

bt.addEventListener('click', function() {
  btTl.restart();
});

And that’s all there is to it, really. You can play with the live demo here:

See the Pen feTurbluence on BUTTONs by Sara Soueidan (@SaraSoueidan) on CodePen.light

The demo works in Chrome and Firefox at the time of writing of this article. It is buggy in the current version of Safari but the issue is resolved in the next version, as the Safari Tech Preview shows the demo works perfectly. It doesn’t work in MS Edge, though, but the button isn’t distorted at all which means that the lack of support does not affect the usability of the button. This is great because you can still use this effect as an enhancement.If the effect isn’t supported, the button will simply look and behave like a normal, effect-less button. Adrien’s article includes quite a few more button distortion effects that use the same principles we’ve just covered that are definitely worth checking out and breaking down. There are one or two nice tricks to learn from each.

Squiggly Text using feTurbulence

One of my favorite examples of feTurbulence in action is Lucas Bebber’s Squiggly Text effect. In his demo, Lucas is using multiple feTurbulence functions:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    <defs>
        <filter id="squiggly-0">
            <feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="0" />
            <feDisplacementMap id="displacement" in="SourceGraphic" in2="noise" scale="6" />
        </filter>
        <filter id="squiggly-1">
            <feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="1" />
            <feDisplacementMap in="SourceGraphic" in2="noise" scale="8" />
        </filter>

        <filter id="squiggly-2">
            <feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="2" />
            <feDisplacementMap in="SourceGraphic" in2="noise" scale="6" />
        </filter>
        <filter id="squiggly-3">
            <feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="3" />
            <feDisplacementMap in="SourceGraphic" in2="noise" scale="8" />
        </filter>

        <filter id="squiggly-4">
            <feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="4" />
            <feDisplacementMap in="SourceGraphic" in2="noise" scale="6" />
        </filter>
    </defs>
</svg>

..and applying them via CSS to a piece of HTML text using CSS animations, animating from one to another:

@keyframes squiggly-anim {
  0% {
    -webkit-filter: url("#squiggly-0");
            filter: url("#squiggly-0");
  }
  25% {
    -webkit-filter: url("#squiggly-1");
            filter: url("#squiggly-1");
  }
  50% {
    -webkit-filter: url("#squiggly-2");
            filter: url("#squiggly-2");
  }
  75% {
    -webkit-filter: url("#squiggly-3");
            filter: url("#squiggly-3");
  }
  100% {
    -webkit-filter: url("#squiggly-4");
            filter: url("#squiggly-4");
  }
}

..thus creating the squiggly effect.

Once again, the text used is real, which means that it is searchable, selectable, accessible and editable (using the contenteditable attribute). Check the live demo out, but beware that this demo is resource-intensive, so you may want to avoid opening the Codepen on mobile.

An animated screenshot of Lucas’s squiggly text demo.


So, some useful takeaways from this section are:

  • The noise generated using feTurbulence can be used to distort both SVG and HTML content.
  • The value of baseFrequency can be animated.
  • You can dial the amount of distortion down by tweaking the values in baseFrequency and by smoothing the noise out with the fractalNoise type.
  • Even though you can animate SVG filters in general, it’s usually recommended to not overdo it because they can be quite resource-intensive. Try to keep the animations limited to smaller areas; the larger the animated area, the more resource-consuming it will be.

feTurbulence is rarely—if ever—useful when used alone. It is pretty much always used by (an)other filter primitive(s) to achieve particular effects. In this section, we used it as a displacement map in feDisplacementMap. Let’s see what more we can do with it.

Simulating Natural Texture with feTurbulence

Another useful way feTurbulence-generated noise can be used is to simulate natural texture. If you’ve ever used the noise generation plugins in After Effects, you may have already come across this functionality and examples of doing so.

Screen Shot 2019-01-21 at 11.09.05
Examples of textures created in After Effects using the Fractal Noise plug-in. (Source)

feTurbulence generates noise (random values) across each of the R, G, B, and A components. You can tweak the values for each of these components to get different variations of the noise. In order to simulate a texture, we usually need to do exactly that: tweak the R/G/B/A components (canceling out components, saturating others, etc.) to get our desired result. Other times, all we need to do is shed some light on it. Literally.

In this section, we’re going to break down a rough paper texture effect created by Michael Mullany. In order to create this texture, we will need to shine a light on a noise texture generated by feTurbulence using SVG’s lighting sources.

Lighting Sources in SVG

SVG conveniently provides a few primitives that can be used to shine a light on objects or images.

There are two filter primitives that are used to specify the type of light you want:

  • feDiffuseLighting which indicates indirect light from an outside source, and is best used for sunlight effects, and
  • feSpecularLighting which specifies secondary light that bounced from reflective surfaces.

Both primitives shine a light on an object or image by using the alpha channel of that image as a bump map. Transparent values remain flat, while opaque values rise to form peaks that are illuminated more prominently.

In other words, a light source filter uses an input’s alpha channel to provide depth information: higher opacity areas are raised toward the viewer and lower opacity areas recede away from the viewer. This means that the alpha value of a pixel in the input is used as the height of that pixel in the z-dimension, and the filter uses that height to calculate a virtual surface, which will reflect a particular amount of light from the light source. (This is pretty powerful stuff!)

Both types of light accept an attribute called surfaceScale which is practically a z-index multiplier. If you increase this value, the “slopes” in the surface texture become steeper.

“Because feTurbulence generates an alpha channel full of noisy values from 0 to 1, it produces a nice variable Z terrain that creates highlights when we shine our light on it.” —Michael Mullany

After deciding on the type of light you need, you’ll want to choose a light source.

There are three kinds of light sources in SVG:

  1. feDistantLight: this represents a distant light source which is arbitrarily far away, and so is specified in terms of its angle from the target. This is the most appropriate way to represent sunlight.
  2. fePointLight: this represents a point light that emanates from a specific point that is represented as a three-dimensional x/y/z coordinate. This is similar to a light source inside a room or within a scene.
  3. feSpotLight: this represents a spotlight and which behaves much like a point light, but its beam can be narrowed to a cone, and the light can pivot to other targets.

Each of these three light sources comes with its own attributes that are used to customize the light it generates by specifying the location of the source in the 3D-space. The attributes are outside the scope of this article, but you can learn more about them in the specification.

To create and apply a lighting effect, you need to nest the light source inside the light type. So, you start by choosing the type of light you want and then picking the source you want it to emanate from. And then finally you need to specify the color of your light. The lighting-color property is used to define the color of the light source for feDiffuseLighting and feSpecularLighting.

With the basics of lighting sources covered, we’ll now get to our example.

For the rough paper texture, we’ll be using sun-like light. This means that we will use a white diffuse lighting that emanates from a distant source. Translated to code, our light looks like this:

<feDiffuseLighting lighting-color="white" surfaceScale="2" in=".." result="..">
    <feDistantLight azimuth="45" elevation="60" />
</feDiffuseLighting>

The azimuth and elevation attributes determine the position of the source of light in 3D space. There’s an article by Rafael Pons that is absolutely fantastic at explaining these two concepts in a simple, easy-to-understand manner, along with beautiful and friendly illustrations to assist with his explanation. I highly recommend checking it out.

Now that we have a light set up, we want to generate our noise that we want to shine this light on. We’ll break the demo down into steps to learn how it’s made.

We gotta start somewhere, so we’ll start by generating a random, basic noise as a base for our texture:

<feTurbulence baseFrequency='0.04' result='noise' />

Our noise looks like this:

Screen Shot 2019-01-21 at 14.33.33

Next, we’ll shine our light onto it and then take it from there:

<feTurbulence baseFrequency='0.04' result='noise' />

<feDiffuseLighting in='noise' lighting-color='white' surfaceScale='2'>
      <feDistantLight azimuth='45' elevation='60' />
</feDiffuseLighting>

Shining the light on our noise gives us the following texture:

Screen Shot 2019-01-21 at 14.35.48

This isn’t the texture result we’re after just yet. The first thing we notice here is the presence of a lot of sharp lines in the texture. We want to get rid of these because a paper surface does not have sharp lines in it. We need to smooth these lines out. We can do that by changing the type of the generated noise to fractalNoise:

<feTurbulence type="fractalNoise" baseFrequency='0.04' result='noise' />

<feDiffuseLighting in='noise' lighting-color='white' surfaceScale='2'>
      <feDistantLight azimuth='45' elevation='60' />
</feDiffuseLighting>

This removes all those sharp lined edges from our texture:

Screen Shot 2019-01-21 at 14.37.59

We’re now one step closer to our rough paper texture.

The above texture isn’t rough enough, though. It lacks the necessary “roughness”. Increasing the amount of tiny detail in it should make it look rougher. To do that, we will increase the value of numOctaves. We’ll find that around 5 is a great place to get the level of roughness we need:

<feTurbulence type="fractalNoise" baseFrequency='0.04' numOctaves="5" result='noise' />

<feDiffuseLighting in='noise' lighting-color='white' surfaceScale='2'>
      <feDistantLight azimuth='45' elevation='60' />
</feDiffuseLighting>

And our paper texture now looks like this:

Screen Shot 2019-01-21 at 14.40.32

Excellent!

You can play with the live demo here:

See the Pen Rough Paper Texture with SVG Filters by Sara Soueidan (@SaraSoueidan) on CodePen.light

The demo works across all major browsers, including MSEdge.

If you want, you can tweak the effect a little further by playing with the source and distance of the light. For example, decreasing the elevation of the light source from 60 to 40 should increase the contrast between the small hills in the texture. The texture would then look more like this:

Screen Shot 2019-01-21 at 14.42.28

I highly recommend playing with the values of the attributes of the light source and the noise and seeing how they impact the resulting texture.

Final Words

feTurbulence is one of SVG’s most interesting and powerful operations. Combined with other primitives and animated, it is capable of generating some really interesting and appealing effects, textures, and interactions.

I strongly believe that feTurbulence is one of those filters that you’d want to experiment with and break other people’s code down to learn more about it. I still find myself guessing how a texture would look like a lot of times. And since there’s so much we can do with only one texture when used by other primitives, there’s an almost countless set of possible effects that you can make with it. I highly encourage you to check out other people’s work and breaking it down to learn more.

Yoksel has been experimenting with SVG filters on Codepen since my SVG Filters talk came out a few months ago. So you can find quite a bunch of effects to break down and learn from on her Codepen profile.

An animated screenshot of one of Yoksel‘s feTurbulence codepen demos.
One of Yoksel’s latest SVG filter experiments leveraging feTurbulence: SVG Filters are 💕

I hope that this article has inspired you and opened a new door in your imagination to see what you can do with SVG Filters. In the last article in this series, I’ll be sharing some further resources and tools to help you move forward with SVG filters and to start making your own experiments. Stay tuned.

SVG Filter Effects: Creating Texture with <feTurbulence> was written by Sara Soueidan and published on Codrops.

SVG Filter Effects: Conforming Text to Surface Texture with <feDisplacementMap>

SVGFilterEffects_displacementMap_featured

Applying texture to text is one of the most popular text effects in graphic design. As much of print and graphic design has made its way into the Web platform, such effects have also been recreated on the Web using CSS, as well as using features of SVG such as patterns, masks and clipping paths. I have an article right here on Codrops that gives you a full overview of different ways to create textured text on the Web using CSS and SVG today that you may be interested in checking out. Yoksel touched on another area of this topic and wrote an article all about animating text fills.

However, one effect that was untouched on was that of text conforming to the texture of a surface. When text conforms to a surface, it takes the shape of that surface. Depending on the surface and texture used, you could end up some really eye-catching results. This is what this article will touch on. And the best part? All these effects are applied to real, searchable, selectable and accessible text.

This is the fifth in a series of articles on SVG filters. In the previous weeks, we got an introduction to SVG filters and learned how to create and use them to produce various effects from outlined text to posterized images, and how to replicate Photoshop-like duotone image effects with SVG filter operations.

Conforming Text to Surface Texture: The Photoshop Way

As with the duotone effect, I looked into how to make text conform to surface texture in Photoshop in an attempt to replicate that effect with SVG filters. I found this step-by-step tutorial on YouTube. The following video is a sped-up version of that tutorial:

In the Photoshop tutorial, the designer created this effect by using what is known as a displacement map. A displacement map is an image whose color information is used to distort the content of another element. To create our text effect, the texture of the image will be used to distort the text so that it conforms to the shape of the texture.

In Photoshop, in order to conform text to a surface the designer followed these steps:

  1. Desaturate the image.
  2. Reduce the amount of detail in the image by blurring it by 1px.
  3. Save the image as a displacement map.
  4. Create the text, and apply a distortion filter to it using the image as a displacement map.
  5. Re-use the original image as a background behind the text.
  6. Then refine the effect more by adding a slight transparency to the text and blending it with the background image.

The displacement map image is blurred in the second step before it is used to displace the text because if the image has too much or too little detail the resulting effect would look less realistic. Usually blurring the image up to 2px is enough to get a moderate amount of detail that’s just enough.

If you’ve read the previous articles in this series, you know that thinking in steps is important to create and recreate effects with SVG filter primitives. And you may have already guessed how to replicate some of these steps using SVG filter primitives, a few of which we have covered in the previous articles.

But the most important step in this effect is the creation and application of the displacement map. How do we do that in SVG?

Conforming Text to Surface Texture in SVG

In order to recreate the effect from the Photoshop tutorial above, we need to first create a displacement map. In SVG, the feDisplacementMap primitive is used to displace content using a displacement map.

feDisplacementMap takes two inputs to produce one result. The image you want to use to displace your content is specified in the in2 attribute. The in attribute is reserved for the input that you want to apply the displacement effect to.

And as with all other primitives, the input for feDisplacementMap can be anything from the SourceGraphic itself to the result of another filter operation. And since we want to apply the displacement map to our source text, this means that the in attribute will have SourceGraphic for a value.

So let’s recreate the Photoshop tutorial steps with SVG filter primitives. The process of conforming text to texture in SVG is very similar to that we saw in Photoshop. I’ll elaborate on each step as we go.

  1. Get the image that will be used as a texture using feImage.
  2. Desaturate the image using feColorMatrix.
  3. Apply a 0.5px Gaussian blur to the image using feGaussianBlur.
  4. Use the image to distort the text using feDisplacementMap.
  5. Blend the text into the background image using feBlend and apply a translucent effect to it (decrease opacity using feComponentTransfer).
  6. Display the text and the image behind it by merging the two layers using feMerge.

The feImage filter primitive is the filter version of the <image> element and has the same attributes as the <image> element too. So in order to render an image in the filter region, we will use feImage. Once we’ve got our image, we can use it as input to other filter operations. It will be used by the feColorMatrix operation to boot because we need to desaturate it.

We’ve mentioned feColorMatrix before, but what we didn’t mention is it comes with a few keywords that are shortcuts to pre-defined matrices. Instead of always having to provide a matrix as a value, you can change the type attribute and use one of the keywords available:

matrix | saturate | hueRotate | luminanceToAlpha

The matrix type is what you’d use when you want to provide a custom matrix as a value for the matrix operation. The other keywords are convenience shortcuts.

To desaturate an image, we use the saturate type. You specify the amount by which you want to desaturate the image in the values attribute. Since we want to completely desaturate our image, we will provide 0 as a value. Note that values are provided as fractions, with 1 (default value) being fully saturated and 0 being completely desaturated (grayscale).

So, let’s start translating our steps into code:

<!-- I'm extending the filter region just to increase its area for visual purposes. This is not required or needed for the effect to work.-->
<filter id="conform" x="-50%" y="-50%" width="200%" height="200%"> 
        
        <!-- Get the image. -->
        <feImage xlink:href="..." x="0" y="0" width="100%" height="100%" preserveAspectRatio="none"></feImage>
        
        <!-- Desaturate the image. -->
        <feColorMatrix type="saturate" values="0" result="IMAGE"/>

        <!-- ... -->

At this point, our filter region looks like this:

Screen Shot 2019-01-16 at 15.15.00

After desaturating the image, we will blur it by a small amount enough to reduce the amount of detail without losing too much of it. For this particular effect, I chose to blur it by 0.25 pixels only. You may need to experiment with the values to get the right one depending on the image you use and the effect you’re after.

<!-- I'm extending the filter region just to increase its area for visual purposes. This is not required or needed for the effect to work.-->
<filter id="conform" x="-50%" y="-50%" width="200%" height="200%"> 
        
        <!-- Get the image. -->
        <feImage xlink:href="..." x="0" y="0" width="100%" height="100%" preserveAspectRatio="none"></feImage>
        
        <!-- Desaturate the image. -->
        <feColorMatrix type="saturate" values="0" result="IMAGE"/>

        <!-- decrease level of details so the effect on text is more realistic -->
        <feGaussianBlur in="IMAGE" stdDeviation="0.25" result="MAP"></feGaussianBlur>

        <!-- ... -->

And our displacement map now looks like this:

Screen Shot 2019-01-16 at 15.43.19

Using feDisplacementMap we can now distort the text with our displacement map:

<!-- I'm extending the filter region just to increase its area for visual purposes. This is not required or needed for the effect to work.-->
<filter id="conform" x="-50%" y="-50%" width="200%" height="200%"> 
        
        <!-- Get the image. -->
        <feImage xlink:href="..." x="0" y="0" width="100%" height="100%" preserveAspectRatio="none"></feImage>
        
        <!-- Desaturate the image. -->
        <feColorMatrix type="saturate" values="0" result="IMAGE"/>

        <!-- decrease level of details so the effect on text is more realistic -->
        <feGaussianBlur in="IMAGE" stdDeviation="0.25" result="MAP"></feGaussianBlur>
        
        <!-- Use the displacement map to distort the source text -->
        <feDisplacementMap in="SourceGraphic" in2="MAP" scale="15" xChannelSelector="R" yChannelSelector="R" result="TEXTURED_TEXT"></feDisplacementMap>

        <!-- ... -->

At this point, the image we used to distort the text is no longer rendered as it has been used to generate a new result, which is the distorted text. The filter region at this point thus only contains the text that is now conforming to the shape and texture of the fabric in our displacement map:

Screen Shot 2019-01-16 at 15.39.23

You can already see the texture of the fabric take shape on the edges of the text. This is great.

Just like in the Photoshop tutorial, we will now re-display the image behind the text. We will do that by using feImage again:

<!-- I'm extending the filter region just to increase its area for visual purposes. This is not required or needed for the effect to work.-->
<filter id="conform" x="-50%" y="-50%" width="200%" height="200%"> 
        
        <!-- Get the image. -->
        <feImage xlink:href="..." x="0" y="0" width="100%" height="100%" preserveAspectRatio="none"></feImage>
        
        <!-- Desaturate the image. -->
        <feColorMatrix type="saturate" values="0" result="IMAGE"/>

        <!-- decrease level of details so the effect on text is more realistic -->
        <feGaussianBlur in="IMAGE" stdDeviation="0.25" result="MAP"></feGaussianBlur>
        
        <!-- Use the displacement map to distort the source text -->
        <feDisplacementMap in="SourceGraphic" in2="MAP" scale="15" xChannelSelector="R" yChannelSelector="R" result="TEXTURED_TEXT"></feDisplacementMap>
        
        <!-- Re-display the image as a background image -->
        <feImage xlink:href="..." x="0" y="0" width="100%" height="100%" preserveAspectRatio="none" result="BG"></feImage>
        
        <!-- ... -->

Lastly, we want to blend the text into the background image to improve the effect. We will decrease the opacity of the text to 0.9 using feColorMatrix, and then we will use the feBlend primitive to apply a blending mode to the text.

Similar to CSS Blend Modes, we have 16 blend modes to choose from. For our effect, the multiply blend mode will do. (In the Photoshop tutorial, the designer used the linear burn, which is not available in SVG/CSS.)

feBlend will take two inputs to blend together: the text and the background image:

<!-- I'm extending the filter region just to increase its area for visual purposes. This is not required or needed for the effect to work.-->
<filter id="conform" x="-50%" y="-50%" width="200%" height="200%"> 
        
        <!-- Get the image. -->
        <feImage xlink:href="..." x="0" y="0" width="100%" height="100%" preserveAspectRatio="none"></feImage>
        
        <!-- Desaturate the image. -->
        <feColorMatrix type="saturate" values="0" result="IMAGE"/>

        
        <feGaussianBlur in="IMAGE" stdDeviation="0.25" result="MAP"></feGaussianBlur>
        
        <!-- Use the displacement map to distort the source text -->
        <feDisplacementMap in="SourceGraphic" in2="MAP" scale="15" xChannelSelector="R" yChannelSelector="R" result="TEXTURED_TEXT"></feDisplacementMap>
        
        <!---->
        <feImage xlink:href="..." x="0" y="0" width="100%" height="100%" preserveAspectRatio="none" result="BG"></feImage>
        
        <!-- Reduce the opacity of the text -->
        <feColorMatrix in="Textured_Text" result="Textured_Text_2" type="matrix" 
           values="1 0 0 0 0 
                   0 1 0 0 0 
                   0 0 1 0 0 
                   0 0 0 .9 0" />      

        <!-- Blend the text with the background -->
        <feBlend in="BG" in2="Textured_Text_2" mode="multiply" result="BLENDED_TEXT"></feBlend>

        <!-- ... -->

And last by not least, we will layer the new blended text layer on top of the background image layer with feMerge:

<!-- I'm extending the filter region just to increase its area for visual purposes. This is not required or needed for the effect to work.-->
<filter id="conform" x="-50%" y="-50%" width="200%" height="200%"> 
        
        <!-- Get the image. -->
        <feImage xlink:href="..." x="0" y="0" width="100%" height="100%" preserveAspectRatio="none"></feImage>
        
        <!-- Desaturate the image. -->
        <feColorMatrix type="saturate" values="0" result="IMAGE"/>

        <!-- decrease level of details so the effect on text is more realistic -->
        <feGaussianBlur in="IMAGE" stdDeviation="0.25" result="MAP"></feGaussianBlur>
        
        <!-- Use the displacement map to distort the source text -->
        <feDisplacementMap in="SourceGraphic" in2="MAP" scale="15" xChannelSelector="R" yChannelSelector="R" result="TEXTURED_TEXT"></feDisplacementMap>
        
        <!---->
        <feImage xlink:href="..." x="0" y="0" width="100%" height="100%" preserveAspectRatio="none" result="BG"></feImage>
        
        <!-- Reduce the opacity of the text -->
        <feColorMatrix in="Textured_Text" result="Textured_Text_2" type="matrix" 
           values="1 0 0 0 0 
                   0 1 0 0 0 
                   0 0 1 0 0 
                   0 0 0 .9 0" />      

        <!-- Blend the text with the background -->
        <feBlend in="BG" in2="Textured_Text_2" mode="multiply" result="BLENDED_TEXT"></feBlend>

        <!-- Layer the text on top of the background image -->
        <feMerge>
            <feMergeNode in="BG"></feMergeNode>
            <feMergeNode in="BLENDED_TEXT"></feMergeNode>
        </feMerge>
</filter>

<text dx="60" dy="200" font-size="10em" font-weight="bold" filter="url(#conform)" fill="#00826C"> organic </text>

And this is our final result:

Screen Shot 2019-01-16 at 16.12.16

Notes about using Displacement Maps in SVG

The feDisplacementMap element has three attributes that determine how the displacement map will affect the source graphic:

  • xChannelSelector: specifies which color channel (R/G/B/A) from in2 to use for the horizontal displacement;
  • yChannelSelector: specifies which color channel (R/G/B/A) from in2 to use for the vertical displacement;
  • scale: determines the amount by which you want to distort the image. The higher the scale, the stronger the distortion effect is. You’ll probably find yourself experimenting with this value to get the desired result.

Possibly the most important thing to be aware of when using images to displace content in SVG filters is that the image and the content are subject to CORS rules. Make sure you’re serving both the image and the content from the same source to ensure that the browser does not skip the displacement operation.

You can also inline an image in the filter (in feImage) and use it as a displacement map. This pen by Gabi is a great example which uses an inlined SVG pattern to distort the source image. The circular pattern resulting in a ripple-like effect is my favorite.

Applying a Transformation to the Source Text

In the Photoshop tutorial that we followed for this effect, the designer applies a rotation transformation to the text that adds a nice touch to the overall effect.

If we apply a rotation transformation to the <text> to which we are applying the filter, the whole filter region will be rotated, including the image in the background:

Screen Shot 2019-01-16 at 19.32.58

This also happens if you apply other styles to the source text. For example, if you set the opacity on the <text> to 0.5, the text and the image in the background will also be affected by that.

In order to rotate the text but not the rest of the filter region, we can wrap the text in a group (<g>) and apply the filter to the group, and then apply the rotation transformation on the text. This will ensure that only the text is rotated, while the rest of the filter region, which is now defined by the group wrapper, remains unaffected by the transformation. This workaround is courtesy of Amelia Bellamy-Royds.

<g filter="url(#conform)">
     <text dx="60" dy="200" font-size="10em" transform="translate(-20 30) rotate(-7)" fill="#00826C">organic</text>
</g>

I’ve tweaked the transformation a little to add a translation to make sure the text remains centered in the filter region. The result of this transformation now looks like this:

Screen Shot 2019-01-16 at 19.40.50

Note that I’m applying the rotation transformation to the text using the SVG transform attribute and not via CSS because, at the time of writing of this article, Internet Explorer and MSEdge don’t support CSS transformations on SVG elements.

Live Demo

This text displacement effect currently works in all major browsers, including MSEdge. The following is a screenshot of the effect in MSEdge:

Screen Shot 2019-01-16 at 17.44.57

This said, Chrome has recently stopped applying the distortion effect on the text. There’s some more information about this issue in this thread. The rest of the filter operations, however, work and are applied just fine, so, until Chrome fixes this issue, you should be able to see the text blended with the background, only without the distortion along its edges. The following is a screenshot of what the demo looks like in Chrome:

Screen Shot 2019-01-16 at 18.06.57

You can check the live demo out here.

Final words

I hope you’re starting to enjoy the power of SVG filters and thinking of more possibilities and effects to create with them already.

If you liked the idea of conforming text to surface texture, then you’re going to love learning how to create your own texture in SVG. Yup, you read that right. SVG can create texture. In the next article, we’re going to learn how to create a simple texture using a combination of SVG-generated noise and lighting effects. Stay tuned.

SVG Filter Effects: Conforming Text to Surface Texture with <feDisplacementMap> was written by Sara Soueidan and published on Codrops.

SVG Filter Effects: Duotone Images with <feComponentTransfer>

SVGFilterEffects_feComponent2_featured

In the previous article in this series I introduced you to the <feComponentTransfer>, and we used it to limit the number of colors in an image to create a poster effect. In this article, we will take a look at how it can be used to create a Photoshop-like duotone effect. We’ll also learn how to use it to control the intensity and contrast of an image’s colors.

A Quick Recap

To quickly recap, the feComponentTransfer primitive allows you to modify each of the R, G, B and A components present in a pixel. In other words, feComponentTransfer allows the independent manipulation of each color channel, as well as the alpha channel, in the input element.

The RGBA components are modified by running different kinds of functions on these components. To do that, each component has its own element. These component elements are nested within feComponentTransfer. The RGBA component elements are: feFuncR, feFuncG, feFuncB, and feFuncA.

The type attribute is used on a component element to define the type of function you want to use to modify this component. There are currently five available function types: identity, table, discrete, linear, and gamma. These function types are used to modify the RGBA components (the colors and alpha channel) of a source graphic. We mentioned that you can modify one or more component at a time and that you can modify channels independently, applying a different function to each component element.

<feComponentTransfer>
    <!-- The RED component -->
    <feFuncR type="identity | table | discrete | linear | gamma"></feFuncR>

    <!-- The GREEN component -->
    <feFuncG type="identity | table | discrete | linear | gamma"></feFuncG>

    <!-- The BLUE component -->
    <feFuncB type="identity | table | discrete | linear | gamma"></feFuncB>

    <!-- The ALPHA component -->
    <feFuncA type="identity | table | discrete | linear | gamma"></feFuncA>
</feComponentTransfer>">

In the previous article, we demystified the discrete function and saw how it can be used to posterize images. In this article, we will start by using the table function to create a duotone effect similar to what you can create in Photoshop.

Creating a Duotone Effect in Photoshop

I’m not a designer, and I don’t know my way around graphics editors like Photoshop. When I wanted to create a duotone effect in SVG, I looked for the way to create this effect in graphics editor first to see if I can replicate it using the filter operations available in SVG. As it turns out, the steps to create duotone images in SVG turned out to be same as those used in Photoshop.

The following video is a sped-up version of this tutorial I found on YouTube.

In the video, the designer creates the duotone effect following these steps:

  1. Desaturate the image, making it grayscale.
  2. Map the grayscale range into a new range that, instead of having black and white on either end, it has two different colors that you want to use in the duotone effect. In other words, you will need to create and use a gradient map that the grayscale is mapped to.

Let’s see how these steps can be replicated in SVG.

Creating a Duotone Effect in SVG

To recreate this effect in SVG, we will need to desaturate the image first. This is possible using the <feColorMatrix> filter primitive.

Then, we need to be able to create and provide a gradient map for the browser to map the new grayscale image to.

Converting an image to grayscale using feColorMatrix

Using feColorMatrix you can provide a color matrix that specifies the amount of red, green, and blue in your image. By providing equal amounts of these three components, we are creating a matrix that converts our image into a grayscale version of itself:

<svg viewBox="0 0 266 400">
    <filter id="duotone">

        <feColorMatrix type="matrix" values=".33 .33 .33 0 0
                .33 .33 .33 0 0
                .33 .33 .33 0 0
                 0   0   0  1 0">
        </feColorMatrix>

        <!-- ... -->
    </filter>
    <image xlink:href="..." width="100%" x="0" y="0" height="100%" 
        filter="url(#duotone)"></image>
</svg>

In the following image, the image on the right is the result of applying the above filter to the image on the left:

Screen Shot 2019-01-12 at 18.50.21
The result (on the right) of converting the image on the left to grayscale using the feColorMatrix filter operation.

You can learn all about feColorMatrix and how to use it in this article by Una Kravets.

Now that our image is essentially made of a gray gradient, we need to create a duotone gradient map to map the gray gradient to.

Creating a Gradient Map using the table component transfer function

In SVG, to create the gradient map, we can use feComponentTransfer primitive with the type table.

In the previous article, we saw how you can map the colors in an image to a list of colors that you provide in the tableValues attribute using the discrete function. The browser used our list of tableValues to generate ranges that are then used to map the colors to the values we provided.

When using the table function, we will also provide color values in the tableValues attribute. Once again, the browser will use the values we provide to map the colors in the image to them. How the browser will map the colors, though, is different. Instead of mapping color ranges to discrete color values, it will create a color range from the values we provide and then map the input range to this new range.

Suppose we want to use the following two colors for our duotone effect:

Screen Shot 2019-01-12 at 19.05.26

These two colors will be used to create a gradient map:

Screen Shot 2019-01-12 at 19.11.46

..that we are going to map our grayscale map to.

IMG_4149

In order to use these colors in feComponentTransfer, we need to get the values of the R, G, and B channels of each color. Since tableValues are provided in fractions, we’ll need to convert the RGB values to fractions. Color values are usually in the range [0, 255]. To convert these values to fractions, we need to divide them by 255.

For example, the pink color has the following RGB values:

R: 254
G: 32
B: 141

Converted to fractions, these values are now equal to:

R: 254/255 = .996078431
G: 32/255  = .125490196
B: 141/255 = .552941176

Similarly, the yellow color values resolve to:

R: .984313725
G: .941176471
B: .478431373

Now that we have our color values handy, it’s time to create our gradient map. We mentioned earlier that when we provide values to tableValues with the table function in use, the browser will use the tableValues to create a range. So we start by providing the RGB values of the two colors as values for the RGB component elements:

<feComponentTransfer color-interpolation-filters="sRGB">
    <feFuncR type="table" tableValues=".996078431  .984313725"></feFuncR>
    <feFuncG type="table" tableValues=".125490196  .941176471"></feFuncG>
    <feFuncB type="table" tableValues=".552941176  .478431373"></feFuncB>
</feComponentTransfer>

Screen Shot 2019-01-12 at 19.26.35

We saw in the previous article that when using the discrete function, the browser creates n ranges for n values in tableValues. When we use the table function, the browser creates n-1 ranges for n values; and since we provided two tableValues for each component, this means that we will get one range ([pink, yellow]) for each.

Now feComponentTransfer will do its thing: The browser will go over each and every pixel in the source image. For each pixel, it will get the value of the Red, Green, and Blue components. Since our image is grayscale, the R/G/B values will be in the range [0, 1] = [black, white] (0 being fully black, 1 being fully white, and shades of gray in between). Then, the value of each component will be mapped to the new range we provided in tableValues. So:

  • The red component value will be mapped to the range [.996078431, .984313725]
  • The blue component value will be mapped to the range [.125490196, .941176471]
  • The green component value will be mapped to the range [.552941176, .478431373]

So by the time the browser goes over all the pixels in the image, you will have replaced all RGB values in the grayscale gradient with the RGB values of the duotone gradient map. As a result, the image becomes duotone.

Screen Shot 2019-01-12 at 19.40.07
The result (on the right) of mapping the grayscale image (left) to our gradient map.

Our full code now looks like this:

<svg viewBox="0 0 266 400">
    <filter id="duotone">
        <!-- Grab the SourceGraphic (implicit) and convert it to grayscale -->
        <feColorMatrix type="matrix" values=".33 .33 .33 0 0
              .33 .33 .33 0 0
              .33 .33 .33 0 0
              0 0 0 1 0">
        </feColorMatrix>

        <!-- Map the grayscale result to the gradient map provided in tableValues -->
        <feComponentTransfer color-interpolation-filters="sRGB">
            <feFuncR type="table" tableValues=".996078431  .984313725"></feFuncR>
            <feFuncG type="table" tableValues=".125490196  .941176471"></feFuncG>
            <feFuncB type="table" tableValues=".552941176  .478431373"></feFuncB>
        </feComponentTransfer>
    </filter>

    <image xlink:href=".." width="100%" x="0" y="0" height="100%" 
           filter="url(#duotone)"></image>
</svg>

And you can play with the live demo here:

See the Pen Duotone Image effect by Sara Soueidan (@SaraSoueidan) on CodePen.light

You can take this further and instead of providing only two color values for the gradient map you can provide three color values in tableValues, creating a gradient map that has three colors instead of two.

Controling color contrast and intensity with the gamma transfer function

Using the gamma component transfer function we are able to perform gamma correction on our source graphic. Gamma correction is the function of controlling an image’s luminance levels.

The gamma function has three attributes that allow you to control the gamma correction function that will be used to control the luminance: amplitude, exponent and offset. Combined, they make up the following transfer function:

C' = amplitude * pow(C, exponent) + offset

gamma can be used to control the overall contrast in an image. Increasing the exponent makes the darker areas darker while increasing the amplitude makes the lighter areas shine more. And this, in turn, increases the overall contrast of the image. The offset is used to increase the intensity of each component, and also affects the overall image: both highlights and dark areas.

Tweaking the contrast and dark and light areas of an image can sometimes be useful if you’re not getting the amount of “shine” that you’d like to see in an image.

For example, if I apply the duotone filter from the previous section to the following image, the result is not as “lively” as I’d want it to be:

Screen Shot 2019-01-14 at 18.50.04

The duotone image on the right looks a little pale and the colors slightly washed out. I want to add some contrast to it to make it look more lively. By increasing the amplitude and the exponent a little bit:

<feComponentTransfer color-interpolation-filters="sRGB">
      <feFuncR type="gamma" exponent="1.5" amplitude="1.3" offset="0"></feFuncR>
      <feFuncG type="gamma" exponent="1.5" amplitude="1.3" offset="0"></feFuncG>
      <feFuncB type="gamma" exponent="1.5" amplitude="1.3" offset="0"></feFuncB>
</feComponentTransfer>

I am able to make the light areas shine more and the darker areas look more intense:

Screen Shot 2019-01-14 at 18.56.11

Of course, this is just an example. You may prefer the paler version of the image, especially that it might look better with text on top. I think gamma correction is most useful in controlling the contrast of black-and-white images. If I apply the same gamma correction operation to the grayscale version of the image, I get a more favorable version:

Screen Shot 2019-01-14 at 19.08.34

Of course, you may want to do the opposite: instead of increasing contrast, you may want to lighten the dark areas a bit, in which case you’d decrease the amplitude and/or exponent values instead of increasing them. The default value for both the amplitude and exponent is 1. The default offset value is 0.

Play with the gamma function values in the following live demo to get a better feel of how it affects the brightness and contrast of an image:

See the Pen Duotone Image effect with Contrast Tweak by Sara Soueidan (@SaraSoueidan) on CodePen.light

The SVG Gradient Map Tool

Yoksel has been playing with SVG Filters for a while and has recently created a fantastic visual tool that allows you to upload an image and apply different duotone and even tritone effects, and that generates the SVG filter code for you ready to copy-paste anywhere you need it. It is a great tool to play with to learn more about feComponentTransfer.

Screen Shot 2019-01-14 at 19.13.12
The SVG Gradient Map Filter tool by Yoksel.

The tool even allows you to tweak the grayscale effect created using feColorMatrix. In our code, we used equal amounts of the R, G, and B channels to get a grayscale effect. This is one way to make an image grayscale. But there are other ways, too. For example, you could create a grayscale effect per channel, which would result in a different grayscale result for each:

b7w

I recommend playing with the tool a little bit and checking how your choice of effect changes the underlying code, as this is one of the best ways to learn more about SVG filters.

Final Words

The feComponentTransfer primitive gives us a lot of control over the color and alpha components of images and enables us to create Photoshop-grade effects in the comfort of our code editors.

In the next article in this series, we will look at a couple more primitives that allow us to replicate yet another Photoshop effect using almost exactly the same steps as those you’d take in Photoshop, showing once again how SVG can bring the power of graphic editors into the Web platform. We’ll learn how to blend text with both the color and the texture of a background image, to create some eye-catching results. Stay tuned.

SVG Filter Effects: Duotone Images with <feComponentTransfer> was written by Sara Soueidan and published on Codrops.

SVG Filter Effects: Poster Image Effect with <feComponentTransfer>

SVGFilterEffects_ComponentTransfer_featured

This is the third in a series of articles exploring SVG filters and some effects made possible through them. The first article introduced us to SVG filters, what they are, how they work, and how to create and use them. The second article explored the creation of paint-like image effects as well as text outlines using the <feMorphology> filter primitive.

This article assumes that you’re already familiar with the basics of SVG filters, or that you’ve read the first (SVG Filters 101) article in this series. If you haven’t, please feel free to take a few minutes to read it.

The feComponentTransfer is one of SVG’s most powerful filter primitives. It gives us control over the individual RGBA channels of our source graphic, enabling us to create Photoshop-like effects in SVG. In this article, which is the first article focusing on feComponentTransfer, we’ll get to know this primitive and see how it can be used to posterize images.

Posterization or posterisation of an image entails conversion of a continuous gradation of tone to several regions of fewer tones, with abrupt changes from one tone to another. This was originally done with photographic processes to create posters. — Wikipedia

Posterization occurs across an image but is most obvious in areas of subtle variation in tone.

Before and After of a posterized image.
Example of a photograph in JPEG format (24-bit color or 16.7 million colors) before posterization, contrasting the result of saving to GIF format (256 colors). (Source: Wikipedia)

In this article, we’ll use feComponentTransfer to reduce the number of colors in an image, which, in turn, will result in the creation of a nice poster effect similar to what we see in commercial or graphic design posters.

Screen Shot 2019-01-22 at 15.53.05
Applying a posterizing effect to an image (left) with feComponentTransfer reduces the number of colors in that image (right).

But first, let’s cover the technical basics…

Quick Overview of feComponentTransfer

The feComponentTransfer primitive allows you to modify each of the R, G, B and A components present in a pixel. In other words, feComponentTransfer allows the independent manipulation of each color channel, as well as the alpha channel, in the input graphic. It allows operations like brightness adjustment, contrast adjustment, color balance or thresholding.

The RGBA components are modified by running transfer functions on these components. To do that, each component has its own element, referred to as Transfer Function Element. I will be referring to these elements as “component elements” throughout this article — elements that refer to individual RGBA components. These elements are nested within feComponentTransfer. So the feComponentTransfer does nothing aside from housing the individual RGBA component elements. The RGBA component elements are: feFuncR, feFuncG, feFuncB, and feFuncA.

The type attribute is used on a component element to define the type of function you want to use to modify this component. There are currently five available function types: identity, table, discrete, linear, and gamma. These function types are used to modify the R/G/B/A components of a source graphic. We will cover most of these and see how they can be used in this series.

<feComponentTransfer>
    <!-- The RED component -->
    <feFuncR type="identity | table | discrete | linear | gamma"></feFuncR>

    <!-- The GREEN component -->
    <feFuncG type="identity | table | discrete | linear | gamma"></feFuncG>

    <!-- The BLUE component -->
    <feFuncB type="identity | table | discrete | linear | gamma"></feFuncB>

    <!-- The ALPHA component -->
    <feFuncA type="identity | table | discrete | linear | gamma"></feFuncA>
</feComponentTransfer>">

For each function type, one or more attributes exist that allow you to specify more details of, and for, the function used. For example, the linear function has a slope attribute that is used to specify the slope of the linear function that will be used to modify the component it is applied to.

You can modify one or more component at a time. This means that the feComponentTransfer may contain one, two, three, or all of the component elements at a time. You can also modify channels independently, applying a different function to each component element.

The ability to use different functions on different component elements means that you have very large control over the colors of your source graphic on the lowest pixel level. You may choose to modify the red and blue channels by mapping them to two new colors while keeping the green unchanged or only increasing its intensity, for example. This low-level component control means that you will be able to apply Photoshop-grade functions to your images in the browser using a few lines of code. I don’t know about you but the (wannabe-)designer in me thinks this is super exciting!

Example: Using the Alpha component to reduce the opacity of an object

A simple real-life example is using the feFuncA component element to reduce the transparency of a source graphic. In the first article in this series, we saw how we can use feColorMatrix to reduce the opacity of an element by changing the value of the alpha channel in a color matrix. I personally prefer using feComponentTransfer for this singular operation.

Applied to a source, the following filter reduces the opacity of that source to 0.5:

<filter id="redOp">
    <feComponentTransfer>
        <feFuncA type="table" tableValues="0 0.5"></feFuncA>
    </feComponentTransfer>
</filter>

We mentioned above that we have five different functions that we can use to manipulate the RGBA components. The table function type works by mapping the values of the component (which is the alpha channel in our example) to a series of values provided in the tableValues attribute.

So, what does that mean?

The alpha channel of an element usually lies in the range [0, 1]. By using the table function and providing two values: 0 and 0.5, we’re essentially telling the browser to map the [0, 1] alpha range to a new range: [0, 0.5]. By doing so, the opacity is reduced to half.

See the Pen feMorphology on an image by Sara Soueidan (@SaraSoueidan) on CodePen.light

We’ll go into a more detailed example of the table function in the next article. In this article, I want to shed the light on the discrete function type. So, let’s see how it works and what we can do with it.

Poster Image Effect: Reducing the number of colors in an image with the discrete function

The discrete function is used to decrease the number of colors in an image (or in a component if used on only one component). Reducing the number of colors in an image means that, instead of smooth, linear gradient color changes, you will see more sudden color shifts, which make the image look like it is made of bands or clusters of color, thus resulting in a poster-like effect.

Screen Shot 2019-01-12 at 12.38.29
The image on the right is a copy of the image on the left with a discrete function used to reduce the number of colors in it to only 5 values per component. You can see how instead of smooth color changes (using gradients), the colors change suddenly, creating color bands and clusters, and the image looks more “posterized”.

Personally, the discrete function reminds me of the steps() timing function in CSS. When compared to a linear function, a step-by-step function jumps from one value to another, instead of moving linearly between them.

Like the table function, the discrete function accepts a series of values provided in the tableValues attribute. The discrete function differs from table in the way it uses these values.

Using tableValues you provide the browser with a finite list of values that you want it to map a color component to. And since you are providing a finite list of values, you will end up with a finite number of colors, thus creating color bands and clusters, that would otherwise normally be linear gradient shifts of colors.

The function is defined by the step function given in the attribute tableValues, which provides a list of n values in order to identify a step function consisting of n steps.The SVG Filters Specification

Let’s see what that means in plain English. Assume we have the following code snippet:

<svg width="500" height="335" viewBox="0 0 500 335">
    <filter id="posterize">
        <feComponentTransfer>
            <feFuncR type="discrete" tableValues="0 .5 1" />
        </feComponentTransfer>
    </filter>

    <image xlink:href="..."cwidth="100%" height="100%" x="0" y="0" 
           filter="url(#posterize)"></image>
</svg>

In the above snippet, we are applying a discrete function to modify the Red color channel in our source image. We provide 3 discrete values that we want the browser to map the red color values to. In an SVG filter, component values are represented in fraction values in the range [0, 1]. This means that the Red component value in any pixel can be 0 (0% Red / fully black) or 1 (100% Red) or any value (shade of red) in between. This is the same for the Green, Blue and Alpha channels too.

For every n number of values you provide, the browser will create n ranges. More specifically, it will divide the [0, 1] into n ranges. Then it will map the color values that lie within those ranges to the n values you provided. Applying this logic to our snippet:

  • The browser sees three discrete values for red in tableValues;
  • It divides the red color values to three equal ranges within the [0, 1] range. So our three ranges look like this:
    • [0, 0.33]
    • [0.33, 0.66]
    • [0.66, 1]
  • Next, the browser checks the current value of red in each and every pixel in the image. For each pixel, it determines which range that red color value belongs in.
  • Then, it maps that red value to the corresponding new value you provided. The mapping looks like this:
    • Colors within the [0, 0.33] range are mapped to 0;
    • Colors within the [0.33, 0.66] range are mapped to 0.5;
    • Colors within the [0.66, 1] range are mapped to 1;

You can also think of this process as turning shades of color ON or OFF. When you provide discrete values for a color, you’re telling the browser that only these values will be ON, and if a pixel contains a value that is not equal to one of these three, it should be turned OFF and replaced by one of these three. So, for example, the 0.8 color value is considered OFF and will be replaced with 1 (because it lies in the third range).

The following is a hand-drawn illustration of this color mapping that I drew when I was wrapping my head around it. You might find it useful.

Screen Shot 2019-01-12 at 13.02.04

By the time the browser goes over all the pixels in the image, you will have replaced a large number of red values with a small number you’ve chosen in tableValues, thus replacing smooth color changes with sudden color changes, and the image looks like it’s made of clusters or bands of colors.

The following is a live demo of applying the above code snippet to an image with a lot of red in it. By limiting the number of Reds in the pixels of the image and zeroing the reds out in some of those pixels, the image shows an overall noticeable reduction in red, especially in the bottom area of the image:

See the Pen Image Posterization 2 by Sara Soueidan (@SaraSoueidan) on CodePen.light

Changing the number of discrete values and/or changing the values themselves will change the overall result. You may want to avoid providing 0 as a value sometimes if you want to avoid having any black areas in an image. For example, if we were to revisit the sky image above, we probably don’t want to have any clusters or bands of black in the poster version of the image because it is still an image of a sky after all. We also want more than just two or three colors because having too few colors would make the image lose a lot of its visual affordance.

To create that effect, I limited the number of RGB colors to five, starting at 0.25 as the lowest value:

<filter id="posterize">
    <feComponentTransfer>
        <feFuncR type="discrete" tableValues=".25 .4 .5 .75 1" />
        <feFuncG type="discrete" tableValues=".25 .4 .5 .75 1" />
        <feFuncB type="discrete" tableValues=".25 .4 .5 .75 1" />
    </feComponentTransfer>
</filter>

You can play with the effect in the following live demo:

See the Pen Image Posterization by Sara Soueidan (@SaraSoueidan) on CodePen.light

And that’s how you posterize images (or any other content, for that matter) using SVG filters.

Final Words

I hope this article has helped demystify feComponentTransfer a little and has shown you how powerful pixel- and component-level color control can be.

In the next article, we will look at two more powerful feComponentTransfer transfer function types. We’ll take a look at how we can mimic Photoshop’s duotone image effect, and how we can control the brightness, contrast, and intensity of colors within an image using feComponentTransfer. Stay tuned.

SVG Filter Effects: Poster Image Effect with <feComponentTransfer> was written by Sara Soueidan and published on Codrops.

SVG Filter Effects: Outline Text with <feMorphology>

SVGFilterEffects_Morphology_featured

Last week, in the first post of this series on SVG filter effects, we covered the basics of SVG filters—how to create them and how to use them. We also covered a few of the most frequently used filter operations (a.k.a. filter primitives). We will be reusing a little of what we covered in the first post in this article. So, unless you’re already familiar with those, I recommend taking a few minutes to read that article before moving forward with this one.

<feMorphology> is one of my favorite SVG filter operations. It is one of the simplest operations, too, and the results of applying it to different elements are predictable most of the time.

What is Morphing?

To morph means to transform or alter the form or the shape of an object.

The morphology filter operates on the form of an object. It provides two predefined shape transformations: erosion (a.k.a thinning, or shrinking) and dilation (a.k.a. thickening, or expanding). In other words, the feMorphology primitive can be used to shrink or expand elements.

Technically speaking, both these operations operate on a pixel level, expanding a pixel into its neighboring pixels (dilate) or crumbling the neighboring pixels at the edges of the pixel being operated on (erode), while still maintaining strokes around the edge of that pixel. The amount by which a pixel is dilated, or the number of neighboring pixels used to “stretch” or “expand” a pixel upon, is determined by a radius parameter.

<feMorphology 
    in=".." result=".." 
    operator="dilate || erode" radius="">
</feMorphology>

You can think of the morphing radius as the radius of a circle or ellipse; any neighboring pixels that lie within the circle determined by this radius and starting at the input pixel then counts as a neighboring pixel and will be used in the dilation or erosion effect.

In reality, though, the radius actually defines the size of a kernel known as the structuring element and which looks more like a matrix. For now, it’s enough to think about it in terms of a small rectangle whose width and height are determined in pixels specified in the radius attribute.

Effect of erosion using a 3x3 structuring element (kernel).

To use the filter we don’t need to get into the nerdy details of what morphing does on a pixel level. Suffice it to know that you can provide one or two radius values to feMorphology that will determine the amount by which your element will be shrunk or expanded. If you provide two numbers in the radius attribute, the first one will correspond to the x-radius and the second one will determine the y-radius.

Morphing Images

When the feMorphology operation is applied to images, it results in two, usually predictable, results:

  • The image size (dimensions) get smaller if the erode operator is used, and larger if the dilate operator is used.
  • With either operator, the image looks like it’s been painted with a large painting brush, with not a lot of fine detail in it.

So, assuming we want to apply the morphing effect to an image, our code would look as simple as this:

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

In this snippet, we are eroding (shrinking) the (pixels in the) image by 3 pixels. The following image shows the result of this code. Notice how the size of the image is slightly smaller on the right:

The result (on the right) of applying the erode morphing effect to the image on the left.
The result (on the right) of applying the erode morphing effect to the image on the left.

Now, if we keep the same morph radius and change the operator from erode to dilate, the effect looks similar, but also distinctively different:

The result (on the right) of applying the dilate morph operation to the image on the left.
The result (on the right) of applying the dilate morph operation to the image on the left.

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

But in addition to the these results, probably the first thing you’ll notice is the difference in colors resulting from each of these two effects: erode produces an image that has more dark pixels, whereas dilate produces a light output. This is due to the fact that:

  • erode (the default value) sets each pixel to its darkest or most transparent neighbor, respectively for each of the R, G, B, and A channels, and
  • dilate sets each channel of each pixel to match the brightest or least transparent value from its neighbors, for each channel respectively.

All this technicality aside, applying feMorphology to images will almost always have the same result: a shrunken or expanded low-detail paint-like version of the image with either dark or light main strokes.

See the Pen feMorphology on an image by Sara Soueidan (@SaraSoueidan) on CodePen.light

When applied to single-color elements, however, such as text, feMorphology only shrinks or expands the element—no noticeable pixel color changes happen because we only have one color to work with anyway…

Adding Colored Outline to Text with feMorphology

We can currently add an outline to text in SVG using the stroke attribute on that 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>

By adding a stroke, the stroke is usually centered at the edges of the text so that half of its thickness overlaps with the text itself, making the text thinner, even when it’s not supposed to. Instead of reducing the thickness of the text to add an outline, we should be able to expand (or dilate) the text so that the thickness of the outline or stroke is added to that of the text. We can do that using feMorphology.

Unless otherwise styled, text usually comes in one color. So, applied to text, feMorphology allows us to shrink or thicken that text. Once the text is thickened using feMorphology, it can be used as input to other filter primitives which then allow us to create text outlines the way they are meant to be created.

Before we dig into how to do that, here is an image showing the difference between text with a stroke outline and an outline added using feMorphology.

Screen Shot 2019-01-08 at 13.39.46
Notice how the stroked text in the middle has become thinner after adding the stroke outline, compared to the text dilated using feMorphology.

So, let’s create a colored piece of text with an outline. We’ll take it step by step. This is the result we will be aiming for:

Screen Shot 2019-01-08 at 18.06.37

So we’ll start with an SVG containing our text and a filter that starts with a simple dilation operation. The amount you dilate the text by depends on the thickness of the outline that you want.

<svg width="900" height="200" viewBox="100 0 900 200">
    <filter id="outline">
        <feMorphology in="SourceAlpha" result="DILATED" operator="dilate" radius="4"></feMorphology>
    </filter>

    <!-- DILATED TEXT -->
    <text font-size="85px" dx="125" dy="130" font-weight="700" filter="url(#outline)">upgrade yourself</text>
</svg>

The above code will get the alpha channel of the text—which is just a black version of the text—and will thicken it by 4px. The result of the code at this point looks like this:

Screen Shot 2019-01-08 at 18.10.25

..compared to the original text which has a dark navy blue fill color:

Screen Shot 2019-01-08 at 18.11.13

In order to create the outline effect, we will layer the original text on top of the dilated text, which will leave only the edges of the dilated text (the additional 4px) visible behind the original text, thus making them look like an outline. Overlaying the text on top of its outline (the dilated text) will be achieved using feMerge. We covered feMerge in the previous article.

Another thing we want to do before we position the outline behind the text is to colorize this outline. Also similar to what we did in the previous article, we will flood the filter region area with the color we want, and then composite the color layer with the dilated text layer (our outline) using the in operator. As a result, only the parts of the flood color that intersect with the dilated text will be rendered and the color will be blended with that text, thus colorizing it. Finally, we will merge the resulting colored outline with the original text to get the result we want. Our code now 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>

        <feMerge>
            <feMergeNode in="OUTLINE" />
            <feMergeNode in="SourceGraphic" />
        </feMerge>
    </filter>

    <!-- DILATED TEXT -->
    <text font-size="85px" dx="125" dy="130" font-weight="700" filter="url(#outline)">upgrade yourself</text>
</svg>

Creating a filter effect in SVG is a matter of thinking of the final result in terms of smaller operations, and using the result of one operation as input to another, and finally merging any layers we have created to achieve the final result.

The following is a live demo of the above code:

See the Pen Colored Text Outline with feMorphology by Sara Soueidan (@SaraSoueidan) on CodePen.light

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

Knocking the Text Out

In addition to adding an outline to text by dilating its alpha channel and layering it behind the text, we can create outline-only text, a.k.a. knockout text, meaning that the inside of the text will be “carved out” so you can see the background behind it through the outline. An example of such effect might look like the text in the following GIF, which shows a background changing color, and how that background can be seen within our text. This is the demo we will be creating in this section:

bird-GIF-svg

This effect is easier to create, and the code required to make it is noticeably shorter. The main difference here is that instead of layering the source text on top of the dilated text, we will use that source text to cut out the inner parts of the dilated text. This means that only the added thickness of the dilated text will remain, while the inside will be removed, thus giving us our outline.

We can do that by compositing the source text with the dilated text. Our source text will go on top, and the dilated text will be its backdrop. Using the out composite operator, only the parts of the backdrop that do not overlap with the source layer will be rendered, which in our case means that only our outline will be rendered.

<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>
    </filter>

    <text dx="100" dy="300" filter="url(#outliner)" letter-spacing="10px">SVG Rocks</text>
</svg>

Using a nice font face, our demo now looks like this:

Screen Shot 2019-01-08 at 18.33.21

Cool. Now, what if you want to change the color of the outline? You’d have to use the feFlood primitive again and composite the Flood color with the outline. And then every time you want to change the color of the outline, you’d have to do the same over and over again. This is, admittedly, too tedious. Fortunately, there is a simpler way.

If instead of grabbing and dilating the alpha channel of the text (which is black by default) you grab the source text itself (which could have any fill color!) and dilate it, and then use the text again to carve out the inside of the dilated text, you end up with an outline that comes from the source text itself. This means that the color of that outline will always be the same as the color of the source text. And since we can define the fill color of the source text in CSS, this means that you have an outline text that is separated from its styles. (Yay separation of concerns!) You can then apply the filter to any piece of text, and change the color of that text in the CSS any time you need to, without having to tweak the filter’s 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 source graphic (the text) and dilating it-->
        <feMorphology operator="dilate" radius="8" in="SourceGraphic" result="THICKNESS" />
        
         <!-- Then use the text (the SourceGraphic) again to cut out the inside of the dilated text -->
        <feComposite operator="out" in="THICKNESS" in2="SourceGraphic"></feComposite>
    </filter>

    <text dx="100" dy="300" filter="url(#outliner)" letter-spacing="10px">SVG Rocks</text>
</svg>

In our style sheet, we can choose the outline color as well as the SVG background color. You can also choose to have an image behind the text inside the SVG. I’m using CSS animations in the code below to animate the color of the background, for no reason other than it being cool.

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;
    }
}

The above SVG filter is reusable across SVG as well as HTML. If you want to apply it to an HTML element, you can do that using the filter property; just place the filter in your HTML and “call” it in your 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 that includes an HTML heading with the filter applied to it:

See the Pen (Text) Outlines (Only) by Sara Soueidan (@SaraSoueidan) on CodePen.light

My favorite thing about this filter recipe is that it can be used as a visual enhancement. If a browser does not support SVG filters, or if it does not support CSS filters, or it does not support applying SVG filters to HTML elements, the user will get the original text without the outline/knockout effect applied to it. Oh, and the cherry on top of the cake? Both the SVG and the HTML text will be fully accessible, searchable and selectable. Yay progressive enhancement! Yay SVG!

Final Words

Using just two filter operations in SVG, you can apply an outlined text effect to your SVG or HTML text content. Place this filter in your HTML and use and reuse it as often as you need.

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

SVG Filter Effects: Outline Text with <feMorphology> was written by Sara Soueidan and published on Codrops.