DEV Community

Cover image for JavaScript Snow
James Bubb
James Bubb

Posted on

JavaScript Snow

Here's how you can add a snow animation with JavaScript to your site just for the festive season :)

See a demo by watching the tutorial here and for my thoughts on why it's a bad idea!

1. Setup Constants

Let's setup some variables to hold some info on the appearance of our snow animation.



const NUMBER_OF_SNOWFLAKES = 300;
const MAX_SNOWFLAKE_SIZE = 5;
const MAX_SNOWFLAKE_SPEED = 2;
const SNOWFLAKE_COLOUR = '#ddd';

const snowflakes = [];


Enter fullscreen mode Exit fullscreen mode

We'll also create an empty array called snowflakes which is going to keep track of all the individual snowflakes on the page.

2. Create a canvas

All our snowflakes are going to be drawn onto a canvas element so let's create that and drop it onto the current document.



const canvas = document.createElement('canvas');
canvas.style.position = 'absolute';
canvas.style.pointerEvents = 'none';
canvas.style.top = '0px';
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);

const ctx = canvas.getContext('2d');


Enter fullscreen mode Exit fullscreen mode

We're sizing the canvas to fit the entire window and setting the pointer-event CSS property to none so it makes the canvas invisible and the user can still click things behind it.

Finally, we'll get the 2d canvas context, which we can use to draw our snowflakes on.

But first, we need a function to generate a snowflake.

 3. Create a snowflake



const createSnowflake = () => ({
    x: Math.random() * canvas.width,
    y: Math.random() * canvas.height,
    radius: Math.floor(Math.random() * MAX_SNOWFLAKE_SIZE) + 1,
    color: SNOWFLAKE_COLOUR,
    speed: Math.random() * MAX_SNOWFLAKE_SPEED + 1,
    sway: Math.random() - 0.5
});


Enter fullscreen mode Exit fullscreen mode

This function will create and return a snowflake object which is at some random position on the page. We'll also create a random size (radius) for it, set its colour to the constant we defined and also give it a random speed with a minimum value.

In the second part of the tutorial, we introduce a sway property in order to move the snowflake either left or right as it falls down the page.

Now we need a way to draw the snowflake onto the canvas.

4. Draw a snowflake



const drawSnowflake = snowflake => {
    ctx.beginPath();
    ctx.arc(snowflake.x, snowflake.y, snowflake.radius, 0, Math.PI * 2);
    ctx.fillStyle = snowflake.color;
    ctx.fill();
    ctx.closePath();
};


Enter fullscreen mode Exit fullscreen mode

We're just going to draw a circle with a radius as specified by the property on the snowflake object that's passed in.

5. Update a snowflake



const updateSnowflake = snowflake => {
    snowflake.y += snowflake.speed;
    snowflake.x += snowflake.sway;
    if (snowflake.y > canvas.height) {
        Object.assign(snowflake, createSnowflake());
    }
};


Enter fullscreen mode Exit fullscreen mode

Here, we'll take one of our snowflake objects and update its x and y positions on the page. If it gets to the bottom of the page, we'll re-assign the reference of the snowflake to a new snowflake object.

6. Animating snowflakes



const animate = () => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    snowflakes.forEach(snowflake => {
        updateSnowflake(snowflake);
        drawSnowflake(snowflake);
    });

    requestAnimationFrame(animate);
};


Enter fullscreen mode Exit fullscreen mode

The animate function clears the current canvas and then loops through our array of snowflakes and updates their position, then draws them onto the canvas.

We can make a really smooth animation by simply calling this function repeatedly by passing it to requestAnimationFrame.

Of course, we haven't put any snowflake objects into our snowflakes array yet so let's populate those now and kick off the animation by calling the animate function.



for (let i = 0; i < NUMBER_OF_SNOWFLAKES; i++) {
    snowflakes.push(createSnowflake());
}

animate();


Enter fullscreen mode Exit fullscreen mode

We should have a fully working snowfall animation on our page now!

7. Adding Event Listeners

If you try and resize your browser window at this point or scroll down to more content (if it exists on your page) then you'll notice that the snowfall effect is fixed to the top of the page and doesn't resize to fit the browser window.

Let's finish off our beautiful snowfall animation with a couple of event listeners to handle this.



window.addEventListener('resize', () => {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
});

window.addEventListener('scroll', () => {
    canvas.style.top = `${window.scrollY}px`;
});


Enter fullscreen mode Exit fullscreen mode

Because we've written everything in JavaScript with no extra HTML/CSS required, we can just copy the code and drop it into any site. For example, here's how it looks on Amazon's site:

JavaScript snow on Amazon

Conclusion

As mentioned in the video, this is just a bit of fun and not something you should probably add to your site unless your company is insistent on sending your site back to the 1990s :)

Thanks for reading and be sure to check out the full tutorial here if you want more details:

Top comments (3)

Collapse
 
madurapa profile image
Maduka Jayalath

Thank you! very useful

Collapse
 
exceptionallybrad profile image
Benny Ball

Nice script! I made a small tweak so the snow forms more uniformly across the viewport. Change the "y" calculation slightly and the snow won't appear so sparse up top and heavy at the bottom:

const createSnowflake = () => ({
    x: Math.random() * canvas.width,
    /* this will offset the snow to be more vertically uniform across the page */
    y: (Math.random() * canvas.height) - (Math.random() * canvas.height * .5),  
    radius: Math.floor(Math.random() * MAX_SNOWFLAKE_SIZE) + 1,
    color: SNOWFLAKE_COLOUR,
    speed: Math.random() * MAX_SNOWFLAKE_SPEED + 1,
    sway: Math.random() - 0.5 // next
});
Enter fullscreen mode Exit fullscreen mode

Cheers!

Collapse
 
lepepe_37 profile image
José González

Instead of updating canvas top when scrolling, I've changed position: absolute to position: fixed

check github.com/le-pepe/snow-effect