I'm Dudley Storey, the author of Pro CSS3 Animation. This is my blog, where I talk about web design and development with HTML, CSS and SVG. To receive more information, including news, updates, and tips, you should follow me on Twitter or add me on Google+.

my books

Pro CSS3 Animation book coverPro CSS3 Animation, Apress, 2013

my other blogs

Massive Head CanonMassive Head Canon: Intelligent discussion of movies, books, games, and technology.

my projects

The New DefaultsThe New Defaults — A Sass color keyword system for designers.

CSSslidyCSSslidy — an auto-generated #RWD image slider. 3.8K of JS, no JQuery.

Bokeh Backgrounds with SVG and JS

Random blurred bokeh backgrounds with SVG and JavaScript

SVG is commonly associated with regular geometric designs that feature hard edges, but with a little cleverness, it can also be used to create random fuzziness.

“Bokeh” is the out-of-focus effects produced by a lens, particularly one associated with a limited depth of field. Visually, this can appear as a pleasant blur, or distorted points of light in different shapes; here, I’m using circles.

This effect has been popularly rendered (and often animated) using <canvas>, but I wanted to create a variant that could be applied as a pure background image. This meant that the blur on each of the circles had to be rendered inside the SVG itself: the CSS blur() filter does not work as one might expect on SVG elements.

Bokeh is, by its very nature, semi-random, so at the core of this effect are two quick functions that create two different kinds of randomness: one a random floating point number (to two decimal places), the other a whole number.

Random Seeds

var randomFloat = function(min,max) {
return (Math.random() * (max - min) + min).toFixed(2);
}
var randomRange = function(min,max) {
return Math.floor(Math.random()*(max-min+1)+min);
}

Coupled with these functions are a bunch of variables as upper and lower limits:

var minRad = 9,
maxRad = 15,
minCircs = 10,
maxCircs = 25,
minOpacity = 0.2,
maxOpacity = 0.8;

After some experimentation I found that best and most realistic visual results were produced using similar hues that varied in saturation and luminosity, making HSLa color an obvious choice for rendering the circles. (The a component is provided by the two opacity variables defined earlier). These choices required more variables:

var hue = 300,
minSat = 25,
maxSat = 75,
minLum = 25,
maxLum = 75;

Finally, each circle needed to be blurred:

var minBlur = .75,
maxBlur = 4,
blurVariants = 3;

Rendering the Result

While it would be completely possible to complete the effect as an actual SVG element, I opted to create the result as a string, concatenating changes as the script progressed, which made the result easier to convert into base64 later. (More on that in a moment).

var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">';

Each of the blur effects is rendered as a separate SVG filter, each with a unique id:

svg += '<defs>';
for (var i=0; i < blurVariants; i++) {
svg += '<filter id="bokeh'+i+'">';
svg += '<feGaussianBlur stdDeviation="'+randomFloat(minBlur, maxBlur)+"'></feGaussianBlur>";
svg += '</filter>';
}
svg += '</defs>";

Finally, each of the circles is rendered out using the limits set by the variables defined earlier:

for (var i=1;i < randomRange(minCircs, maxCircs);i++) {
svg += '<circle ";
svg += 'r="'+randomRange(minRad, maxRad)+'%"';
svg += 'cx="'+randomRange(0, 100)+'%"';
svg += 'cy="'+randomRange(0, 100)+'%"';
svg += 'fill="hsla('+hue+', '+randomRange(minSat, maxSat)+'%, '+randomRange(minLum, maxLum)+'%, '+randomFloat(minOpacity, maxOpacity)+')"';
svg += ' filter="url(#bokeh'+randomRange(0,blurVariants)+")';
svg += '></circle>;
}
svg += '</svg>';

Deep Background

Placing the SVG as a string for the background (background-image: url("<svg>…")) can be problematic, and has poor cross-browser compatibility. Instead, I opted to convert the SVG string into base64, and apply that as the page background via JavaScript:

var encodedData = window.btoa(svg),
url = "data:image/svg+xml;base64," + encodedData;
document.body.style.backgroundImage = "url("+url+")";

Making The Demo

To provide some user control - rather than simply refreshing the page to get a new result - I allowed the user to alter one of the variables in the demo. This was enabled by turning the SVG creation part of the script into a function, and one of the variables into reading a range input:

var hue = document.getElementById("hueangle").value;

Any changes to this slider then call the function, recreating the background with new random results:

hueangle.addEventListener("change", function() { 
    svg();
})

You can learn more about these changes by inspecting the associated CodePen demo.

This site helps millions of visitors while remaining ad-free. For less than the price of a cup of coffee, you can help pay for bandwidth and server costs while encouraging further articles.