Combine The Results of SVG Filter Primitives with feMerge

Interesting SVG filter effects are produced when you combine multiple filter primitives inside a single filter. Even more interesting is when you can output the results of different primitives as layers that can be displayed together.

A couple of weeks ago I started to cover the details of SVG filter primitives as part of a larger series about SVG filter effects. I began with an overview and then walked through the filter element before talking about filter primitives and their input and output. So far I’ve talked about some simple filter primitives and a couple for working with external images.

Today and the next two weeks I want to show you how you can create multiple layers with different effects and then combine those layers to create a single composite graphic for display. I’ll talk about three different primitives that take separate images or layers and combine them into a single output. Today I’ll focus on feMerge and the next couple of weeks I’ll look at feComposite and feBlend.

The feMerge Filter Primitive

SVG filters create intermediate layers before creating a final image for output. The feMerge filter primitive places these layers one on top of another and then collapses all the layers into a single image for display.

An feMerge element contains multiple feMergeNode subelements, each with a different input, which you set via an “in” attribute.

1
2
3
4
<feMerge>
 <feMergeNode in=""/>
 <feMergeNode in=""/>
</feMerge>

The inputs can be any of the keyword values of in, such as SourceGraphic or SourceAlpha or they can be the result of any other filter primitive as long as it’s inside the same filter.

Each node is essentially a layer and they all get stacked one on top of another with the layer in the first feMergeNode on the bottom and the layer in the last feMergeNode displaying on top.

For example let’s say you want to create a drop shadow effect. A drop shadow usually takes the alpha channel of the source image, blurs it, and offsets it slightly making it look like a shadow. Sounds like something we can create with a combination of feGaussianBlur and feOffset.

Here I created a blue square and referenced a filter that blurs the SourceAlpha using feGaussianBlur and then passes the result to be offset by feOffset.

1
2
3
4
5
6
7
8
9
10
<svg width="100%" height="220" style="outline: 1px solid red">
 <defs>
   <filter id="drop-shadow"> 
     <feGaussianBlur in="SourceAlpha" stdDeviation="2" result="blur"/>
     <feOffset in="blur" dx="4" dy="4" result="offsetBlur"/>     
 </filter>
 </defs>

 <rect x="10" y="10" width="100" height="100" fill="#00f" filter="url(#drop-shadow)" />
</svg>

Here’s the result. It’s hard to tell that the graphic has been offset so I’ll ask you to trust that it has been. You should at least be able to see that the SourceAlpha has been blurred. We created a shadow, but we’re missing the original square.

One possible solution is to create a second blue square in the same location as the first. The only change to the previous example would be the addition of the second <rect> element, which comes after the filtered rectangle so it displays on top.

1
2
<rect x="10" y="10" width="100" height="100" fill="#00f" filter="url(#drop-shadow)" />
<rect x="10" y="10" width="100" height="100" fill="#00f" />

This works as you can see, but it requires two elements instead of one. It also means any changes we might want to make dynamically, we’d have to make twice, one for each element.

A better solution is to use the feMerge filter primitive. In this next example I created a single element referencing the filter. The filter takes the SourceAlpha, blurs it and then passes the result to the feOffset primitive just like the earlier example. However, this time we’ll combine the result with the original graphic.

Notice the feMerge element. Inside are two feMergeNodes, the first has the final result of the blur and offset as its “in” value. The second has the original SourceGraphic as its “in” value. The SourceGraphic will appear on top since it’s the last node in the list.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<svg width="100%" height="220" style="outline: 1px solid red">
 <defs>
   <filter id="drop-shadow-3"> 
     <feGaussianBlur in="SourceAlpha" stdDeviation="2" result="blur"/>
     <feOffset in="blur" dx="4" dy="4" result="offsetBlur"/>

     <feMerge>
       <feMergeNode in="offsetBlur"/>
       <feMergeNode in="SourceGraphic"/>
     </feMerge>
   </filter>
 </defs>

 <rect x="10" y="10" width="100" height="100" fill="#00f" filter="url(#drop-shadow-3)" />
</svg>

Here’s the result, which is the original graphic with a new drop shadow.

By using feMerge any change to the original graphic will also change the shadow produced by the filter without needing to modify a second graphic.

The following example is the same as the last one, except for the dimensions of the original element, which I changed to width=“200” and height=“150” from the initial 100px each.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<svg width="100%" height="220" style="outline: 1px solid red">
 <defs>
   <filter id="drop-shadow-4"> 
     <feGaussianBlur in="SourceAlpha" stdDeviation="2" result="blur"/>
     <feOffset in="blur" dx="4" dy="4" result="offsetBlur"/>

     <feMerge>
       <feMergeNode in="offsetBlur"/>
       <feMergeNode in="SourceGraphic"/>
     </feMerge>
   </filter>
 </defs>

 <rect x="10" y="10" width="200" height="150" fill="#00f" filter="url(#drop-shadow-4)" />
</svg>

Because the shadow created in the filter relies on the original element, there was no need to adjust the filter. Again, the only difference between this and the previous example is a change to the width and height of the source graphic.

Finally, note that while the examples in this post use only two nodes, the feMerge element can have as many nodes as you’d like.

Closing Thoughts

Think of each feMergeNode like a different layer in your favorite graphic editor. The layers sit one on top of another to produce the final image.

The main difference is that in your graphic editor the layer on the top in the layer stack is the layer that will sit on top in the overall display, while inside an feMerge primitive the last node (the one at the bottom of the list) is the one that sits on top in the final image.

That’s one way to combine the results of multiple primitives. Next week I want to talk about another, feComposite, which offers some additional ways in how the filter output is combined. The following week I’ll show you the feBlend primitive, which lets you blend two images together using some of the blend modes you’ve likely used in your favorite image editor.

Download a free sample from my book, Design Fundamentals.

Leave a Reply

Your email address will not be published. Required fields are marked *