The Falling Snow Effect

by kirupa   |   8 November 2015

For most of my life, I grew up in parts of the world where you never really had snow. That wasn't fun. During Christmas, the images of wintery goodness you grew up expecting and the sunny and warm weather outside your window never made sense. To make up for the lack of snow in my real world, I simulated falling snow on my computer. It was one of the first animations I created using ActionScript in Flash many years ago, and below is a variation of the original effect running in sweet HTML, CSS, and JavaScript:

What you should see are snowflakes slowly zigging and zagging as they fall from the virtual sky. In this article, you will learn two things. You will first learn how to quickly add this effect to your own content. Second, if you are interested in learning how this effect works, I provide a deconstruction of all the markup and code that goes into making this effect work.

Onwards!

Adding This Effect

To add some falling snow to your own HTML document, you just have to do three things. First, add the following HTML to the document you wish to see some falling snow in:

<div id="snowflakeContainer">
	<p class="snowflake">*</p>
</div>

Next, add the CSS that turns this HTML into something that more closely resembles a snowflake:

#snowflakeContainer {
	position: absolute;
	left: 0px;
	top: 0px;
}
.snowflake {
	padding-left: 15px;
	font-family: Cambria, Georgia, serif;
	font-size: 14px;
	line-height: 24px;
	position: fixed;
	color: #FFFFFF;
	user-select: none;
	z-index: 1000;
}
.snowflake:hover {
	cursor: default;
}

The last thing to do is add the JavaScript. Go ahead and download the fallingsnow_v6 zip file, extract the script contained inside it, and upload this script file to a location you can easily reference. After you've done that, anywhere in your HTML document (preferably towards the bottom just above your closing <body> tag, add the following line:

<script src="/your_path_to/fallingsnow_v6.js"></script>

Replace the placeholder script path (/your_path_to/) with that of your uploaded version. Once you've done that, preview in your browser to make sure everything works fine. If for whatever reason things aren't working properly, take a look at the example page and see how I have everything setup. If you still have questions, quickly post on the forums and I or somebody else will help you out.

And with that, you have a three-step way to get snow all over your HTML content. If that's all you came here for, you are free to go and enjoy the rest of the day...or night.

Now, I do recommend you take some time and learn how this effect works. Besides giving you some great things to talk about during dinner with friends and/or family, learning how this effect works will help you to better understand how to create your own animations - animations that are far cooler than just falling snow. Now, who wouldn't want to put that on their resume?

Overview of How this Effect Works

The way this effect works is pretty simple. You have many snowflakes (aka our elements), and each element's vertical and horizontal positions are changed slightly many times each second. This change is what we call an animation, and you are about to learn how this animation works. Here is the breakdown...

In our HTML, we have a single snowflake element defined:

snowflake

Programmatically, that single snowflake is cloned many MANY times:

many snowflakes

As part of cloned our snowflakes, we make some minor changes to their appearance so that they all look a little bit different from each other:

snowflakes different

Once we have our (mostly) unique snowflakes, the rest of the effort is just taking the well-trodden path (see Animating Many Things) towards getting them to each move slightly differently...in HTML, without affecting the layout of the rest of your page, and with great performance.

That's all there is to it!

The Code

Now that you have a basic understanding of how this effect works, let's shift gears and focus on how this effect is implemented. Let's start by looking at what our HTML and CSS look like:

<!doctype html>
<html>
<head>
<title>Falling Snow Example | kirupa.com</title>
<style>
body {
	background-color: #1D1D1D;
	padding: 30px;
	margin: 0px;
}
#snowflakeContainer {
	position: absolute;
	left: 0px;
	top: 0px;
}
.snowflake {
	padding-left: 15px;
	font-family: Cambria, Georgia, serif;
	font-size: 14px;
	line-height: 24px;
	position: fixed;
	color: #FFFFFF;
	user-select: none;
	z-index: 1000;
}
.snowflake:hover {
	cursor: default;
}
h1 {
	font-size: 32px;
	font-family: Arial, Helvetica, sans-serif;
	background-color: #FF3300;
	padding: 15px;
	color: #FFF;
	margin: 0px;
}
p, ol {
	font-family: "Franklin Gothic Medium", "Arial Narrow", sans-serif;
	font-size: 24px;
	color: #CCC;
}
li {
	margin-bottom: 24px;
	padding-left: 10px;
}
</style>
</head>
<body>

