Effects for the Web!

by CSS Design HTML5 Internet Explorer

Hey there…

This post is 974 days old. It was written on 27.12.2011. Please make sure to be careful with the information provided and check a more recent source on this topic.

Christian “Schepp” Schaefer shows how to use the new filter-properties for CSS3 and combine them with methods implemented by most modern browsers. This article was first published in German on December 19th 2011.

In the late nineties CSS 2.1 brought us a basic set of good-enough tools to finally get table-free layouts en route. Then came CSS3 which started off by providing us with more creative tools to carve out the details. Today we finally have embeddeable fonts, rounded corners, gradients, semitransparent elements and colors, box and text shadows and so on and so forth.

Yet, comparing our toolset with that of an image editor like Photoshop we still discover a lot to desire. In Photoshop, for example, we have possibilities to desaturate parts of an image, or to sharpen or blur them. How might we need that for the web? Well, desaturating or blurring an area of a web page might be a means of directing your visitor’s attention to where you want it to be. Or it might help your visitor to concentrate better on an area, which you left untouched. Such a case might be magnifying pictures of a gallery:

A sharpen effect on the other hand might be useful when you scale down images in the browser, since without counter-measures you lose a lot of image details.

That said, it would be nice if we had such effects and could apply them the same way we do apply opacity to tranparentize a whole area. Alas, we don’t.

The status quo

Instead, to replicate a desaturation/grey scaling we are currently forced to cycle “by hand” through the color values of every affected element with JavaScript and set them to a corresponding grey value. When we meet (background-)images or videos it gets even more complicated: Then we need to switch to HTML5 canvas to manipulate their color-values pixel by pixel and swap out the original ones. But even if we get help from libraries like CamanJS or Hoverizr, it remains a profound messy affair.

A blur effect can also be simulated, this time by using text-shadows and/or box-shadows that use the same color/background-color as the to be blurred text/box. The thing is, the illusion doesn’t work with multicolored boxes and, as before, not with images or video. This requires again some HTML5 canvas action. Sigh.

Speaking of HTML5 canvas: A very brutal approach to that blurring topic is being made by a library called blurry.js, which extracts all the content, recreates it in a canvas (partially via Cufón), blurs it there and injects the whole thing back to where the original content was.

Finally, for sharpening elements there is no CSS-based trick at all, which leaves us alone with HTML5 canvas.

SVG to the rescue

Interestingly SVG knows a huge palette of so-called filters since ages, e.g. color blending, brightness/contrast adjustments, lighting, displacement mapping, gaussian and motion blur, clouds, noise, sharpen et cetera.

Now, what we might try, since SVG is more and more widely supported, is to not put our content into HTML, but instead to put it into an SVG which we then inline or embed into out HTML “frame”. Inside that SVG we are then able to apply any effect we like to our contents. The drawback is that a HTMLer won’t be too keen on switching to SVG markup like the following:

<text font-family="Arial" font-weight="900" font-size="40" x="20" y="55%">SVG Example</text>

Something way cooler is an SVG thing called ForeignObject/xlink, which allows us to embed foreign objects or areas of foreign markup inside an SVG. Think of it like inlining SVG into an HTML5 document, but the other way ’round. Once you have embedded your stuff, you can apply filters to it like you can for any other area of your SVG. You might embed and filter a full HTML page from A to Z, or you restrict yourself to just embedding and filtering a single bitmap image. Of course, you’d need to put the holding SVG into an HTML page again, which makes up for some Inception-like braintwister (HTML with embedded SVG with embedded HTML). Browser support of the ForeignObject is quite good. Remains left IE, who will follow suite in version 10 both with SVG filters and ForeignObject.

SVG Filters in HTML via CSS

Firefox since version 3.5 goes even further. He allows you to reach out to a filter that resides inside an SVG from out your HTML document’s stylesheet and have that filter then applied to any HTML element. We could for example define a feGaussianBlur-filter in an SVG with a radius of 2px and assign it the id gaussian_blur:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="1" height="1" version="1.1" 
xmlns="http://www.w3.org/2000/svg" 
xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <filter id="gaussian_blur">
      <feGaussianBlur in="SourceGraphic" stdDeviation="2" />
    </filter>
  </defs>
