Menu Search
Jump to the content X X
Smashing Conf Barcelona

You know, we use ad-blockers as well. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. our upcoming SmashingConf Barcelona, dedicated to smart front-end techniques and design patterns.

The Art Of SVG Filters And Why It Is Awesome

After almost 20 years of evolution, today’s web typography, with its high-density displays and support for OpenType features, is just a step away from the typographic quality of the offline world. But there’s still one field of graphic design where we still constantly fall back to bitmap replacements instead of using native text: display typography, the art of staging letters in illustrative, gorgeous, dramatic, playful, experimental or unexpected ways.

SVG Filters1
Liquid type effect (demo2)

A Case For Display Text In HTML Link

Sure, we’re able choose from thousands of web fonts and use CSS effects for type, some with wide browser support (like drop-shadows and 3D transforms) and others that are more experimental (like background-clip and text-stroke), but that’s basically it. If we want really outstanding display typography on our websites, we’ll usually embed it as an image.

Woodtype style, created purely with SVG filters.3
Woodtype, a style created purely with SVG filters (demo4)

The disadvantages of using images for type on the web are obvious: file size, lack of feasibility for frequently altered or user-generated content, accessibility, time-consuming production of assets, etc.

Further Reading on SmashingMag: Link

Wouldn’t it be great if we could style letters the same way we usually style text with CSS? Apply multiple borders with different colors? Add inner and outer bevels? Add patterns, textures and 3D-effects? Give type a used look? Use multiple colors and distorted type? Give type a distressed look?

Sophisticated SVG Filters: CSS For Type Link

Most of this is already possible: The trick is to unleash the magic of SVG filters. SVG filters (and CSS filters) are usually considered a way to spice up bitmaps via blur effects or color manipulation. But they are much more. Like CSS rules, an SVG filter can be a set of directives to add another visual layer on top of conventional text. With the CSS filter property, these effects can be used outside of SVG and be applied directly to HTML content.

A 3D vintage effect9
3D vintage effect (demo10)

Talking about filters in CSS and SVG can be a bit confusing: SVG filters are defined in an SVG filter element and are usually applied within an SVG document. CSS filters can be applied to any HTML element via the filter property. CSS filters such as blur, contrast and hue-rotate are shortcuts for predefined, frequently used SVG filter effects. Beyond that, the specification11 allows us to reference user-defined filters from within an SVG. A further point of confusion is the proprietary -ms- filter tag, which was deprecated in Internet Explorer (IE) 9 and removed when IE 10 was released.

This article mostly deals with the first case: SVG filters used in an SVG document embedded on an HTML page, but later we’ll experiment with SVG filters applied to HTML content.

Using feImage to fill text with a repeating pattern12
Using feImage to fill text with a repeating pattern (demo13)

The illustrations in this article are taken from demos of SVG filter effects applied to text. Click on any one of them to see the original (modern, SVG-capable browsers only). I call them “sophisticated” SVG filters because under the hood these filters are crafted by combining multiple effects into one output. And even though the appearance of the letters has been altered dramatically, under the hood the text is still crawlable and accessible and can be selected and copied. Because SVG filters are supported in every modern browser, these effects can be displayed in browsers beginning from IE 10.

Applying a sketch effect to text14
A sketchy text effect (demo15)

Understanding SVG filters is challenging. Even simple effects like drop-shadows require a complicated, verbose syntax. Some filers, such as feColorMatrix and feComposite, are difficult to grasp without a thorough understanding of math and color theory. This article will not be a tutorial on learning SVG filters. Instead I will describe a set of standard building blocks to achieve certain effects, but I will keep explanations to the bare minimum, focusing on documenting the individual steps that make up an effect. You will mostly read about the how; for those who want to know the why, I’ve put a reading list at the end of this article.

Variations of posterized text effects16
Some variations of posterized text effects (demo17)

Constructing A Filter Link

Below is a sophisticated SVG fiter in action. The output of this filter is a weathered text effect, and we will use this for a step-by-step walkthrough:

Making text look grungy18
A grungy wall painting (demo19)

