Using SVG with CSS3 and HTML5 — Supplementary Material

Example code and online extras for the O'Reilly Media book by Amelia Bellamy-Royds, Kurt Cagle, and Dudley Storey.

Making the Wave

In Example 16-8 in the book, we used the <feTurbulence> and <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 feTurbulence filter.

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 seed attribute.

There are actually two algorithms available, selected with the type attribute: fractalNoise or 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:

<feTurbulence y="30px" height="40px" result="waves"
              type="turbulence" baseFrequency="0.01 0.1"
              numOctaves="1" seed="53" />

Figure 16-X1 shows what the turbulence result looks like on its own, and then after some color modifications.

A rectangle filled with a wavy blue-green-clear pattern.  The clear sections are thin ribbons which mostly flow left to right, but wave up and down, merging and separating.  The appearance is somewhat like bright sunlight reflecting off rippling water under a bright blue sky.
Figure 16-X1. A turbulent Perlin noise wave pattern, and the same noise converted to blue-green colors

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 <feFlood> layer.

Tip

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 <rect>).

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”.

The 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.

Tip

The 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.

Finally, the 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.

While type, numOctaves, and 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.

Tip

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).

The <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> filter:

<feDisplacementMap in="SourceGraphic" in2="waves"
                   y="36px" height="30px"
                   scale="4"
                   xChannelSelector="G"
                   yChannelSelector="B" />

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 (B) channel.

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 <feDisplacementMap>.

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.