Roll Your Own Filtered SVG


Sure, I know that SVG can be used to make straight lines, smooth curves and shapes. It's great for diagrams, icons and math textbooks, but what has it done for me lately? OK, I know that if I were a sufficiently talented artist with the right tools, I could produce an SVG like tiger.svg, but that's a big if.

Well, it turns out that there are some more tricks up SVG's sleeve. If you've ever used a photo manipulation tool like Photoshop or Gimp, you might have used some filters like blur, sharpen, emboss, edge detect, noise, &c. While SVG doesn't have a builtin "Oilify" filter, it does have some interesting and flexible possibilities. Using them is not difficult, but it can be confusing at first.

SVG is an XML format. That imposes a number of limitations. It also makes things a little verbose and tedious at times, but there are some convenient features as well. In particular, there are two tags that make a lot of things possible, "g" and "defs". Both of these tags make life a lot less tedious. The "g" tag is used to group things together, much as you would in a drawing program. By grouping them, you can apply styles and transforms to the group as a whole, so that you don't need to repeat yourself for every item. The "defs" tag provides something like a macro or variable which can be referred to with in the form of a local url. But what does that have to do with filters?

The filters are not visible objects themselves, rather they are part of a pipeline. Each filter is made up of a pipeline of individual elements, each with a specified input and output. The pipeline is enclosed in a "filter" tag.  The resulting filter can then be applied to a particular element (or group of elements, courtesy the "g" tag) by setting the "filter" attribute. For example, here's a simple blur filter:

<filter id="Blur" filterUnits="userSpaceOnUse" x="0" y="0" width="100" height="100">
  <feGaussianBlur in="SourceGraphic" stdDeviation="1" result="blur"/>

Alas, to get a handle on this, we need to take it apart, one piece at a time. First, the id. This is the name that you use to apply it to an element, or group of elements, with a url reference, i.e. "url(#Blur"). For example, if you wanted to blur a rectangle, you would say:

<rect x="10" y="10" width="161" height="100" filter="url(#Blur)"/>

After the id attribute is the "filterUnits" attribute. This indicates what units are used in the filter, particularly "x", "y", "width" and "height". If it's set to "userSpaceOnUse", just means that it's using the same units as the current element. The other option is "objectBoundingBox", which uses fractions (or percentages) current element size. The next four attributes describe a rectangular clipping region, or bounding box for the filtered result. In practice I tend to make that big enough that it doesn't clip anything, but it's good to know it's there if you need it.

Inside the filter element is a list of "filter primitives". In this case the list has only one element, "feGaussianBlur". Typically each primitive has an input ("in"), an output ("result") and some other parameters that vary depending on the primitive. When there are multiple primitives, setting the "in" of one to the "result" of another is a way of chaining the effects together, but lets table that for now and just consider single element filters like the one above. In this case, there are several possibilities for "in". The two I want to consider for the moment are "SourceGraphic" and "SourceAlpha".

"SourceGraphic" just refers to the element that the filter is on, e.g. the rect above. It will apply the filter to that element and the element is replaced by the output of the filter. If the input is "SourceAlpha", then the input is the "Alpha Channel" of the element. If you're not familiar with that terminology, it just means that everywhere that is opaque is seen as black semi-transparent is some shade of gray and transparent is transparent, if that makes sense. Again, the element is replaced by the output of the of the filter, so in this case, you'd get a blurry black rectangle, instead of one matching the color of the original. This might not seem so useful, but it is handy for doing shadows, by stacking an unfiltered element on top of a filtered one.

Finally there is the "stdDeviation" attribute. This is specific to the "feGaussianBlur" primitive and it indicates the amount of blur. If two numbers are specified then it specifies the blur in the x and y direction, respectively.

I've put some examples up on JSFiddle to play with. Next time I'll cover some more complicated filter pipelines. It's pretty cool what you can do with a collection of filters.

Get started with the Intel XDK

Download Now