There are a lot of articles out there regarding using SVG’s feColorMatrix
with CSS filters to get a “duotone” or “Instagram” effect on photos, but frankly most of the examples looked too weird to me, and the matrix multiplication that’s going on is pretty hard to wrap my brain around.
Usually what I want is a simple monochrome duotone effect; in other words, the Belle & Sebastian effect:
Most of these are a “dark” duotone effect, where the blacks remain black and the whites become the desired color.
There’s also a “light” duotone effect, which does the reverse:
Here the whites remain white and the blacks turn into the desired color.
How do we do that with feColorMatrix
? The best explanation that I’ve seen of how the filter works is probably from A List Apart, but I think it gets the grayscale stuff wrong. For instance, in all its grayscale examples, it only maintains the brightness of a single color channel, rather than averaging them out.
Instead, what we need to start with is a “true” grayscale, where all color channels are multiplied by the brightness value of all other color channels equally, and in such a way that no channel has a value greater than 1
. What this means is starting with something like this:
<filter id="grayscale">
<feColorMatrix
type="matrix"
values="0.33 0.33 0.33 0 0
0.33 0.33 0.33 0 0
0.33 0.33 0.33 0 0
0 0 0 1 0 "/>
</filter>
Then, to “colorize” the whites — for a “dark”/If You’re Feeling Sinister duotone effect — we need to subtract the color channels we don’t want. So, for instance, if we want our whites to turn red, we remove the green and blue channels by changing their “multiplier” value to -1
:
<filter id="grayscale">
<feColorMatrix
type="matrix"
values="0.33 0.33 0.33 0 0
0.33 0.33 0.33 0 -1
0.33 0.33 0.33 0 -1
0 0 0 1 0 "/>
</filter>
If instead we want a “light” duotone, where whites remain whites and blacks change to our desired color, we increase the multiplier of the color we want, rather than decreasing the multiplier for the colors we don’t. So, for green:
<filter id="grayscale">
<feColorMatrix
type="matrix"
values="0.33 0.33 0.33 0 0
0.33 0.33 0.33 0 1
0.33 0.33 0.33 0 0
0 0 0 1 0 "/>
</filter>
If you need a color that isn’t pure red, green, or blue, use values that are the ratio of your desired color’s R, G, and B values to 255. So, for rebeccapurple
, which is RGB(102, 51, 153)
, we would do:
<filter id="grayscale">
<feColorMatrix
type="matrix"
values="0.33 0.33 0.33 0 0.4
0.33 0.33 0.33 0 0.2
0.33 0.33 0.33 0 0.6
0 0 0 1 0 "/>
</filter>
Below is a CodePen you can play around with to see how I’m doing this. In order to affect contrast, I find it’s best to play with the “main” alpha multiplier — that 1
in the bottom-right — and to increase or decrease the 0.33
values (though they should all remain equal), but I admit that I’m mostly flailing here.
There’s also a great tool to play with these values live on a GitHub Page by @kazzkiq.
See the Pen Belle & Sebastian CSS feColorMatrix filters by Jay (@jsit) on CodePen.