Quick summary ↬
What exactly is a displacement filter? In this article, Dirk Weber explains the SVG feDisplacementMap filter primitive with a good number of examples to demonstrate the concept of animated displacement maps.
Even today, the magic, wicked realm of before continuing. All the demos in this article have been optimized for the latest versions of the three major browser engines.
To get the most out of this article, you should already have a basic understanding of SVG filters. If you are a filter novice, you may want to take a short detour to A sneak peek of the various demo examples we’ll be taking a closer look at in this article. (Source: ) (, and most likely you already saw on film and TV, created with a VFX tool like After Effects or GiantRed.
To achieve a distortion effect the filter needs two images as input:
The actual source graphic should be distorted (from now on just “source”);
The “displacement map” (from now on just “map”). This map contains information on how we want the source to be distorted.
Most of the time, a map will be some Bitmap image, but in the next part, I will demonstrate how to use SVG images or fragments as an input.
Let’s see what happens when we use an image of Barcelona’s famous La Sagrada Familia to “distort” the Mona Lisa:
The first filter primitive is feImage which holds a reference to the map (there are other filter primitives that can be used as an input. You will find several fascinating demos out there where feTurbulence is used as displacement map, but in this article, we will mostly focus on feImage).
This feImage is then fed into a feDisplacementMap primitive where the actual distortion happens:
A positive or negative scale attribute defines the strength of the distortion.
The purpose of xChannelSelector and yChannelSelector is to determine which of the input image’s four-color channels (red, green, blue, alpha) should be applied to which axis for distortion. Both attributes default to the map’s alpha channel (which means if you’re using a map without alpha channels and omit these attributes, you’ll see nothing more than a diagonal shift of the source).
We then apply the filter with CSS:
It may be fun to play around distorting images this way but it is unpredictable how the result will look and most of the time it’s not aesthetically pleasing at all. Is there a way to get pixel-perfect control over the output? Here’s what the spec says:
This filter primitive uses the pixels values from the image from in2 to spatially displace the image from in. This is the transformation to be performed:
P'(x,y) ← P( x + scale * (XC(x,y) - .5), y + scale * (YC(x,y) - .5))
The displacement map, in2, defines the inverse of the mapping performed.
OK, looks complicated at a first glance, but it is actually pretty easy to understand when broken down:
P'(x,y) stands for the coordinates of a pixel in the result;
X and Y are the coordinates of this pixel in the unfiltered source;
XC and YC are the normalized (1/255) RGB color values of the given pixel in the map;
Finally, the result of the operation must be inverted (which basically means every + in the formula must be replaced by a -).
We will run some simple experiments to verify our formula by feeding primitive bitmaps into a filter, consisting of only one single color. Let’s say the map is filled with rgb(51, 51, 51), how would we expect the coordinates of a source pixel at x=100 / y=100 to be transformed when fed into a displacement primitive with a scale value of 100?
X: 100 - 100 * (51/255 - .5) = 130
Y: 100 - 100 * (51/255 - .5) = 130
The resulting pixel will be moved to coordinates 130⁄130. It will be shifted to 30px to the right and 30px to the bottom. What happens when we change the map to a 50% grey as in rgb(127,127, 127)?
Obviously, a neutral color doesn’t have a recognizable effect. The resulting pixels stay in place. And if we change the color values to something above 128, let’s say rgb(204, 204, 204)?
The coordinates have been shifted to 30px to the left and 30px to the top.
By now we have learned enough to sum up the inner mechanics of the displacement filter in these three simple sentences:
Any color value above 127 will shift the corresponding pixel into the direction of the scale value;
Any color value below 127 will shift the corresponding pixel into the opposite direction;
A color value of 127 will have no effect.
From intuition, one would tend to believe that the color black wouldn’t have any effect, but by now it should be clear that this isn’t the case. In fact, black and white will result in the maximum possible shift to or away from the scale value.
More after jump! Continue reading below ↓
The Absolute Map
At this point, I should introduce you to the one special map that will be the foundation for all the effects we will see from now on. It is a map that will perform a very simple distortion: scale an image proportionally. We’ll call it the identity map or the absolute map from now on.
To scale an image equally in all directions, the color values must gradually decline from a maximum at one edge to a minimum at the opposite edge. We will use red for X and blue for Y from now on, but in the end, it doesn’t matter which color you chose for x- and yChannelSelector.
In your favorite Image editor, open a new document;
Set the background color of the document to black;
Create a new layer and fill it with a left to right gradient from rgb(255, 0, 0) to rgba(255, 0, 0, 0);
Add a second layer and add a top to bottom gradient from rgb (0, 0, 255) to rgba(0, 0, 255, 0);
Set the blending mode for this layer to screen.
Et voilà, you’ve built an absolute map! This map will serve as a solid foundation for all kinds of image distortions:
By applying Photoshop-like distortion filters to this map, we are able to use these effects in CSS filters!
We can control the scaling of the x- and y-axis independently by altering the transparency of the blue or red gradient.
It is possible to “mask” parts of the map with a “neutral” color (rgb(127, 0 ,127) or #7F007F) to prevent the corresponding parts in the image from displacing.