# The Falling Snow Effect by [kirupa](https://www.kirupa.com/me/index.htm) | filed under [Web Animation](https://www.kirupa.com/html5/learn_animation.htm) 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: Your browser does not support inline frames or is currently configured not to display inline frames. In this article, we will cover two things: 1. How you can add this effect easily to your web site or app 1. A high-level deconstruction of how this code works This is going to be a fun article, so sit back and let's get all of this falling snow shenanigans underway. Onwards! ## Adding This Effect To add some falling snow to your own web site/app, you just have to copy and paste some stuff. Towards the bottom of your page (probably just above the closing `` tag), add all of the following: ```js
``` What you just pasted contains everything you would need to generate our snowflakes and cause them to animate and fall to the ground. ### Preview And/Or Get Help Once you've added all of this HTML, CSS, and JavaScript, preview in your browser to make sure everything works as expected. If for whatever reason things aren't working properly, take a look at [this pen](https://codepen.io/kirupa/pen/YzveVzx) or the source in the [ example page](https://www.kirupa.com/html5/examples/falling_snow2.htm) and see how I have everything setup. If you still have questions, quickly post on the [forums](https://forum.kirupa.com) and I or somebody else will help you out. ### Customizing the Snowflakes Out of the box, the animation may be perfect for your needs. If you want to make some changes to it, there are a bunch of customizations that you can make. After all, it is all just HTML, CSS, and JavaScript. With that said, there are a few changes that you may want to make that are easily doable. #### Snowflake Appearance The first is changing the color of the snowflake. The color of our snowflake is defined inside the **.snowflake** class in our CSS: ```css .snowflake { position: fixed; background-color: #CCC; user-select: none; z-index: 1000; pointer-events: none; border-radius: 50%; width: 10px; height: 10px; } ``` Change the `background-color` property to whatever color you want your snowflake to be. In our example, I have it set to **#FFFFFF** (white) because the background is darker. If you are going to be using this effect on a lighter colored background, you should adjust the snowflake color accordingly. You can even go further and decide that you may want something completely different for the snowflake...like an actual snowflake image: ![](https://www.kirupa.com/html5/images/snowflake_snowflake.png) For this variation, the HTML could look as follows: ```html
``` The corresponding CSS does away with the default circular colors and optimizes for the image instead: ```css .snowflake { position: fixed; user-select: none; z-index: 1000; pointer-events: none; width: 15px; } ``` Feel free to get even more creative. If you created something really cool, please do share it with me by posting about it [on the forums](https://forum.kirupa.com). #### Changing the Number of Snowflakes Another customization you can make revolves around the number of snowflakes that make up this effect. The more snowflakes you have, the more of a wintery storm feeling your page will have. The fewer snowflakes you have, well...that will just look weak! To adjust the number of snowflakes, find the `numberOfSnowflakes` variable in our JavaScript: ```js let numberOfSnowflakes = 50; ``` It is set to 50 currently. Replace it to a higher or lower number depending on the sort of effect you are looking for. One thing to keep in mind is performance. The more snowflakes you add, the more sluggish your page will be on older hardware. Don't let that warning deter you, though. If your audience will tend to be on more powerful hardware (both mobile and desktop), then you should feel free to go crazy! And with that, you are done with having a simple 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 further your web animation-fu. One can never have too much web animation-fu! #### Copyright Details / Licensing You may be wondering whether you can use this effect, modify it for your needs, and a boatload of other questions. The answer to everything is "**Yes**". This code is provided in an MIT license, so feel free to use it in your commercial or non-commercial situations. All code on this site is freely available for you to use. ## Overview of How this Effect Works The way this effect works is pretty simple...sort of. You have many snowflakes (aka our elements), and each element's position and size is changed slightly many MANY times each second. This change is what results in the animation we ultimately see. The starting point for all this is found in our HTML where have a single snowflake element defined. It's a circle in our case, but for illustration purposes I'll use the following snowflake-looking shape instead: ![](https://www.kirupa.com/html5/images/snowflake_300.png) Using code, that single snowflake is cloned a bunch of times (as determined by what our `numberOfSnowflakes` variable says): ![](https://www.kirupa.com/html5/images/many_snowflakes_300.png) As part of cloning our snowflakes, we make some minor changes to their appearance so that they all look a little bit different from each other: ![](https://www.kirupa.com/html5/images/snowflakes_different_300.png) Once we have our (mostly) unique snowflakes, the rest of the effort is just taking the well-trodden path towards getting them to each move slightly differently. We will cover the code and everything in some detail in the next few sections, so roll up your sleeves, get some coffee, and let's get started! ## Deeper Look at How the Effect Works Now that we have a very 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 our HTML: ```html
``` There isn't much going on here. We have one element called **snowflakeContainer**, and it has one child with a class value of **snowflake**. That is the entirety of the HTML we explicitly specify in our document. To turn this HTML into the falling snowflake-like effect that you see, we have to take a look at our JavaScript. That's where a lot of the heavy lifting gets done. ### The Starting Point When it comes to our JavaScript, the `setup` function is what gets called by our code first: ```js function setup() { if (enableAnimations) { window.addEventListener("DOMContentLoaded", generateSnowflakes, false); window.addEventListener("resize", setResetFlag, false); } } setup(); ``` We first check if animations are enabled. After that, the big thing we do here is listen for two events. When the **DOMContentLoaded** event is fired, we call `generateSnowflakes`. If the window is resized, we call `setResetFlag`. Of these two events, what happens when our page's DOM has loaded is the most interesting. We will look at that code next. ### Generating the Snowflakes A big part of this effect is taking the one snowflake we have defined in our HTML and using it as a template for the many snowflakes we ultimately end up seeing. The code for making this happen lies in the `generateSnowflakes` function: ```js function generateSnowflakes() { // get our snowflake element from the DOM and store it let originalSnowflake = document.querySelector(".snowflake"); // access our snowflake element's parent container let snowflakeContainer = originalSnowflake.parentNode; snowflakeContainer.style.display = "block"; // get our browser's size browserWidth = document.documentElement.clientWidth; browserHeight = document.documentElement.clientHeight; // create each individual snowflake for (let i = 0; i < numberOfSnowflakes; i++) { // clone our original snowflake and add it to snowflakeContainer let snowflakeClone = originalSnowflake.cloneNode(true); snowflakeContainer.appendChild(snowflakeClone); // set our snowflake's initial position and related properties let initialXPos = getPosition(50, browserWidth); let initialYPos = getPosition(50, browserHeight); let speed = 5 + Math.random() * 40; // create our Snowflake object let snowflakeObject = new Snowflake(snowflakeClone, speed, initialXPos, initialYPos); snowflakes.push(snowflakeObject); } // remove the original snowflake because we no longer need it visible snowflakeContainer.removeChild(originalSnowflake); moveSnowflakes(); } ``` This code does a bunch of things: 1. Creates a clone of our snowflake in the DOM 1. Sets the random initial snowflake position (based on the current viewport size) 1. Sets the random speed our snowflake will fall in 1. Stores everything in a JavaScript representation of our snowflake 1. Kicks off the animation All of these steps are repeated for each snowflake that we create. One output of all this code running is a new `Snowflake` object: ```js let snowflakeObject = new Snowflake(snowflakeClone, speed, initialXPos, initialYPos); snowflakes.push(snowflakeObject); ``` This object not only carries with it a reference to the DOM representation of a snowflake element, it also carries the values for the speed and position that were generated a few lines earlier: ![](https://www.kirupa.com/html5/images/snowflake_object_300.png) We'll cover this object in greater detail later, but this `Snowflake` object and its associated DOM element make up our snowflake from a code point of view. Getting back to the present, after our snowflake is created, we keep this object around for later use by stashing it away in an array called `snowflakes`. All of this happens inside a loop that runs for each snowflake we want as part of the effect. The last thing that happens in our `generateSnowflakes` function is the call to `moveSnowflakes` where all of the snowflakes we generated earlier be set to move. We'll find out next. ### The Animation Loop In every animation that relies on JavaScript, we have something known as the animation loop that is responsible for running repeatedly and containing any code needed to animate something around. The `moveSnowflakes` function is that loop for us: ```js function moveSnowflakes() { if (enableAnimations) { for (let i = 0; i < snowflakes.length; i++) { let 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 (let i = 0; i < snowflakes.length; i++) { let snowflake = snowflakes[i]; snowflake.xPos = getPosition(50, browserWidth); snowflake.yPos = getPosition(50, browserHeight); } resetPosition = false; } requestAnimationFrame(moveSnowflakes); } ``` We can see that by looking at the `requestAnimationFrame` call at the bottom that will call the `moveSnowflakes` function repeatedly. Inside our animation loop, the main thing that happens is our code for updating each of our snowflake: ```js if (enableAnimations) { for (let i = 0; i < snowflakes.length; i++) { let snowflake = snowflakes[i]; snowflake.update(); } } ``` We go through every snowflake stored by our `snowflakes` array and call the `update` method on it. If you were expecting something more fancy, here ain't it. The fancy stuff lives in our `Snowflake` code, so let's go there next. ### The Snowflake Implementation All of the complexity for making our snowflake animate lives inside the definition for our `Snowflake` object. When we first create our snowflakes via the `generateSnowflakes` function, this is the code that gets called: ```js function Snowflake(element, speed, xPos, yPos) { // set initial snowflake properties this.element = element; this.speed = speed; this.xPos = xPos; this.yPos = yPos; this.scale = 1; // 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()) / 3; } ``` When our snowflake gets created, all we do is store a bunch of properties on it to represent the various characteristics we want it to maintain. Some of the properties we specify as part of creating the snowflake and provide as arguments, but other properties we define for internal use like `counter`, `scale`, and `sign`. The important detail to keep in mind is that these properties aren't designed to stay still. They start off with the initial values they get when the snowflake is created. When the `update` method (that our `moveSnowflakes` animation loop calls) runs, these properties change and play a major role in defining how the snowflake moves. The `update` method looks as follows: ```js update(delta) { // using some trigonometry to determine our x and y position this.counter += (this.speed / 5000) * delta; this.xPos += (this.sign * delta * this.speed * Math.cos(this.counter)) / 40; this.yPos += Math.sin(this.counter) / 40 + (this.speed * delta) / 30; this.scale = 0.5 + Math.abs((10 * Math.cos(this.counter)) / 20); // setting our snowflake's position setTransform( Math.round(this.xPos), Math.round(this.yPos), this.scale, this.element ); // if snowflake goes below the browser window, move it back to the top if (this.yPos > browserHeight) { this.yPos = -50; } } ``` The bulk of this code helps our snowflakes oscillate as they make their way to the ground. This oscillation is made possible by using the **sin** and **cos** trigonmetric functions along with some numbers that scale up or down the intensity of the movement. The `counter` property is incremented here as well, and this rate of incrementing determines how quickly our snowflakes animate. If you are up for it, you can adjust the values and numbers you see here to see how your snowflake's movements will be affected. All of these numerical changes are still in the JavaScript realm. They are a part of our `Snowflake` object, but they don't actually affect the snowflake DOM element we see on the screen. That gap is addressed by our call to the `setTransform` function: ```js setTransform(Math.round(this.xPos), Math.round(this.yPos), this.scale, this.element); ``` We will take a look at what it does next! ### Setting our Snowflake's Position and Size The `setTransform` function takes all the calculations made by our snowflake's `update` method and translates them into the position and size values that we ultimately see: ```js function setTransform(xPos, yPos, scale, el) { el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0) scale(${scale}, ${scale})`; } ``` The way this we do this is by setting the `transform` property on the DOM element that represents each snowflake. We set both the **translate3d** function to [performantly adjust](https://www.kirupa.com/html5/animating_movement_smoothly_using_css.htm) the horizontal and vertical position, and we also set the **scale** function to adjust how big or small the snowflake is as it is falling down. **Once this line of code has executed, one frame of our animation is complete with our snowflake having moved ever so slightly towards the ground.** #### Accessibility and Animations Throughout our code, you may have seen us checking the value of an `enableAnimations` property. This property's value corresponds to whether the user has disabled animations as part of an accessibility setting. The [Toggling Animations On and Off](https://www.kirupa.com/html5/toggling_animations_on_off.htm) tutorial goes into more detail on this, but there are users who, when seeing animations like our falling snow, can get sick or nauseus. There is an accessibility setting most operating systems provide to let users reduce the animations they see across everything they do, and we can respect that setting as part of animations we create. The code that sets `enableAnimations` looks as follows: ```js // Handle accessibility let enableAnimations = false; let reduceMotionQuery = matchMedia("(prefers-reduced-motion)"); // Handle animation accessibility preferences function setAccessibilityState() { if (reduceMotionQuery.matches) { enableAnimations = false; } else { enableAnimations = true; } } setAccessibilityState(); reduceMotionQuery.addListener(setAccessibilityState); ``` The system accessibility preference for reducing animation is represented by the **prefers-reduced-motion** media query feature. What we do here is poll for that feature's value and set the `enableAnimations` property to specify whether we play the falling snow animation or we don't. #### 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](http://www.krilnon.com)'s excellent [ Constraining the Falling Snow Effect](constraining_the_falling_snow_effect.htm) tutorial. ## Conclusion So, there you have it - a look at how HTML, CSS, and JavaScript come together to create the animated falling snow effect that you see. If you've been following the [animation tutorials on this site](https://www.kirupa.com/html5/learn_animation.htm), most of what you saw here should have been a review. What isn't a review is how everything comes together to create the final effect that you see. That is always the most fun as well as the most tricky part of combining all of the various individual tips and techniques we tend to go deep on. If you do want to go deep on any subtopic this effect exposes, read the following tutorials: [Creating, Removing, and Cloning Elements](https://www.kirupa.com/html5/creating_dom_elements_and_other_stuff.htm), [Animating with requestAnimationFrame](https://www.kirupa.com/html5/animating_with_requestAnimationFrame.htm), [Animating Many Things](https://www.kirupa.com/html5/animating_many_things_on_a_canvas.htm), [Toggling Animations On and Off](https://www.kirupa.com/html5/toggling_animations_on_off.htm), [Running Your Code at the Right Time](https://www.kirupa.com/html5/running_your_code_at_the_right_time.htm), [Generating Random Numbers](https://www.kirupa.com/html5/random_numbers_js.htm), [Animating Movement Smoothly Using CSS](https://www.kirupa.com/html5/animating_movement_smoothly_using_css.htm), [A Deeper Look at Objects](https://www.kirupa.com/html5/a_deeper_look_at_objects_in_javascript.htm), [Introduction to Easing](https://www.kirupa.com/html5/introduction_to_easing_in_javascript.htm), and a few more!