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.
Pro CSS3 Animation, Apress, 2013
Massive Head Canon
The New Defaults
CSSslidy