Let’s break down this effect into its building blocks:

  1. green text;
  2. red extrusion;
  3. text and extrusion are separated by a transparent gap;
  4. text has a grungy, weathered look.

Our SVG filter effect will be constructed by combining multiple small modules, so-called “filter primitives.” Every building block is constructed from a set of one or more primitives that are then combined into a unified output. This process is easier to understand when shown as a graph:

Image of an SVG filter graph

The processing steps that make up a sophisticated filter are illustrated best in a graph.

Adding A Filter Link

We’ll start with a boilerplate SVG that contains an empty filter and text:

<svg version="1.1" id="Ebene_1" xmlns="" xmlns:xlink="">
    <style type="text/css">
          filter: url(#myfilter);

    <filter id="myfilter">
      <!-- filter stuff happening here -->

  <g class="filtered">
    <text x="0" y="200" transform="rotate(-12)">Petrol</text>

The filter Element Link

We have to start somewhere, and the filter tag is the element to begin with. Between its start and end tag, we will put all of the rules for transformation, color, bitmap manipulation, etc. The filter can then be applied to a target as an attribute or via CSS. The target will usually be an element inside an SVG, but later on we will learn about another exciting option: applying SVG filters to HTML elements.

A handful of attributes exist to control the filter element:

  • x and y positions (default -10%);
  • width and height (default 120%);
  • an ID, which is necessary to refer to later on;
  • filterRes, which predefines a resolution (deprecated with the “Filter Effects Module Level 120” specification);
  • relative (objectBoundingBox is the default) or absolute (userSpaceOnUse) filterUnits.

A Word on Filter Primitives Link

As we’ve learned, filter primitives are the building blocks of SVG filters. To have any effect, an SVG filter should contain at least one primitive. A primitive usually has one or two inputs (in, in2) and one output (result). Primitives exist for blurring, moving, filling, combining or distorting inputs.

The specification allows us to take several attributes of the filtered element as an input source. Because most of these do not work reliably across browsers anyway, in this article we will stick with SourceGraphic (the unfiltered source element with colors, strokes, fill patterns, etc.) and SourceAlpha (the opaque area of the alpha channel — think of it as the source graphic filled black), which do have very good browser support.

How To Thicken The Input Text Link

The first filter primitive we will get to know is feMorphology, a primitive meant to extend (operator="dilate") or thin (operator="erode") an input — therefore, perfectly suited to creating outlines and borders.

Here is how we would fatten the SourceAlpha by four pixels:

Source fattened by 4 pixels

Source fattened by four pixels
<feMorphology operator="dilate" radius="4" in="SourceAlpha" result="BEVEL_10" />

Creating an Extrusion Link

The next step is to create a 3D extrusion of the result from the last primitive. Meet feConvolveMatrix. This filter primitive is one of the mightiest and most difficult to grasp. Its main purpose is to enable you to create your own filter. In short, you would define a pixel raster (a kernel matrix) that alters a pixel according to the values of its neighbouring pixels. This way, it becomes possible to create your own filter effects, such as a blur or a sharpening filter, or to create an extrusion.

Here is the feConvolveMatrix to create a 45-degree, 3-pixel deep extrusion. The order attribute defines a width and a height, so that the primitive knows whether to apply a 3×3 or a 9×1 matrix:

Using feConvolveMatrix to create an extrusion on the fattened input
Using feConvolveMatrix to create an extrusion on the fattened input
<feConvolveMatrix order="3,3" kernelMatrix=
   "1 0 0 
   0 1 0
   0 0 1" in="BEVEL_10" result="BEVEL_20" />

Be aware that IE 11 and Microsoft Edge (at the time of writing) cannot handle matrices with an order greater than 8×8 pixels, and they do not cope well with multiline matrix notation, so removing all carriage returns before deploying this code would be best.

The primitive will be applied equally to the left, top, right and bottom. Because we want it to extrude only to the right and bottom, we must offset the result. Two attributes define the starting point of the effect, targetX and targetY. Unfortunately, IE interprets them contrary to all other browsers. Therefore, to maintain compatibility across browsers, we will handle offsetting with another filter primitive, feOffset.

Offsetting Link

As the name implies, feOffset takes an input and, well, offsets it:

<feOffset dx="4" dy="4" in="BEVEL_20" result="BEVEL_30"/>

Cutting Off the Extruded Part Link

feComposite is one of the few filter primitives that take two inputs. It then combines them by applying a method for composing two images called Porter-Duff compositing. feComposite can be used to mask or cut elements. Here’s how to subtract the output of feMorphology from the output of feConvolveMatrix:

Cutting off the fattened 1st primitive from the extrusion
Cutting off the fattened first primitive from the extrusion
<feComposite operator="out" in="BEVEL_20" in2="BEVEL_10" result="BEVEL_30"/>

Coloring the Extrusion Link

This is a two-step process:

First, we create a colored area with feFlood. This primitive will simply output a rectangle the size of the filter region in a color we define:

<feFlood flood-color="#582D1B" result="COLOR-red" />

We then cut off the transparent part of BEVEL_30 with one more feComposite:

Coloring the extrusion
Coloring the extrusion
<feComposite in="COLOR-red" in2="BEVEL_30" operator="in" result="BEVEL_40" />

Mixing Bevel and Source Into One Output Link

feMerge does just that, mix bevel and source into one output:

Bevel and source graphic mixed into one output
Bevel and source graphic mixed into one output
<feMerge result="BEVEL_50">
   <feMergeNode in="BEVEL_40" />
   <feMergeNode in="SourceGraphic" />

Looks pretty much like the desired result. Let’s make it a little more realistic by giving it a weathered look.

Adding a Fractal Texture Link

feTurbulence is one of the most fun primitives to play with. However, it can melt your multicore CPU and make your fans rotate like the turbojet engines of a Boeing 747. Use it wisely, especially on a mobile device, because this primitive can have a really, really bad effect on rendering performance.

Like feFlood, feTurbulence outputs a filled rectangle but uses a noisy, unstructured texture.

We have several values on hand to alter the appearance and rhythm of the texture. This way, we can create surfaces that look like wood, sand, watercolor or cracked concrete. These settings have a direct influence on the performance of the filter, so test thoroughly. Here’s how to create a pattern that resembles paint strokes:

<feTurbulence baseFrequency=".05,.004" width="200%" height="200%" top="-50%" type="fractalNoise" numOctaves="4" seed="0" result="FRACTAL-TEXTURE_10" />

By default, feTurbulence outputs a colored texture — not exactly what we want. We need a grayscale alpha map; a bit more contrast would be nice, too. Let’s run it through an feColorMatrix to increase the contrast and convert it to grayscale at the same time:

Finally adding a fractal texture to the result
Finally, adding a fractal texture to the result
<feColorMatrix type="matrix" values=
   "0 0 0 0 0,
   0 0 0 0 0,
   0 0 0 0 0,
   0 0 0 -1.2 1.1"
   in="FRACTAL-TEXTURE_10" result="FRACTAL-TEXTURE_20" />

The last thing to do is compose the textured alpha into the letterforms with our old friend feComposite:

<feComposite in="BEVEL_50" in2="FRACTAL-TEXTURE_20" operator="in"/>


How To Apply SVG Filters To SVG Content Link

There are two methods of applying SVG filters to an SVG text element:

1. Via CSS Link

.filtered {
   filter: url(#filter);

2. Via Attribute Link

<text filter="url(#filter)">Some text</text>

Applying SVG Filters To HTML Content Link

One of the most exciting features of filters is that it’s possible to embed an SVG, define a filter in it and then apply it to any HTML element with CSS:

filter: url(#mySVGfilter);

At the time of writing, Blink and WebKit require it to be prefixed:

-webkit-filter: url(#mySVGfilter);

As easy as it sounds in theory, this process is a dark art in the real world:

  • SVG filters on HTML content are currently supported in WebKit, Firefox and Blink. IE and Microsoft Edge will display the unfiltered element, so make sure that the default look is good enough.
  • The SVG that contains the filter may not be set to display: none. However, you can set it to visibility: hidden.
  • Sometimes the size of the SVG has a direct influence on how much of the target element is filtered.
  • Did I say that WebKit, Blink and Firefox understand this syntax? Well, Safari (and its little brother mobile Safari) is a special case. You can get most of these demos working in Safari, but you will pull your hair out and bite pieces out of your desk in the process. At the time of writing, I can’t recommend using SVG filters on HTML content in the current version of Safari (8.0.6). Results are unpredictable, and the technique is not bulletproof. To make things worse, if Safari fails to render your filter for some reason, it will not display the HTML target at all, an accessibility nightmare. As a rule of thumb, you increase your chances of getting Safari to display your filter with absolute positioning and fixed sizing of your target. As a proof of concept, I’ve set up a “pop” filter effect, optimized for desktop Safari21. Applying feImage to HTML elements seems to be impossible in Safari.

Previous Demos, Applied To HTML Content Link

In these demos, the wrappers are set to contenteditable = "true for convenient text editing. (Be aware that these demos are experimental and will not work in Safari, IE or Edge.)

Structuring A Filter Link

Depending on its complexity, a filter can quickly become a messy thing. During authoring, you could be adding and removing rules and changing their order and values, and soon you’re lost. Here are some rules I’ve made for myself that help me keep track of what’s going on. People and projects vary; what seems logical and structured for me might be chaotic and incomprehensible for you, so take these recommendations with a grain of salt.

Grouping Link

I group my filter primitives into modules depending on their functionality — for example, “border,” “fill,” “bevel,” etc. At the start and end of a module, I put a comment with the name of this group.

Naming Link

A good naming convention will help you structure your filter and keep track of what’s going in and outside of a primitive. After experimenting with BEM-like schemas29, I finally settled on a very simple naming structure:


For example, you would have BEVEL_10, BEVEL_20, OUTLINE_10 and so on. I start with 10 and increment by 10 to make it easier to change the order of primitives or to add a primitive in between or to the beginning of a group. I prefer full caps because they stand out and help me to scan the source faster.

Always Declare Input and Result Link

Though not necessary, I always declare an “in” and a “result.” (If omitted, the output of a primitive will be the input of its successor.)

Some Building Blocks Link

Let’s look at some single techniques to achieve certain effects. By combining these building blocks, we can create new sophisticated filter effects.

Text Stroke Link

SVG filter outline30
<!-- 1. Thicken the input with feMorphology: -->

<feMorphology operator="dilate" radius="2" 
in="SourceAlpha" result="thickened" />

<!-- 2. Cut off the SourceAlpha -->

<feComposite operator="out" in="SourceAlpha" in2="thickened" />

This method is not guaranteed to look good. Especially when you apply dilate in conjunction with big values for radius, the result can look worse than the geometry created via stroke-width. Depending on the situation, a better alternative would be to store the text in a symbol element, and then insert it when needed via use, and thicken the instance with CSS’ stroke-width property. Be aware that stroke-width cannot be applied to HTML content, though.

Torn Look Link

Torn look31
<!-- 1. create an feTurbulence fractal fill -->

<feTurbulence result="TURBULENCE" baseFrequency="0.08"
numOctaves="1" seed="1" />

<!-- 2. create a displacement map that takes the fractal fill as an input to distort the target: -->

<feDisplacementMap in="SourceGraphic" in2="TURBULENCE" scale="9" />

Color Fill Link

Tornout look32
<!-- 1. Create a colored filled area -->

<feFlood flood-color="#F79308" result="COLOR" />

<!-- 2. Cut off the SourceAlpha -->

<feComposite operator="in" in="COLOR" in2="SourceAlpha" />

It should be mentioned that, besides feFlood, feColorMatrix is another method of altering the source input’s color, even though that concept is more difficult to grasp.

Offsetting Link

Offsetting the filtered element33
<!-- Offset the input graphic by the amount defined in its "dx" and "dy" attributes: -->

<feOffset in="SourceGraphic" dx="10" dy="10" />

Extrusion Link

Extrude the target34
<!-- Define a convolve matrix that applies a bevel. -->

<!-- Order defines the depth of the extrusion; angle is defined by the position of "1" in the matrix. Here we see a 45-degree, 4-pixel deep extrusion: -->

<feConvolveMatrix order="4,4" 
   1 0 0 0
   0 1 0 0
   0 0 1 0 
   0 0 0 1" in="SourceAlpha" result="BEVEL" />

<!-- offset extrusion: -->

<feOffset dx="2" dy ="2" in="BEVEL" result="OFFSET" />

<!-- merge offset with Source: -->

   <feMergeNode in="OFFSET" />
   <feMergeNode in="SourceGraphic" />

Noise Fill Link

Create a noise fill35

The feTurbulence filter primitive will create a noisy texture by applying the so-called Perlin noise algorithm (invented by Ken Perlin during his work on TRON in 1981). This will generate a rectangle filled with noise that looks like what you could see on old TV sets late at night before cable TV was invented.

The appearance of the noise structure can be modified by several parameters:

  • type in its default state will produce a liquid texture.
  • type can be set to fractalNoise instead, which will output a sandy result.
  • baseFrequency is there to control x and y pattern repetition.
  • numOctaves will increase the level of detail and should have a low value if performance is an issue.
  • The number to start randomization with is determined by seed.
<feTurbulence type="fractalNoise" baseFrequency="0.1" numOctaves="5" seed="2" />

Image Fill Link

Fill target with an image36

feImage‘s purpose is to fill the target with a texture. If we want to apply a repeating pattern, it must be used in conjunction with feTile.

<!-- The following code will create a 100 × 200-pixel square filled with "myfill.svg": -->

<feImage xlink:href="myfill.svg" x="0" y="0" width="100" height="200" result="IMAGEFILL"/>

<!-- We then use this fill as an input for feTile, creating a repeating pattern this way: -->

<feTile in="IMAGEFILL" resulte="TILEPATTERN"/>

<!-- Now we will use feComposite to "cut off" SourceAlpha's transparent areas from the fill: -->

<feComposite operator="in" in="TILEPATTERN" in2="SourceAlpha" />

The cool thing about this filter is that the specification allows us to use any SVG element as an input and to create a pattern fill from it. So, in theory, you could create pattern fills from symbols, groups and fragments within your SVG and then apply them as a texture, even to HTML elements. Unfortunately, because of an old bug37, Firefox accepts only external resources as input. If you prefer to keep things self-contained and want to avoid the additional HTTP request, there’s hope: Embed the pattern fill as an UTF-8 data URI:

<feImage xlink:href='data:image/svg+xml;charset=utf-8,<svg width="100" height="100"><rect width="50" height="50 /></svg>' />

Some browsers do not understand UTF-8 data URIs when they aren’t URL-encoded, so make URL encoding38 a default:

<feImage xlink:href='data:image/svg+xml;charset=utf-8,%3Csvg%20width%3D%22100%22%20height%3D%22100%22%3E%3Crect%20width%3D%2250%22%20height%3D%2250%20%2F%3E%3C%2Fsvg%3E' />

If you want to apply feImage to HTML content, be aware that size matters. The SVG that contains the filter must cover the area where it is being applied. The easiest way to achieve this is by making it an absolutely positioned child within the block element it is being applied to:

    position: relative;
    filter: url(#myImageFilter);
  h1 svg{
    position: absolute;
    visibility: hidden;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
  My Filtered Text
    <filter id="myImageFilter">…</filter>

Lighting Effect Link

3D Bevel39

This is one “Wow” effect that quickly becomes boring when used too often. This filter has a serious effect on performance, so use it wisely.

<!--We create a heightmap by blurring the source: -->

<feGaussianBlur stdDeviation="5" in="SourceAlpha" result="BLUR"/>

<!-- We then define a lighting effect with a point light that is positioned at virtual 3D coordinates x: 40px, y: -30px, z: 200px: -->

<feSpecularLighting surfaceScale="6" specularConstant="1" specularExponent="30" lighting-color="#white" in="BLUR" result="SPECULAR">
    <fePointLight x="40" y="-30" z="200" />

<!-- We cut off the parts that overlap the source graphic… -->

<feComposite operator="in" in="SPECULAR" in2="SourceAlpha" result="COMPOSITE"/>

<!-- … and then merge source graphic and lighting effect: -->

    <feMergeNode in="SourceGraphic" />
    <feMergeNode in="COMPOSITE"/>

Conclusion Link

There is a gap between pure CSS layout and custom design elements created in software such as Photoshop or Illustrator. External assets embedded as background images, icon sprites and SVG symbols will always have their place in the design of websites. But sophisticated SVG filters give us more independence from third-party design tools and bridge this gap by enabling us to create visual styles directly in the browser.

In this article we’ve seen how SVG filters help us to create playful, decorative web typography. But nothing says we have to stop here. Soon, browser support will be good enough for us to use these effects on every HTML element as easily as we use CSS today. Even though the effects behave differently from native CSS techniques (an SVG filter will affect not only an element but all its children), it will be exciting to see how inventive web designers use these techniques in the near future.

Resources From This Article Link

Reading List Link

(ds, al)

Footnotes Link

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55

↑ Back to top Tweet itShare on Facebook

Dirk Weber is a designer and frontend developer with a passion for everything related to type on the web. He has worked for clients like Nokia, Roche and Deutsche Telekom. He's either trying to avoid breaking the web while playing with some new web technology he didn't fully understand or on his way to become the greatest barrista on the planet. He occasionally blogs on

  1. 1

    Christian B. Mueller

    May 26, 2015 2:41 pm

    Just one of those reasons to love SVG. Thanks for the in depth article Dirk! The process graph clearly shows how to think while approaching the task :)

  2. 2

    Johan Ronsse

    May 27, 2015 5:49 am

    Amazing article, and brilliant research. Bookmarking this for sure.

    For some odd reason the water demo works in Safari but not in Chrome for me.

  3. 3

    Andrew Mick

    May 27, 2015 1:02 pm

    WoW! it’s just awesome art work i don’t no why SVG art of design love it. but i am very first time saw these design and i love it, these design attract you that’s the reason SVG design are popular in people. specially Liquid type effect and
    3D vintage effect are soo awesome.

  4. 4

    Martin Rinehart

    May 28, 2015 7:37 pm

    Great article.

    But to the editor: please don’t use two-space indents for code. No excuses. Wrap the long lines. (Look at the examples here. The long lines don’t benefit.) The two-space fad repeats like locusts, every 17 years. Don’t get sucked in.

  5. 5

    Various SVG filter-related code samples for frivolous fun:

  6. 6

    Giel Berkers

    May 29, 2015 11:05 am

    This is truly awesome!

  7. 7

    Awesome article Dirk, thanks for sharing it. SVG truly is awesome and not so explored, with a reason… look at the syntax, makes most of the people puke and give up.

  8. 8

    Definitely cool, but I don’t think file size is an issue with using images for words. In fact, in many cases, a font can be a larger file size than an image.

  9. 9

    This seems pretty cool, a great way to get around having to wait for a graphic designer to deliver assets when they are bogged down with other items. My concern is that a developer/UI designer could be putting a lot of time into learning and then implementing, as you say, very complex syntax to setup a very particular set of designs. How reusable are these outside coloring text and some images? Should that time go into using SVGs, or just learning Photoshop/GIMP and making them that way?

  10. 10

    Mr. Meows question is quite similar to my thoughts, though my direction differs: So, would all of that fancy stuff work using Inkscape, too?

    With that, one could skip having to learn all the non-visual codehacking first.

    cu, w0lf.

  11. 11

    Michael Mullany

    June 29, 2015 11:15 pm

    @fwolf. Inkscape has a filter editor and a bunch of built-in filter effects that can help get you started. But the filter editor is clumsy to use and it won’t allow you to do feComponentTransfer. It’s best to learn how to hand edit filters. It takes maybe a day to learn the basics.

  12. 12

    Hey, interesting article. You should know that none of these example work in the latest canary.


↑ Back to top