<div id="snowflakeContainer">
	<p class="snowflake">*</p>
</div>
<div id="content">
<h1>List of things I want from Santa</h1>
	<ol>
		<li>A pony</li>
		<li>Proof that wooly mammoths existed in the time of the railroads</li>
		<li>Recipe for the perfect bubble tea</li>
		<li>Timeframe for the REAL Matrix sequels</li>
		<li>Coal. Lots and lots of coal so that I can sell it to power plants</li>
	</ol>
</div>
<script src="//www.kirupa.com/js/fallingsnow_v6.js"></script>
<script src="//www.kirupa.com/js/prefixfree.min.js"></script>
</body>

</html>

If you type this up (...or preferably copy and paste it :P) into a HTML document and preview in your browser, you will have falling snow! Now, our implementation isn't obviously as simple as what you see here. A lot of the heavy lifting is done in the falling snow JavaScript file (fallingsnow_v6.js) that this document references, so let's take a quick look at that:

// The star of every good animation
var requestAnimationFrame = window.requestAnimationFrame || 
                            window.mozRequestAnimationFrame || 
                            window.webkitRequestAnimationFrame ||
                            window.msRequestAnimationFrame;

var transforms = ["transform", 
                  "msTransform", 
                  "webkitTransform", 
                  "mozTransform", 
                  "oTransform"];
                   
var transformProperty = getSupportedPropertyName(transforms);

// Array to store our Snowflake objects
var snowflakes = [];

// Global variables to store our browser's window size
var browserWidth;
var browserHeight;

// Specify the number of snowflakes you want visible
var numberOfSnowflakes = 50;

// Flag to reset the position of the snowflakes
var resetPosition = false;

//
// It all starts here...
//
function setup() {
	window.addEventListener("DOMContentLoaded", generateSnowflakes, false);
	window.addEventListener("resize", setResetFlag, false);
}
setup();

//
// Vendor prefix management
//
function getSupportedPropertyName(properties) {
    for (var i = 0; i < properties.length; i++) {
        if (typeof document.body.style[properties[i]] != "undefined") {
            return properties[i];
        }
    }
    return null;
}

//
// Constructor for our Snowflake object
//
function Snowflake(element, speed, xPos, yPos) {

	// set initial snowflake properties
    this.element = element;
    this.speed = speed;
    this.xPos = xPos;
    this.yPos = yPos;
	
	// declare variables used for snowflake's motion
    this.counter = 0;
    this.sign = Math.random() < 0.5 ? 1 : -1;
	
	// setting an initial opacity and size for our snowflake
    this.element.style.opacity = .1 + Math.random();
    this.element.style.fontSize = 12 + Math.random() * 50 + "px";
}

//
// The function responsible for actually moving our snowflake
//
Snowflake.prototype.update = function () {

	// using some trigonometry to determine our x and y position
	this.counter += this.speed / 5000;
	this.xPos += this.sign * this.speed * Math.cos(this.counter) / 40;
	this.yPos += Math.sin(this.counter) / 40 + this.speed / 30;
	
	// setting our snowflake's position
	setTranslate3DTransform(this.element, Math.round(this.xPos), Math.round(this.yPos));
    
    // if snowflake goes below the browser window, move it back to the top
	if (this.yPos > browserHeight) {
		this.yPos = -50;
	}
}

//
// A performant way to set your snowflake's position
//
function setTranslate3DTransform(element, xPosition, yPosition) {
	var val = "translate3d(" + xPosition + "px, " + yPosition + "px" + ", 0)";
    element.style[transformProperty] = val;
}