</svg>

Then we could reference that filter by the SVG’s file name and the filter’s id in our styles and have it applied to every image:

img {
  filter: url(blur.svg#gaussian_blur);
}

You find a live-demo here. Alas, as I said, this just works with Firefox.

Thankfully this is not the end of the story. IE also knows filters, although not SVG-based, but instead based on early incarnations of the Windows graphics library DirectX. Amongst those filters are not only the well known alpha oder gradient filters that we use to fix bugs or to replicate CSS3 features. There are also filters that are quite similar to many of the interesting SVG filters:

/* blur by 2 pixels */
filter: progid:DXImageTransform.Microsoft.Blur(pixelradius=2);
/* 13 pixel motion blur rotated to an angle of 310° */
filter: progid:DXImageTransform.Microsoft.MotionBlur(strength=13, direction=310);
/* grey scale / desaturate */
filter: gray;
/* Röntgen effect (inverted grey scale) */
filter: xray;
/* light cone */
filter: light();
/* emboss */
filter: progid:DXImageTransform.Microsoft.emboss();

You find a full list of those visual filters here.

So, with the help of conditional comments we can now already serve certain effects to two browser families, which together make up a considerable amount of the browser market:

<!DOCTYPE HTML>
<!--[if lte IE 9]> <html class="ie" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--> <html lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<title>Blur via CSS</title>
<style>
img {
  filter: url(blur.svg#gaussian_blur);
}
.ie img {
  margin: -2px;
  filter: progid:DXImageTransform.Microsoft.blur(pixelradius=2);
  zoom: 1;
}
</style>
</head>
<body>
  <img src="stadt.jpg"
   alt="Some rights reserved by zigazou76"
   width="500" height="333">
</body>
</html>

margin: -2px counters the increased dimensions of the filtered element in IE. zoom: 1 is needed for most filters to work in IE6/7. Sad fact: IE10 won’t support filters anymore :(

Here you find the extended example

As people liked Firefox’s idea to open up SVG filters for use with HTML, those people formed a new task force in the W3C called W3C FX Task Force whose purpose it got to standardize and even simplify usage of SVG filters for use in all browsers. Because Firefox already pioneered filter effects the first draft didn’t take too long to appear under the label of W3C Filter Effects 1.0. The mechanics proposed in that draft work almost like in Firefox, but with the addition of offering shortcuts to some of the most interesting filters. Filters that have a shortcut defined work without the help of an external SVG. They are sort of permanently hard wired inside browser engine. Those are the filters with shortcuts:

Another advantage of the shortcut filters is that they are animateable via CSS transitions or animations:

.foo {
  filter: blur(2px);
  transition: filter 1s ease-in-out;
}
 
.foo:hover {
  filter: blur(0);
}

This would be much harder and less efficient to do within an SVG.

Finally the filter effects are planned to be extended with OpenGL/WebGL vertex and fragment shaders. Vertex shaders will let you span a 2D mesh over an element and then let you distort the object by moving all the crossing points of the mesh following a mathematical formula of your choice. Fragment shaders on the other hand will allow you to do such mathematical processing on the colors of every pixel of that element. And all of it, on top, hardware accelerated on your graphics card.

Conveniently last week saw the first implementation of those filters in the WebKit-Nightlies, from were they quickly spawned over to Chrome Canary. That means that within a 3 months timeframe CSS filter effects will be available in your everyday Chrome browser and probably not too much later also in Safari. And this means that we are not too far away from being able to serve some flavor of CSS filters to 90% – 95% of all browsers on the market!

Returning to our code example, all that we need to add for WebKit now is a simple -webkit-filter: blur(2px); which for once we need to put after the unprefixed filter-property. The reason is that once WebKit will support an unprefixed filter-property that Firefox-specific syntax in the last line would kill our CSS transition, which we do apply in order to fade nicely from blurred to unblurred and back via -webkit-transition: -webkit-filter 1s ease-in-out:

<!DOCTYPE HTML>
<!--[if lte IE 9]> <html class="ie"> <![endif]-->
<!--[if gt IE 9]><!--> <html> <!--<![endif]-->
<head>
<meta charset="utf-8">
<title>Blur via CSS</title>
<style>
img {
  filter: url(blur.svg#gaussian_blur);
  -webkit-filter: blur(2px);
  -webkit-transition: -webkit-filter 1s ease-in-out;
}
img:hover {
  filter: none;
  -webkit-filter: blur(0);
}
.ie img {
  margin: -2px;
  filter: progid:DXImageTransform.Microsoft.blur(pixelradius=2);
  zoom: 1;
}
.ie img:hover {
  margin: 0;
  filter: none;
}
</style>
</head>
<body>
  <img src="stadt.jpg"
   alt="Some rights reserved by zigazou76"
   width="500" height="333">
</body>
</html>

And here is the final example.

And that’s it, filter effects for everyone! Let 2012 be the year of the CSS filters and have fun playing with’em.

Christian ”Schepp” Schaefer is a freelance web developer living in Düsseldorf, Germany. He is the author of CSS-JS-Booster, co-author of Turbine CSS Framework, one hosts of the (German) Working Draft Podcast and he follows your tweets with his account derSchepp.


Jeremie

Thank you for this awesome crystal clear article :)

It’s so interesting that I would like to translate it into French (I’m French). Would you allow me to do it?


Christian "Schepp" Schaefer

Of course! Go ahead. If you’d be so kind to link to this article, that’d be great.


David Murdoch

Have you played around with background-image:-moz-element(#elementID) at all?

I hacked with it a bit to blur elements in Firefox and it creates an okay effect: http://blog.vervestudios.co/blog/post/2011/04/15/Applying-Gaussian-Blur-to-HTML-elements-with-CSS.aspx


axel

good read, thanks!

looking forward to the day, where you can mask a part of your page and blur it with ease.

if you find a solution, where i could just blur parts of my svg (not elements but areas that blur everything behind with gauss), looking like a milky glass or something like that..

thats the only thing, that i think is not achievable, if the svg in the back gets panned and zoomed


jonathan

I used your way for my background but If I need to write text and all the text is blurred too. What can I do ?


Christian "Schepp" Schaefer

Hi Jonathan,

a blur filter blurs the whole object with everything contained. If you need sharp text on top of it, you need to put the text into a seperate layer which overlays the blurred one, e.g. using absolute positioning.


Adam

Am I missing something? You examples don’t work in IE9 as far as I can tell. I even tried different browser modes with no luck.


Christian "Schepp" Schaefer

Hi Adam,

probability is high that you forgot to set the class=”ie” within a conditional comment on the html-element, as show above. IE9 definitely works (as opposed to IE10, where MS sadly kicked the old filters out of).


Kasper B

Hi Christian, Very interesting article! After much research it seems like this is one of the only few cross-browser ways to archieve gaussian blur on elements. I have a question though. Your example shows how to add the blur effect to one element only. What would be the most efficient way to do a full page blur EXCEPT for one element (e.g. blur whole page content and keep modal dialog sharp)? The usual trick when to overlay the page with a color (usually used with modals) is to use an empty div with opacity and a background color. However, if blur is added to an empty div in the same way it does not affect the elements behind the div causing this method to be useless. Any suggestions?


Christian "Schepp" Schaefer

Hi Kasper,

thanks! In the meanwhile I implemented those techniques into a JavaScript polyfill, which is less painfull: https://github.com/Schepp/CSS-Filters-Polyfill

Regarding your question: As you already found out it does not work the same way as a colored overlay. What you would need to do is put all of your page’s content into a separate container below and blur that, and append the element that should not be blurred (let’s say a modal/dialog) as second child to the :

... ...


Christian "Schepp" Schaefer

Arg, my code example was stripped out… :(


Christian "Schepp" Schaefer

Hi Kasper,

my implementation should be at least as fast as the others. And the modal needs to live outside the blurred div-wrapper – otherwise it will be blurred as well.

Regards!


Leave a Comment

Remember what your mother told you: Be friendly. Your email address will not be published.
Markdown-Goodies available.