Greek mythology tells the story of Zeus creating the cloud nymph, Nephele. Like other Greek myths, this tale gets pretty bizarre and X-rated. Here’s a very abridged, polite version.
Nephele, we are told, was created by Zeus in the image of his own beautiful wife. A mortal meets Nephele, falls in love with her and, together, they take an adult nap™. Finally, in a strange twist, the cloud gives birth to half-human half-horse Centaur babies.
Weird, right??Personally, I can’t make heads or tails of it. Thankfully, the process for creating clouds in the browser is much more straightforward and far less risqué.
Yuan Chuan’s clouds detail. (Demo)
Recently, I discovered that developer Yuan Chuan has realized code-generated, photorealistic clouds. For me, this notion in the browser had long been the stuff of myth.
With one glance at the code in this pen we can imagine that convincing individual clouds are achievable through the use of CSS box-shadow with a
The photorealism we want is achieved with a delicate mix of feTurbulence and feDisplacementMap. These SVG filters are powerful, complex and offer very exciting features (including an Oscar winning algorithm)! However, under the hood, their complexity can be a bit intimidating.
While the physics of SVG filters is beyond the scope of this article, there is ample documentation available on MDN and w3.org. A very informative page on feTurbulence and feDisplacement is freely available (and offered as a chapter of this amazing book).
For this article, we will focus on learning to use these SVG filters to get spectacular results. We don’t need to delve too deeply into what is happening behind the scenes algorithmically, much in the way an artist isn’t required know the molecular structure of paint to render a stunning landscape.
Instead, let’s pay close attention to small handful of SVG attributes that are essential for drawing convincing clouds in the browser. Their use will enable us to bend these powerful filters to our will and learn how to customize them with precision in our own projects.
Let’s start with some basics
The CSS box-shadow property has five values that deserve close attention:
box-shadow:
Let’s crank these values up (probably higher than what any sane developer would so that this shadow becomes a player on the stage in its own right.
(Demo)
#cloud-square {
background: turquoise;
box-shadow: 200px 200px 50px 0px #000;
width: 180px;
height: 180px;
}
#cloud-circle {
background: coral;
border-radius: 50%;
box-shadow: 200px 200px 50px 0px #000;
width: 180px;
height: 180px;
}
You’ve either made or seen shadow puppets, right?
Credit: Double-M
In the same way that a hand changes shape to alter the shadow, a “source shape” in the our HTML can move and morph to move and alter the shape of a shadow rendered in the browser. box-shadow duplicates the “morphing” features on the original size and border-radius. SVG filters get applied to both the element and its shadow.
This is the markup for our SVG so far. It won’t render because we haven’t defined anything visual (not to mention the zero width and height). It’s sole purpose is to hold a filter that we feed our SourceGraphic (aka our
We’ll add the essential CSS rule linking the HTML element (`#cloud-circle`) to the SVG filter using its ID:
#cloud-circle {
filter: url(#filter);
box-shadow: 200px 200px 50px 0px #fff;
}
Et Voilà!
OK, so admittedly, adding the SVG filter is pretty underwhelming.


No worries! We have only just scratched the surface and have a lot more good stuff to look at.
Experimenting with the feDisplacementMap scale attribute
A few un-scientific experiments with this one attribute can yield dramatic results. For the moment, let’s keep the all values in feTurbulence constant and simply adjust the scale attribute of DisplacementMap.
As scale increases (by increments of 30) our source


OK, we’re getting somewhere! Let’s change the colors a bit to produce a more convincing cloud and to “sell” the effect.
body {
background: linear-gradient(165deg, #527785 0%, #7FB4C7 100%);
}
#cloud-circle {
width: 180px;
height: 180px;
background: #000;
border-radius: 50%;
filter: url(#filter);
box-shadow: 200px 200px 50px 0px #fff;
}
Now we’re getting closer to a realistic cloud effect!


Modifying the box-shadow blur value
The following suite of images shows the influence that the blur value has on box-shadow. Here, blur is increased by 10 pixels incrementally.


To give our cloud a bit of a cumulus-like effect, we can widen our source
#cloud-circle {
width: 500px;
height: 275px;
background: #000;
border-radius: 50%;
filter: url(#filter);
box-shadow: 200px 200px 60px 0px #fff;
}


Wait! We’ve widened the source element and now it’s in the way of our of the white shadow we’re calling a cloud. Let’s “re-cast” the shadow at a greater distance so that our cloud is no longer obscured by the source image. (Think of this as moving your hand away further from the wall so it doesn’t block the view of your shadow puppet.)
This is nicely achieved with a bit of CSS positioning. The is the parent element for our cloud, which is statically positioned by default. Let’s “tuck” our source
#cloud-circle {
width: 500px;
height: 275px;
background: #000;
border-radius: 50%;
filter: url(#filter);
box-shadow: 400px 400px 60px 0px #fff; /* Increase shadow offset */
position: absolute; /* Take the parent out of the document flow */
top: -320px; /* Move a little down */
left: -320px; /* Move a little right */
}
Yes! We’ve arrived at a pretty persuasive cloud.
See the Pen
by Beau Haus (@beauhaus)
on CodePen.
What is painted to the browser is a pretty decent depiction of a cloud–But, I’m not sure…does this cloud really do justice the cloud nymph, Nephele? I’m sure we can do better!
Conveying depth with layers
Here’s what we want:


From the look of the depth, texture and richness of the clouds in this photograph, one thing is clear: Zeus went to art school. At the very least, he must have read the The Universal Principles of Design which illustrates a powerful–yet, deceptively ordinary–concept:
[…] lighting bias plays a significant role in the interpretation of depth and naturalness, and can be manipulated in a variety of ways by designers…Use the level of contrast between light and dark areas to vary the appearance of depth.
This passage provides for us a hint as to how to we can vastly improve our own code-generated cloud. We can render our cloud with a good deal of fidelity to the clouds in our reference image by stacking layers of differing form, size and color on top of each other. All that takes is calling our filter as many times as we want layers.


Applying our layers will afford us an opportunity to explore feTurbulence and realize its versatility. We’ll choose the smoother type available to us: fractalNoise with numOctaves cranked up to 6.
What does all that mean? For now, let’s focus specifically on the baseFrequency attribute. Here’s what we get as we increase the value of n:


Words like turbulence, noise, frequency, and octave may seem odd and even confusing. But fear not! It’s actually perfectly accurate to analogize this filter’s effects to sound waves. We may equate a low frequency (baseFrequency=0.001) with a low, muffled noise and a rising frequency (baseFrequency=0.1) with a higher, crisper pitch.
We can see that our sweet spot for a cumulus-like effect may lie comfortably around the ~0.005 and ~0.01 range for the baseFrequency.
Adding detail with numOctaves
Incrementing numOctaves allows us to render our image in extremely granular detail. This requires a great deal of calculation, so be warned: high values are a significant performance hit. Try to resist the temptation to pump up this value unless your browser is wearing a helmet and knee-pads.


The good news is that we don’t have to crank this value too high in order to produce detail and delicacy. As the array of images above shows, we can satisfy ourselves with a numOctavesvalue of 4 or 5.
Here’s the result
See the Pen
by Beau Haus (@beauhaus)
on CodePen.
Infinite variety with the seed attribute
There is much to say about the seed attribute as it offers a hint into the magic happening behind the scenes. But, for our purposes, the utility of seed can be reduced to four words: “different value, different shape.”
The Perlin Noise function (mentioned earlier) uses this value as the starting point for its random number generator. Choosing not to include this attribute will default seed to zero. When included, however, whatever value we give seed, we don’t need to worry about a performance hit.


The GIF above represents some of what seed has to offer. Keep in mind that each of those clouds is a layered, composite cloud. (While I have tweaked attributes for each layer, I have kept their respective seed values uniform.)


Here, with a close look at the reference image, I’ve layered 3 cloud-
See the Pen
Nephele Reference Image study by BEAU.HAUS (@beauhaus)
on CodePen.
Sky’s the limit
Of course, it would be hubris to think that the
However, the more mystery we are able to tease out of CSS and SVG filters, the more we are empowered create something visually stunning with a high degree of fidelity to the Thunder God’s original creation. We can, then, can go on experiment further!
Reflecting Mist


Alto-Cirrus Clouds


In this article, we have just dipped our toe in an ocean of power and complexity. SVG filters can often seem overwhelming and unapproachable.
However, much like the examples found in the A Single Div project project or Diana Smith’s painting techniques, a playful and experimental approach will always rewarded with spectacular results!
Achievement Unlocked! Nephele Cloud Generator


I’m sure many of you are cool to geek out over all the technical details it takes to make clouds, but might prefer something that makes it a whole lot easier to put clouds to use on a project. I developed a little tool to help generate clouds and experiment with shapes and variations.
Make Clouds!
Any questions, suggestions or advice? Ping me in the twitterverse or drop a comment here in the post.
Many thanks to Amelia Bellamy-Royds for her kind advice on this article.