Using the SVG feTurbulence Filter for Wave Effects

Nathan K.
The Startup
Published in
5 min readJan 10, 2020
A hero image manipulated with SVG filters

The front-end development space is nothing short of electrifying. It feels like every other week I find a tool, methodology or framework that intrigues me. With all of these “shiny things,” I tend to remind myself to always practice clever adoption methods.

This week, while working on an SVG animation demo, I discovered an interesting way to manipulate SVG elements using the SVG <filter> element. As a developer who specializes in interactive design and UX, this finding sparked a variety of ideas on animated transitions, hover effects and other ways to enhance the presentation of web applications.

Structuring a Basic SVG

Before I get into filters, I’d like to explain a bit about the structure of an SVG.

An SVG in its essence is an XML-based markup language for describing 2D graphics. MDN’s definition gives a good example by saying, “SVG is essential to graphics what HTML is to text”.

An extremely basic structure of an SVG document looks like this:

<svg xmlns=”http://www.w3.org/2000/svg" viewBox=”0 0 100 100">
<rect x=”10" y=”10" width=”80" height=”80" fill=”green” />
</svg>`

The SVG starts with the <svg> tag, which can carry a few attributes. Similar to other HTML tags, attributes such as “width” and “height” can be added to the <svg> tag as well.

xmlns: Stands for XML Namespace. Since SVG is XML-based, it needs to declare it’s namespace to identify its elements as SVG elements. Note, if an SVG is inside HTML it does not need this attribute. The namespace is already provided in the HTML parser.

rect: This is the shape that is being rendered within the SVG. Check out MDN for more shapes.

viewBox: The viewBox attribute defines the dimensions and position of the SVG. You can think of the viewBox as a telescope lens. You’re looking through the viewBox to see the image inside. The attribute takes a few values:

//pans within viewbox
<min-x> <min-y>
//zooms in or out of image within viewbox
<width> <height>

min-x and min-y: Moves or pans the viewBox within the scope of the SVG.

width and height: Zooms in or out of the image within the viewBox (ex. width = 100 and height = 100 views the whole image)

Filters

Now that we’ve established what a basic SVG structure looks like we can dive into filters! Just like the <rect> element which displays a rectangle, SVG’s can hold many other elements. One of those elements being a <filter>.

Filter: This is an SVG element that wraps a group of Filter Primitive Elements.

Filter Primitive Elements: Are elements that can both create effects and effect other SVG elements.

A good metaphor for this would be the act of painting a picture. The Filter is the palette that holds the paint. The filter primitives are the colors that can be mixed and added to your image. To better show this here is a good example of a basic filter from CoDrops.

<svg width="600" height="450" viewBox="0 0 600 450">
<filter id="myFilter">
<feGaussianBlur stDeviation="5"></feGaussianBlur>
</filter>
<image xlink:href="…"
width="100%" height="100%" x="0" y="0"
filter="url(#myFilter)"></image>
</svg>

In this example, they have a basic SVG element with an image and a filter element contained in it. The filter element is holding a filter primitive called `feGaussianBlur`. This filter primitive does exactly what the name says. It produces a gaussian blur on whatever element it’s mapped to within the SVG element.

Under the filter element, there is an image — yes you can put images within SVGs. The image tag has a couple of attributes. The height and width should be familiar but there are a couple of attributes that stand out.

Let’s look at this again:

<svg width="600" height="450" viewBox="0 0 600 450">
<filter id="myFilter">
<feGaussianBlur stDeviation="5"></feGaussianBlur>
</filter>
<image xlink:href="…"
width="100%" height="100%" x="0" y="0"
filter="url(#myFilter)"></image>
</svg>

xlink:href: This attribute points to the image file chosen (ex. /myImage.png).

x and y: Positions the image x-horizontally and y-vertically from its origin.

Filter: Specifies the filter effects that you want on your image based on the filter’s id.

There are many other filters besides `feGaussianBlur` that you can use in tandem with others to create some interesting effects.

My Demo

feTurbulence filter mapped to an image

Now you’ve learned a little about how filters can be used to affect images, shapes or other elements within your SVG. The kicker is that these filter primitives all come with their own attributes. The real power comes from your ability to change filter primitive values using javascript, CSS or the SVG animate element.

For my demo, I wanted to give an image a “liquid-like” warped animation (no coincidence to my company name). I will eventually use that animation to warp images during the hover state.

A Quick Fact

I began doing some research on the types of filter primitives and came across the feTurbulence Filter. The feTurbulence filter produces a cloudy effect on whatever it’s element is mapped to. The cool part of learning about these filters is that they don’t just apply to front-end development. The turbulence filter derives from a computer graphics algorithm that generates a type of gradient noise. The type of noise was dubbed, Perlin Noise named after the inventor Ken Perlin. The algorithm is now used a lot in motion picture visual effects. I only mentioned that because learning these filters can help you understand more about computer graphics in general.

Back to It!

To create my warped image effect, I decided to mess with the feTurbulence filter. Here is the static rendered SVG which was initially created dynamically via javascript.

View the filter here
https://codepen.io/lwdstudio/pen/yLyKRpW

<svg viewBox="0 0 180 100" width="1920"><filter width="100%" height="100%" x="0%" y="0%" id="noise"><feTurbulence type="turbulence" baseFrequency="0.0547184" id="turbulence" numOctaves="1" result="turbulence" seed="5"><animate id="noiseAnimate" attributeName="baseFrequency" values="0;.1;0,0" from="0" to="100" dur="10s" repeatCount="indefinite"></animate></feTurbulence><feDisplacementMap in="SourceGraphic" in2="turbulence" scale="30" xChannelSelector="R" yChannelSelector="R"></feDisplacementMap></filter><foreignObject width="100%" height="100%"><img src="/img/img1.jpg" xlink:href="data:/img/img1.jpg" width="100%" height="100%" x="0" y="0" style="filter: url(&quot;#noise&quot;);"/></foreignObject></svg>

As you can see this SVG structure looks similar to the earlier structure.

1. There is an SVG element that contains a filter and an image element.

2. The image element is wrapped within a foriegnObject tag which is used to display elements that aren’t naturally placed in the SVG Namespace.

3. The Filter takes in two filter primitives, feTurbulence which creates the Perlin Noise effect mentioned earlier, and the feDisplacementMap which takes in two inputs (this being the filter and the image) and outputting the sum of the two. If your a Dragon Ball Z fan, think of it as the act of fusing two things. Your filter is referenced within the in2 attribute and the other output (in this case the image) is referenced in the in output.

4. Within the feTurbulence element, I added an animate element. The attributeName attribute on the animate element targets which attribute you’d like to animate. In this case, it was the “baseFrequency” to add more noise to the filter.

SVG’s have lots of surprises which can enhance our knowledge of both web development and computer graphics. So before you reach for that animation library or plugin, checkout what SVG’s have to offer.

Happy coding!

-lwd
Like Water Design, LLC is a multidisciplinary design studio specialized in eCommerce, modern web experiences and product design

--

--

The Startup
The Startup

Published in The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +772K followers.

Nathan K.
Nathan K.

Written by Nathan K.

Owner | UI/UX Engineer @ Digital Anthro, LLC— Design studio

Responses (1)