inz.fi

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 4e29b7f77fb1a6488244c7ac0819b5eec71b2a61
parent dddee901f09274ff6540a8d8e30acc77b7ee590b
Author: Santtu Lakkala <inz@inz.fi>
Date:   Thu,  8 Sep 2016 15:02:23 +0000

CSS / SVG filters for fun and profit

Diffstat:
Aposts/css-svg-filters-for-fun-and-profit.md | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+), 0 deletions(-)

diff --git a/posts/css-svg-filters-for-fun-and-profit.md b/posts/css-svg-filters-for-fun-and-profit.md @@ -0,0 +1,78 @@ +# CSS / SVG filters for fun and profit + +Nowadays the web technologies support nice and fancy things, such as CSS filters. The basic filters are pretty nice for many interactions, like hover effects etc. However there is also support for SVG effects, which can be really complex and produce some really nice results. I wanted to share some little hacks with which I had fun. Unfortunately the browser support is not quite there yet, in my experience these work best in Firefox, but YMMV. + +First up, a very simple "bloom" filter + +<div class="p" style="font-weight: bold; filter: url(#bloom); -webkit-filter: url(#bloom); background: linear-gradient(to bottom right, #f00 0%,#ff0 50%,#0f0 100%);"><span style="font-size: 10em; color: #ffffff;">bloom</span></div> +<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0"><filter id="bloom"><feGaussianBlur in="SourceGraphic" stdDeviation="5" /><feComposite in2="SourceGraphic" operator="arithmetic" k2="0.8" k3="1" /></filter></svg> + + <svg + xmlns="http://www.w3.org/2000/svg" + width="0" + height="0"> + <filter + id="bloom"> + <feGaussianBlur + in="SourceGraphic" + stdDeviation="5" /> + <feComposite + in2="SourceGraphic" + operator="arithmetic" k2="0.8" k3="1" /> + </filter> + </svg> + +This is very simple, just blur the image and add it up with the original, this makes lighter areas "leak" and makes them look bright. + +Next up, slight variation of the previous, unsharp mask: + +<div class="p" style="font-weight: bold; filter: url(#unsharp); -webkit-filter: url(#unsharp); background: linear-gradient(to bottom right, #f00 0%,#ff0 50%,#0f0 100%);"><span style="font-size: 10em; color: #ffffff;">unsharp</span></div> +<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0"><filter id="unsharp"><feGaussianBlur in="SourceGraphic" stdDeviation="5" /><feComposite in2="SourceGraphic" operator="arithmetic" k2="-0.2" k3="1.2" /></filter></svg> + + <svg + xmlns="http://www.w3.org/2000/svg" + width="0" + height="0"> + <filter + id="unsharp"> + <feGaussianBlur + in="SourceGraphic" + stdDeviation="5" /> + <feComposite + in2="SourceGraphic" + operator="arithmetic" k2="-0.3" k3="1.3" /> + </filter> + </svg> + +Here, instead of adding the blurred version, we substract it from the original, giving areas with higher contrast some depth. + +Next up an homage to simpler times: + +<div class="p" style="font-weight: bold; filter: url(#retro); -webkit-filter: url(#retro); background: linear-gradient(to bottom right, #f00 0%,#ff0 50%,#0f0 100%);"><span style="font-size: 10em; color: #ffffff; text-shadow: #000 4px 0 0, #000 0 4px 0, #000 -4px 0 0, #000 0 -4px 0;">8-bit</span></div> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="0" height="0"><filter id="retro" x="0%" y="0%" width="100%" height="100%"><feImage width="2" height="2" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAEklEQVQI12P4/5/hPwMDA4QAACfmA/2h2gQ5AAAAAElFTkSuQmCC" /><feTile /><feDisplacementMap in="SourceGraphic" xChannelSelector="G" yChannelSelector="R" scale="1" result="pxl" /><feColorMatrix type="matrix" in="pxl" values="1 0 0 0 -0.25 0 1 0 0 -0.25 0 0 1 0 -0.25 0 0 0 1 0" /><feColorMatrix type="matrix" values="10000 0 0 0 0 0 10000 0 0 0 0 0 10000 0 0 0 0 0 1 0" /><feColorMatrix type="matrix" values="0.5 0 0 0 0 0 0.5 0 0 0 0 0 0.5 0 0 0 0 0 1 0" result="halftones" /><feColorMatrix type="matrix" in="pxl" values="1 0 0 0 -0.75 0 1 0 0 -0.75 0 0 1 0 -0.75 0 0 0 1 0" /><feColorMatrix type="matrix" values="10000 0 0 0 0 0 10000 0 0 0 0 0 10000 0 0 0 0 0 1 0" /><feComposite in2="halftones" operator="arithmetic" k2="1" k3="1" /></filter></svg> + + <!-- Pixelize the image using a 2x2 pixel image and displacement map --> + <feImage width="2" height="2" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAEklEQVQI12P4/5/hPwMDA4QAACfmA/2h2gQ5AAAAAElFTkSuQmCC" /> + <feTile /> + <feDisplacementMap in="SourceGraphic" xChannelSelector="G" yChannelSelector="R" scale="1" result="pxl" /> + + <!-- Map all color channel values <0.25 to 0, and >0.33 to 0.5 --> + <feColorMatrix type="matrix" in="pxl" values="1 0 0 0 -0.33 0 1 0 0 -0.33 0 0 1 0 -0.33 0 0 0 1 0" /> + <feColorMatrix type="matrix" values="10000 0 0 0 0 0 10000 0 0 0 0 0 10000 0 0 0 0 0 1 0" /> + <feColorMatrix type="matrix" values="0.5 0 0 0 0 0 0.5 0 0 0 0 0 0.5 0 0 0 0 0 1 0" result="halftones" /> + + <!-- Map all color channel values <0.66 to 0 and >0.75 to 1 --> + <feColorMatrix type="matrix" in="pxl" values="1 0 0 0 -0.66 0 1 0 0 -0.66 0 0 1 0 -0.66 0 0 0 1 0" /> + <feColorMatrix type="matrix" values="10000 0 0 0 0 0 10000 0 0 0 0 0 10000 0 0 0 0 0 1 0" /> + + <!-- Add the two together --> + <feComposite in2="halftones" operator="arithmetic" k2="1" k3="1" /> + +This effect uses displacement map filter to make the image 2x2 pixel squares, for the retro feeling and abuses color matrix result clamping to reduce colors so that channel values can be only 0, 0.5 or 1, i.e. 27 different colors. This quantization can also be achieved using feComponentTransfer with big lookup tables for each feFunc[RGB]. + +And last, probably also the least, even simpler times: + +<div class="p" style="font-weight: bold; filter: url(#ascii); -webkit-filter: url(#ascii); background: linear-gradient(to bottom right, #600 0%,#660 50%,#060 100%);"><span style="font-size: 10em; color: #ffffff; text-shadow: #000 4px 0 0, #000 0 4px 0, #000 -4px 0 0, #000 0 -4px 0;">ascii</span></div> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="0" height="0"><filter id="ascii"><feColorMatrix type="matrix" in="SourceGraphic" 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" /><feGaussianBlur stdDeviation="2" result="smoothed" /><feImage width="8" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQCAMAAAAcVM5PAAABg1BMVEW08ACl8ACW8ACH8AB48ABp8ABa8ABL8AC04QCl4QCW4QCH4QB44QBp4QBa4QBL4QC00gCl0gCW0gCH0gB40gBp0gBa0gBL0gC0wwClwwCWwwCHwwB4wwBpwwBawwBLwwC0tACltACWtACHtAB4tABptABatABLtAC0pQClpQCWpQCHpQB4pQBppQBapQBLpQC0lgCllgCWlgCHlgB4lgBplgBalgBLlgC0hwClhwCWhwCHhwB4hwBphwBahwBLhwC0eACleACWeACHeAB4eABpeABaeABLeAC0aQClaQCWaQCHaQB4aQBpaQBaaQBLaQC0WgClWgCWWgCHWgB4WgBpWgBaWgBLWgC0SwClSwCWSwCHSwB4SwBpSwBaSwBLSwC0PAClPACWPACHPAB4PABpPABaPABLPAC0LQClLQCWLQCHLQB4LQBpLQBaLQBLLQC0HgClHgCWHgCHHgB4HgBpHgBaHgBLHgC0DwClDwCWDwCHDwB4DwBpDwBaDwBLDwD///9m3j+6AAAAEUlEQVQIHWNhYIQAFo5BwQAAVEABKTNM4qgAAAAASUVORK5CYII=" /><feTile result="pixelate-map" /><feDisplacementMap in="smoothed" in2="pixelate-map" xChannelSelector="R" yChannelSelector="G" scale="17" /><feDisplacementMap in2="pixelate-map" xChannelSelector="R" yChannelSelector="G" scale="17" /><feDisplacementMap in2="pixelate-map" xChannelSelector="R" yChannelSelector="G" scale="17" result="pxltd" /><feImage width="8" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQEAAAAABecb3iAAAAIUlEQVQY02NgwAQqKjNnKiqiCFlb19SgqcrJjoxkGHYAAELnBLSnWvRcAAAAAElFTkSuQmCC" /><feTile result="raster0" /><feColorMatrix type="matrix" in="pxltd" values="1 0 0 0 -0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" /><feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 10000 0 0 0 0" /><feComposite in2="raster0" operator="arithmetic" k1="1" result="rastered0" /><feImage width="8" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQCAAAAAAO4WGhAAAAFElEQVQI12NgoB5wMoUyZtWRbQYAYAEBkKdf+CEAAAAASUVORK5CYII=" /><feTile result="raster1" /><feColorMatrix type="matrix" in="pxltd" values="1 0 0 0 -.07692307692307692307 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" /><feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 10000 0 0 0 0" /><feComposite in2="raster1" operator="arithmetic" k1="1" /><feComposite in2="rastered0" operator="over" result="rastered1" /><feImage width="8" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQBAAAAADLEYygAAAAEUlEQVQIHWPADzg6OgQYSAEANzABKTU1USkAAAAASUVORK5CYII=" /><feTile result="raster2" /><feColorMatrix type="matrix" in="pxltd" values="1 0 0 0 -.15384615384615384615 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" /><feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 10000 0 0 0 0" /><feComposite in2="raster2" operator="arithmetic" k1="1" /><feComposite in2="rastered1" operator="over" result="rastered2" /><feImage width="8" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQCAAAAAAO4WGhAAAAHElEQVQI12NgIBHMqoMynEyhDEE+dCk9NQZyAQD2HAMbtDOg1AAAAABJRU5ErkJggg==" /><feTile result="raster3" /><feColorMatrix type="matrix" in="pxltd" values="1 0 0 0 -.23076923076923076923 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" /><feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 10000 0 0 0 0" /><feComposite in2="raster3" operator="arithmetic" k1="1" /><feComposite in2="rastered2" operator="over" result="rastered3" /><feImage width="8" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQEAAAAABecb3iAAAAQklEQVQY02NgoATw8TEzowiUlkpIwDmMjAEB+fkcHFAuL6+iYne3ggKUy8paULB0CZLe/PyqKh4eJIG5c6WkGOgMAPanCgq6R6xkAAAAAElFTkSuQmCC" /><feTile result="raster4" /><feColorMatrix type="matrix" in="pxltd" values="1 0 0 0 -.30769230769230769230 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" /><feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 10000 0 0 0 0" /><feComposite in2="raster4" operator="arithmetic" k1="1" /><feComposite in2="rastered3" operator="over" result="rastered4" /><feImage width="8" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQEAAAAABecb3iAAAAKUlEQVQYV2NgoBqIiPD0RBGYNauuDs5RVm5t3bJ5zerWVkFB7CoGBQAAB1oK3VtewAEAAAAASUVORK5CYII=" /><feTile result="raster5" /><feColorMatrix type="matrix" in="pxltd" values="1 0 0 0 -.38461538461538461538 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" /><feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 10000 0 0 0 0" /><feComposite in2="raster5" operator="arithmetic" k1="1" /><feComposite in2="rastered4" operator="over" result="rastered5" /><feImage width="8" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQEAAAAABecb3iAAAAQ0lEQVQYV2NgoA0wNl6ypLt77hxvb6hAZOS0aeLisrL29uzsYAEOjtTUSZPKyri5oSp6e319mZjCw+fOFRTEbgYdAACVuhK7GQqOdQAAAABJRU5ErkJggg==" /><feTile result="raster6" /><feColorMatrix type="matrix" in="pxltd" values="1 0 0 0 -.46153846153846153846 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" /><feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 10000 0 0 0 0" /><feComposite in2="raster6" operator="arithmetic" k1="1" /><feComposite in2="rastered5" operator="over" result="rastered6" /><feImage width="8" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQEAAAAABecb3iAAAAMklEQVQY02NgoA0wNl6ypLt77hxvb6jAgQNRUQwMTk4HDkhIECkQHQ0xIyeHnZ2BTgAA+kIW/wRHjE4AAAAASUVORK5CYII=" /><feTile result="raster7" /><feColorMatrix type="matrix" in="pxltd" values="1 0 0 0 -.53846153846153846153 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" /><feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 10000 0 0 0 0" /><feComposite in2="raster7" operator="arithmetic" k1="1" /><feComposite in2="rastered6" operator="over" result="rastered7" /><feImage width="8" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQEAAAAABecb3iAAAAPElEQVQY02NgoA04cCAqioHByenAAQkJcgWio5cs6e6eOwcuAAIsLGlpCxYICoI53UDQ1ztxopgYtdwNAGDwHmlk9WmSAAAAAElFTkSuQmCC" /><feTile result="raster8" /><feColorMatrix type="matrix" in="pxltd" values="1 0 0 0 -.61538461538461538461 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" /><feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 10000 0 0 0 0" /><feComposite in2="raster8" operator="arithmetic" k1="1" /><feComposite in2="rastered7" operator="over" result="rastered8" /><feImage width="8" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQEAAAAABecb3iAAAAQElEQVQY02NgIBcUFBw44O2NJMDDgyTAz99QP2sWksDaNZmZQkJIAgcOREWhaMEQMDZesqSp0clpwQJBQQY6AQCtCRtMKr6BCQAAAABJRU5ErkJggg==" /><feTile result="raster9" /><feColorMatrix type="matrix" in="pxltd" values="1 0 0 0 -.69230769230769230769 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" /><feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 10000 0 0 0 0" /><feComposite in2="raster9" operator="arithmetic" k1="1" /><feComposite in2="rastered8" operator="over" result="rastered9" /><feImage width="8" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQEAAAAABecb3iAAAAM0lEQVQYV2NgoA04AARRUQcO5GSzs0MFWlsWLyovP3BAQgIqEBXV1+vkhF9AXR1JgA4AAE65HEKtdujQAAAAAElFTkSuQmCC" /><feTile result="raster10" /><feColorMatrix type="matrix" in="pxltd" values="1 0 0 0 -.76923076923076923076 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" /><feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 10000 0 0 0 0" /><feComposite in2="raster10" operator="arithmetic" k1="1" /><feComposite in2="rastered9" operator="over" result="rastered10" /><feImage width="8" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQCAAAAAAO4WGhAAAAKElEQVQI12NgwAMOZLE5HZAAMQ5shTGAAMyIUoAxmEKgDAYnVAY5AACBUhL0e3tCkgAAAABJRU5ErkJggg==" /><feTile result="raster11" /><feColorMatrix type="matrix" in="pxltd" values="1 0 0 0 -.84615384615384615384 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" /><feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 10000 0 0 0 0" /><feComposite in2="raster11" operator="arithmetic" k1="1" /><feComposite in2="rastered10" operator="over" result="rastered11" /><feImage width="8" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQCAAAAAAO4WGhAAAAIElEQVQIW2NgwAMOZHOEH5AAMUAAlREl4gRlMOBikAMAjzAS9w9UXYcAAAAASUVORK5CYII=" /><feTile result="raster12" /><feColorMatrix type="matrix" in="pxltd" values="1 0 0 0 -.92307692307692307692 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" /><feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 10000 0 0 0 0" /><feComposite in2="raster12" operator="arithmetic" k1="1" /><feComposite in2="rastered11" operator="over" result="rastered12" /></filter></svg> + +Check the source if you want to see how it is made, essentially using the same tricks as in the retro filter, but making the tiles 8x16 and instead of using the quantized color values, use images of 8x16 console font characters to get a ascii art-ish result. This can be made pixel-perfect on firefox, but to have it also working in chrom(ium) at least somehow, some characters appear distorted.