//
// The function responsible for creating the snowflake
//
function generateSnowflakes() {
	
	// get our snowflake element from the DOM and store it
	var originalSnowflake = document.querySelector(".snowflake");
	
	// access our snowflake element's parent container
	var snowflakeContainer = originalSnowflake.parentNode;
    
    // get our browser's size
	browserWidth = document.documentElement.clientWidth;
	browserHeight = document.documentElement.clientHeight;
	        
    // create each individual snowflake
	for (var i = 0; i < numberOfSnowflakes; i++) {
	
		// clone our original snowflake and add it to snowflakeContainer
		var snowflakeClone = originalSnowflake.cloneNode(true);
		snowflakeContainer.appendChild(snowflakeClone);
	
		// set our snowflake's initial position and related properties
	    var initialXPos = getPosition(50, browserWidth);
	    var initialYPos = getPosition(50, browserHeight);
	    var speed = 5+Math.random()*40;
	    
	    // create our Snowflake object
		var snowflakeObject = new Snowflake(snowflakeClone, 
											speed, 
											initialXPos, 
											initialYPos);
		snowflakes.push(snowflakeObject);
	}
    
    // remove the original snowflake because we no longer need it visible
	snowflakeContainer.removeChild(originalSnowflake);
	
	// call the moveSnowflakes function every 30 milliseconds
    moveSnowflakes();
}

//
// Responsible for moving each snowflake by calling its update function
//
function moveSnowflakes() {
	for (var i = 0; i < snowflakes.length; i++) {
	    var snowflake = snowflakes[i];
	    snowflake.update();
	}
    
	// Reset the position of all the snowflakes to a new value
	if (resetPosition) {
		browserWidth = document.documentElement.clientWidth;
		browserHeight = document.documentElement.clientHeight; 
		
		for (var i = 0; i < snowflakes.length; i++) {
			var snowflake = snowflakes[i];
			
			snowflake.xPos = getPosition(50, browserWidth);
			snowflake.yPos = getPosition(50, browserHeight);
		}
		
		resetPosition = false;
	}
    
    requestAnimationFrame(moveSnowflakes);
}

//
// This function returns a number between (maximum - offset) and (maximum + offset)
//
function getPosition(offset, size) {
	return Math.round(-1*offset + Math.random() * (size+2*offset));
}

//
// Trigger a reset of all the snowflakes' positions
//
function setResetFlag(e) {
	resetPosition = true;
}

The collision of this JavaScript file with the HTML and CSS you saw earlier is what results in this falling snow animation effect. Take a few moments and glance through everything. In the next section, we'll look at all of this in great detail and learn why things work the way they do.

The Markup and Code Explained

Now that you've seen the insides of what this effect looks like, it's time to make sense of it all. Let's start with our snowflake and the HTML/CSS that makes it come alive.

Meet the Snowflake

Our snowflake is represented by the following HTML:

<div id="snowflakeContainer">
	<p class="snowflake">*</p>
</div>

As you can see, there is a div element called snowflakeContainer. Inside it is a p element whose text makes up the snowflake that you see on the screen:

<div id="snowflakeContainer">
	<p class="snowflake">*</p>
</div>

Thaaaaat's right. Our wonderful / little / unique snowflake is nothing more than the asterisk * character. I'll let that disappointment sink in for a few moments....

Now, the most important thing to note is that the p element has a class value of snowflake. This class value is something both our CSS and JavaScript use to identify the element that will be moving around on screen. The reason this is important is that, if you want to animate something that isn't a text element, make sure that "something" has a class value of snowflake added to it.

Remember, your snowflake doesn't have to be a text element. It can be anything such as an img, another div, etc:

<div id="snowflakeContainer">
	<img class="snowflake" src="mysnowflake.png"/>
</div>

Whatever it is that you are animating, just make sure it has a class value of snowflake in order to have our CSS and JavaScript work without requiring any modification.

The Snowflake CSS

Right now, our snowflake looks pretty boring. Worse, it has to suffer through the default layout constraints all HTML elements have to live with. Both of these issues (and more) are addressed with the following style rules:

#snowflakeContainer {
	position: absolute;
	left: 0px;
	top: 0px;
}
.snowflake {
	padding-left: 15px;
	font-family: Cambria, Georgia, serif;
	font-size: 14px;
	line-height: 24px;
	position: fixed;
	color: #FFFFFF;
	user-select: none;
	z-index: 1000;
}
.snowflake:hover {
	cursor: default;
}

Freeing our snowflake (and its parent snowflakeContainer) from the default HTML box model layout is handled by the following highlighted lines:

#snowflakeContainer {
	position: absolute;
	left: 0px;
	top: 0px;
}
.snowflake {
	padding-left: 15px;
	font-family: Cambria, Georgia, serif;
	font-size: 14px;
	line-height: 24px;
	position: fixed;
	color: #FFFFFF;
	user-select: none;
	z-index: 1000;
}
.snowflake:hover {
	cursor: default;
}

These highlighted lines allow us to programmatically position and move each snowflake around without having to worry about how it will affect the layout of other elements. They also grant our snowflakes a degree of freedom from the various paddings, margins, scrollbars, overlaps, and other constraints that you would otherwise have to deal with.

The rest of the CSS is pretty simple. Because our snowflake is a text element, there are some font-related properties that I specify. To avoid our snowflakes from being selectable either with the mouse or Ctrl + A, the user-select property makes an appearance. To stop our cursor from changing to anything but our default arrow when you hover over each snowflake, the .snowflake:hover style rule exists with the cursor style declaration defined inside it.

That's all there is to our CSS. At this point, you will see a single snowflake that (besides looking lonely) is optimistically looking forward to all the great things JavaScript promised it will do for it.

Spoiler Alert

Things don't end well for this snowflake.

  

The JavaScript

The HTML and CSS you've seen so far are responsible for getting our single snowflake displayed on the screen and in a state where its position can be manipulated easily. That's it. Everything else is handled entirely in JavaScript, so in this section we'll take a detailed look at what is happening.

The Global Variables

We are going to start by looking at the global variables that we have declared at the beginning:

// The star of every good animation
var requestAnimationFrame = window.requestAnimationFrame || 
                            window.mozRequestAnimationFrame || 
                            window.webkitRequestAnimationFrame ||
                            window.msRequestAnimationFrame;

var transforms = ["transform", 
                  "msTransform", 
                  "webkitTransform", 
                  "mozTransform", 
                  "oTransform"];
                   
var transformProperty = getSupportedPropertyName(transforms);

// Array to store our Snowflake objects
var snowflakes = [];

// Global variables to store our browser's window size
var browserWidth;
var browserHeight;

// Specify the number of snowflakes you want visible
var numberOfSnowflakes = 50;

// Flag to reset the position of the snowflakes
var resetPosition = false;

These variables seem pretty uninteresting right now (they are), but they will be very important as we start looking at other parts of our code. Unless you know what you are doing, you shouldn't fiddle with any of these variables or their values...except for one. The one variable whose value you may want to alter and experiment with is numberOfSnowflakes. The value associated with this variable specifies how many snowflakes you will generate. The more snowflakes you have, the more awesome your animation will be. Your animation may become slower as well, so be careful about that. For our purposes, the current value of 50 is good...for now!

Anyway, getting back on track, quickly glance through all of these variables and pay attention to any initial values they have. I'll be referring to these variables quite a bit, so let's not spend too much time here at this point.

It All Starts Here

The next thing I want us to look at is the very first function that gets called when our script loads. That is our setup function:

function setup() {
	window.addEventListener("DOMContentLoaded", generateSnowflakes, false);
	window.addEventListener("resize", setResetFlag, false);
}
setup();

This function's job is simple. It contains two event listeners. The first event listener listens for the DOMContentLoaded event, and when that event is overheard, calls the generateSnowflakes function. The second event listener calls the setResetFlag function when the resize event is overheard.

The DOMContentLoaded event is fired when the overall structure of your document is ready. Listening to this event is a quick way of having your scripts ready to manipulate your document without waiting for the more comprehensive load event that only fires when everything in your page has fully downloaded. To learn more about this, check out my Running Scripts at the Right Time tutorial.

The resize event is fired when your window's size is changed...such as when you resize your browser window. We'll look at this event and the setResetFlag function in a little bit, but let's focus on the generateSnowflakes function next.

Generating the Snowflakes

The first real function that we will look at is the function that gets called when the DOMContentLoaded event has fired - our generateSnowflakes function:

function generateSnowflakes() {
	
	// get our snowflake element from the DOM and store it
    var originalSnowflake = document.querySelector(".snowflake");
    
    // access our snowflake element's parent container
    var snowflakeContainer = originalSnowflake.parentNode;
    
    // get our browser's size
	browserWidth = document.documentElement.clientWidth;
    browserHeight = document.documentElement.clientHeight;
	        
    // create each individual snowflake
    for (var i = 0; i < numberOfSnowflakes; i++) {
    
    	// clone our original snowflake and add it to snowflakeContainer
        var snowflakeCopy = originalSnowflake.cloneNode(true);
        snowflakeContainer.appendChild(snowflakeCopy);

		// set our snowflake's initial position and related properties
        var initialXPos = getPosition(50, browserWidth);
        var initialYPos = getPosition(50, browserHeight);
        var speed = 5+Math.random()*40;
        
        // create our Snowflake object
        var snowflakeObject = new Snowflake(snowflakeCopy, 
        									speed, 
        									initialXPos, 
        									initialYPos);
        snowflakes.push(snowflakeObject);
    }
    
    // remove the original snowflake because we no longer need it visible
	snowflakeContainer.removeChild(originalSnowflake);
	
	// call the moveSnowflakes function every 30 milliseconds
    moveSnowflakes();
}

This function, is responsible for creating the many snowflakes that you see. As you can tell by how many lines of code there is here, there are a few more things it does as part of creating the snowflakes. This function heroically:

  1. Finds our .snowflake and snowflakeContainer elements in our HTML.
  2. Clones many versions of our snowflake element and "pastes" it back in our DOM
  3. Creates a Snowflake Object for each snowflake clone
  4. Removes our original snowflake element
  5. Starts off the animation

Let's look at how our code translates into the five things that I've written in English above...starting with finding our snowflake element and its parent, our snowflakeContainer:

// get our snowflake element from the DOM and store it
var originalSnowflake = document.querySelector(".snowflake");

// access our snowflake element's parent container
var snowflakeContainer = originalSnowflake.parentNode;

Getting the snowflake is easy. We just use the querySelector function and pass in the class name snowflake. For finding the snowflakeContainer, I make a call to our snowflake's parentNode property and set the returned element as the value to our snowflakeContainer variable.

At this point, the originalSnowflake and snowflakeContainer variables contain a reference to our snowflake element and its parent respectively. Let's skip the next few lines where we just declare some properties and jump ahead to our for loop:

for (var i = 0; i < numberOfSnowflakes; i++) {

	// clone our original snowflake and add it to snowflakeContainer
    var snowflakeCopy = originalSnowflake.cloneNode(true);
    snowflakeContainer.appendChild(snowflakeCopy);

	// set our snowflake's initial position and related properties
    var initialXPos = getPosition(50, browserWidth);
    var initialYPos = getPosition(50, browserHeight);
    var speed = 5+Math.random()*40;
    
    // create our Snowflake object
    var snowflakeObject = new Snowflake(snowflakeCopy, 
    									speed, 
    									initialXPos, 
    									initialYPos);
    snowflakes.push(snowflakeObject);
}

This loop is responsible for creating a copy of our snowflake as well as creating the Snowflake object that helps us work with the snowflake copy. The first thing we do is create a copy of our snowflake and add it to snowflakeContainer:

var snowflakeClone = originalSnowflake.cloneNode(true);
snowflakeContainer.appendChild(snowflakeClone);

Copying an element is pretty easy. The cloneNode function with an argument of true ensures we clone the element we are calling along with any children it may contain. At this point, we just have an in-memory copy of our snowflake element. That doesn't really do anyone any good. To have our snowflake actually show up, we need to add it to our DOM, and that is done by calling the appendChild function on snowflakeContainer.

The next thing we do is create a Snowflake object that helps associate our snowflake element with something we can easily manipulate:

creating the snowflake object

The code is less visual but equally interesting:

var snowflakeObject = new Snowflake(snowflakeClone, 
									speed, 
									initialXPos, 
									initialYPos);

The way we do this is pretty straightforward. We create a variable called snowflakeObject that will store our soon to be created Snowflake object. We'll look at the Snowflake function in detail shortly, but for now, just note that we pass in the following arguments:

  1. A reference to our cloned snowflake element
  2. The speed at which we want our snowflakes to move
  3. The initial horizontal position
  4. The initial vertical position

The "big picture" thing to emphasize is that we have a self-contained Snowflake object that contains all of the visual properties we care about along with a reference to its snowflake element in our DOM. This is important as you will see shortly.

