Making the Wave
In Example 16-8 in the book, we used the
<feDisplacement> to create a distortion effect, similar to looking at a graphic through wavy water. But we skipped over how it actually worked.
The initial wave pattern is produced using the
The turbulence filter uses the Perlin noise filter, a well-established algorithm commonly used in computer graphics to generate clouds, the surface patterns in marble, and other things that should have a “random” appearance. It isn’t truly random, or even randomized: you will get the same result every time you run the filter (and approximately the same result from one browser to the next). However, you can change the result by changing the
There are actually two algorithms available, selected with the
turbulence. The two aren’t interchangeable: fractal noise generates variations from 50% gray, 50% alpha, while turbulence starts with transparent black and adds color to it. With the turbulence algorithm, the variations build upon one another to create more continuous patterns. That’s what we used in Example 16-8:
Figure 16-X1 shows what the turbulence result looks like on its own, and then after some color modifications.
In the basic
<feTurbulence> output, all the colors and the alpha channel are evenly distributed between 0 and 100%. To make the final waves actually look like water waves—and not just whispy multi-colored fog—the result is passed through a set of
<feComponentTransfer> commands, to reduce the red channel, increase the alpha channel, and modulate the blue and green channels, and then composited on a solid
In both versions of the filter used in Figure 16-X1, a final
<feComposite> step, with
operator="in", clips the generated pattern layer to the original shape outline (here, a rounded
The Perlin patterns are described in terms of two-dimensional waves. Like sound waves, the structure is characterized by a frequency and by patterns of increasing “octaves”.
baseFrequency attribute sets the number of waves per px, where a “wave” is the average distance between two maximum color values, after shifting to a minimum and back. Since you usually want your colors to change on scales much greater than 1px, the frequency numbers are usually small decimals. They can even be zero (no color change in that direction), but they can’t be negative.
baseFrequency is not affected by object bounding-box
primitiveUnits. It is affected by any
viewBox or other scaling in the user-space coordinate system.
The two values given to
baseFrequency are for the x- and y-directions. If you only give one number, it will be used for both directions. Here, we have a very low x-frequency of 0.01 (1 in 100), meaning waves will average 100px long in the horizontal direction. In contrast, the y frequency is 0.1, meaning the colors will oscilate back and forth every 10px (or so) in the vertical direction.
numOctaves attribute allows you to specify additional variation on top of the main waves. Each subsequent octave wave will have double the frequency (half the wavelength) of the previous one.
The number of octaves therefore determines the fine detail in the resulting texture: the more octaves, the more detail. But beware: the more octaves, the more computation required. Here, we use a single octave to get smooth wave patterns.
baseFrequency describe the structure of the pattern, the
seed determines which exact pattern you get. It’s value can be any number, although any decimal part of the number will be ignored.
Need a more mathematical explanation of how the colors are generated? This tutorial by Eevee is a great explanation of Perlin noise functions for many applications (although it doesn't cover the specifics of the SVG filter).
<feTurbulence> filter can be used to create all sorts of textures and color patterns within SVG. But in Example 16-8, we didn’t use the
<feTurbulence> output to create visible wave patterns. Instead, we used it to create distortion patterns in our text, by passing it as the second input to the
<feDisplacementMap> element displaces (moves) the pixels from its primary input (the
SourceGraphic in this case) according to color values extracted from the second input (the turbulent waves), which is known as the map.
The horizontal and vertical displacements are each scaled according to a different color value from the input. The
xChannelSelector attribute tells the browser to use the green (
G) channel of our turbulence to determine horizontal displacement. If the pixel in the map has 0% green, the pixel being displaced will go the maximum distance left. If it is 100% green (in
rgb() notation), it will be moved the maximum distance right. And if it is 50% green, it won’t move horizontally at all. The
yChannelSelector does the same, but for vertical offsets—in this case, using the blue (
Because the input
waves had all colors evenly distributed, the channel selections don’t really matter, except that they are different. The important part is the
scale, which sets the maximum displacement, in scaled px units. A larger scale would equal more distortion.
If you wanted more-controlled distortion effects, you could use smoothly-blurred shapes or a gradient (imported via
<feImage> as a separate image file or data URI) as the
in2 map for
If more web browsers supported the
BackgroundImage filter input, you could also distort a shape relative to its backdrop, or distort the backdrop as a shape passes over it. But (so far) that is only supported in Internet Explorer and MS Edge.