The last thing we do in this loop is store each newly created Snowflake object into the snowflakes array:

snowflakes.push(snowflakeObject);

At the end of our loop having run many times to completion, our snowflakes array will contain a pointer to every single Snowflake object that was created. This array will be your only connection to the snowflakes that are now living in your DOM and taking up space as Snowflake objects in your browser's memory!

The next thing we do in this function is remove our original snowflake element from the DOM:

snowflakeContainer.removeChild(originalSnowflake);

I wasn't kidding when I mentioned that things won't end well for our original snowflake element. The reason we are doing this is because our original snowflake was just the template for all of the snowflakes that we ended up creating clones of. Once our snowflake clones are ready, there is no point in keeping the original around. The original snowflake exists only in the DOM sense. We never created a Snowflake object to go along with it, and it is more of a hassle to do that than to just remove it by using the removeChild function on its parent. George R. R. Martin would concur with my decision.

The last thing this epic function does is call the moveSnowflakes function:

moveSnowflakes();

This function is responsible for taking all of the Snowflake objects that we have (stored neatly inside the snowflakes array) and moving them around. We'll look at this function shortly, but next, let's look at the Snowflake Object in more detail.

The Snowflake Object

After your generateSnowflakes function has run to completion, you will have a lot of Snowflake objects floating around. Let's look at this in more detail starting with its constructor:

function Snowflake(element, speed, xPos, yPos) {

	// set initial snowflake properties
    this.element = element;
    this.speed = speed;
    this.xPos = xPos;
    this.yPos = yPos;
	
	// declare variables used for snowflake's motion
    this.counter = 0;
    this.sign = Math.random() < 0.5 ? 1 : -1;
	
	// setting an initial opacity and size for our snowflake
    this.element.style.opacity = .1 + Math.random();
    this.element.style.fontSize = 12 + Math.random() * 50 + "px";
}

As you can see, a Snowflake object is largely just a giant bag where properties are defined and stored. That is especially true for the element, speed, xPos, and yPos variables that get passed in as the arguments. The counter and sign properties join this party on their own, and you'll get to see their sweet dance moves in a few seconds when they get called to the stage.

The opacity and fontSize properties are the exception to what is going on here, though:

this.element.style.opacity = .1 + Math.random();
this.element.style.fontSize = 12 + Math.random() * 50 + "px";

With these two lines, you are setting the opacity and fontSize CSS properties on the snowflake element directly. They are responsible for giving your snowflake the arbitrary size and transparency when you see it on the stage.

The Snowflake Update Function

The constructor function isn't the only thing our Snowflake object needs. To complement the list of properties it stores, it has a prototype called update where a lot of these properties are actually modified and result in an animation when called many times a second as part of our animation loop:

Snowflake.prototype.update = function () {

	// using some trigonometry to determine our x and y position
    this.counter += this.speed / 5000;
    this.xPos += this.sign * this.speed * Math.cos(this.counter) / 40;
    this.yPos += Math.sin(this.counter) / 40 + this.speed / 30;

	// setting our snowflake's position
    setTranslate3DTransform(this.element, Math.round(this.xPos), Math.round(this.yPos));
    
    // if snowflake goes below the browser window, move it back to the top
    if (this.yPos > browserHeight) {
    	this.yPos = -50;
    }
}

You'll see this update function get called shortly as part of our look into the moveSnowflakes function, but notice that all this function does is update the values of the various properties that you saw in the constructor earlier.

These lines are responsible for getting our snowflake's new position values:

// using some trigonometry to determine our x and y position
this.counter += this.speed / 5000;
this.xPos += this.sign * this.speed * Math.cos(this.counter) / 40;
this.yPos += Math.sin(this.counter) / 40 + this.speed / 30;

Taking these values and actually positioning the elements is handled by the call to the setTranslate3DTransform function:

// setting our snowflake's position
setTranslate3DTransform(this.element, Math.round(this.xPos), Math.round(this.yPos));

This function basically sets the transform CSS property to the translate3d function that allows you to very efficiently position things on the screen. You can see more about this in the Animating Movement Smoothly Using CSS tutorial.

The last thing this update function does is reset your snowflake's position if it falls 50 pixels below your browser window:

if (this.yPos > browserHeight) {
	this.yPos = -50;
}

Obviously, this is a good thing since you probably don't want to waste resources animating something that you can definitely no longer see :P

The Animation Loop

The last thing we will look at is probably one of the most important pieces of JavaScript we have in this example, the moveSnowflakes function:

function moveSnowflakes() {
    for (var i = 0; i < snowflakes.length; i++) {
        var snowflake = snowflakes[i];
        snowflake.update();
    }
    
	// Reset the position of all the snowflakes to a new value
    if (resetPosition) {
    	browserWidth = document.documentElement.clientWidth;
	    browserHeight = document.documentElement.clientHeight; 
	    
		for (var i = 0; i < snowflakes.length; i++) {
	        var snowflake = snowflakes[i];
	        
	        snowflake.xPos = getPosition(50, browserWidth);
	        snowflake.yPos = getPosition(50, browserHeight);
	    }
	    
	    resetPosition = false;
    }
    
    requestAnimationFrame(moveSnowflakes);
}

This function is our animation loop, and you can see that by looking at the last line where our requestAnimationFrame function makes its triumphant entrance:

requestAnimationFrame(moveSnowflakes);

This line ensures that the moveSnowflakes function gets called the next time the browser is ready to repaint. That usually happens at around 60 times a second, but for this particular example, it may be a little less given how much work this function does. You can learn more about requestAnimationFrame in the Animating with requestAnimationFrame tutorial.

Between the start of this function and the end where our requestAnimationFrame line is, two major things happen. The first major thing is updating the position of each snowflake:

for (var i = 0; i < snowflakes.length; i++) {
    var snowflake = snowflakes[i];
    snowflake.update();
}

Notice that this loop goes through every Snowflake object that lives inside our snowflakes array and calls the update function that we looked at earlier. This chunk of code living inside the animation loop is what causes each snowflake to animate its way down the document.

The second major thing that our moveSnowflakes function does is reset the position of all of our snowflakes when the window is resized:

if (resetPosition) {
	browserWidth = document.documentElement.clientWidth;
	browserHeight = document.documentElement.clientHeight; 
	
	for (var i = 0; i < snowflakes.length; i++) {
		var snowflake = snowflakes[i];
		
		snowflake.xPos = getPosition(50, browserWidth);
		snowflake.yPos = getPosition(50, browserHeight);
	}
	
	resetPosition = false;
}

This code only runs when the resetPosition variable's value is set to true, and that only happens when the setResetFlag function is called as part of the resize event getting fired. What this code does is pretty simple. The code goes through each snowflake (thanks the great bookkeeping our snowflakes array has done) and specifies a new value for each Snowflake object's xPos and yPos properties. The next time the update function on the snowflake gets called, your snowflakes will respect their new positions and revised boundaries.

Constrain the Falling Snow to a Particular Region

Our falling snow effect currently goes over the entire page. You may not want that. You may just want the snow effect to appear over just a particular region - like an image, a banner, etc. To learn how to modify this effect to achieve that, check out Kyle Murray's excellent Constraining the Falling Snow Effect tutorial.

Conclusion

So, there you have it - a look at how HTML, CSS, and JavaScript come together to created the animated falling snow effect that you see. If you've been following the animation tutorials on this site, most of what you saw here should have been a review. What isn't a review is how the snowflakes are generated. These snowflakes are cloned from an existing HTML element as opposed to being drawn or explicitly defined in the HTML. That's pretty cool!

Getting Help

If you have questions, need some assistance on this topic, or just want to chat - post in the comments below or drop by our friendly forums (where you have a lot more formatting options) and post your question. There are a lot of knowledgeable and witty people who would be happy to help you out

Share

Did you enjoy reading this and found it useful? If so, please share it with your friends:

If you didn't like it, I always like to hear how I can do better next time. Please feel free to contact me directly via e-mail, facebook, or twitter.

Kirupa Chinnathambi
I like to talk a lot - A WHOLE LOT. When I'm not talking, I've been known to write the occasional English word. You can learn more about me by going here.

Add Your Comment (or post on the Forums)

blog comments powered by Disqus

Awesome and high-performance web hosting!
BACK TO TOP
new books - yay!!!