Christmas snowflake animation

It might be a bit of a cliche, but everyone loves a bit of Christmas customisation. The thought of adding little seasonal Easter eggs is like catnip to web developers and at Pixel Pixel we are no different…

Last year we fancied a bit of a Christmas spruce up so we added a snowfall animation backdrop to our menu sidebar to give it a little extra pizzazz. We loved our little addition so much that it has since become a permanent fixture of our site, set to automatically switch on in December.

Below, we’ll show you an idea of how to create your own version using SCSS loops and randomised values, the principles of which we discussed recently.

We’re going to run you through the process quite quickly, but the full code is provided at the end of the article so try to follow along using this. Hopefully, the whole set-up is fairly clear, but if not then please do let us know and we can help you to set up your own seasonal decoration!

See the Pen Xmas Snowflakes – randomised SCSS by Pixel Pixel (@pxpxltd) on CodePen.dark


The method.

We have 20 snowflakes and we want them to look and behave as if they are each as unique as a real snowflake. To do this, we need to give each flake a set of randomised style properties to make them behave differently to their compatriots.To make this happen, we’re going to need to loop through our list of snowflakes and start getting some random values. For each instance of the loop, the random() calls will return different values. These randomised property values will then be applied via a master animation.
note: SCSS @for loops are 1 indexed! 

First, we need to create the loop:

As we are looping through an arbitrary number of iterations rather than looping over an object, we’re going to be using a @for loop.

The for loop is created in a similar fashion to a for loop in Javascript, although the syntax is different. We need an index, a startpoint and an endpoint.

e.g. '@for index from startpoint through endpoint'

For our example, we are setting 20 snowflakes so that the loop runs from 1 until 21. In reality you will likely want to use rather more, but the beauty of this setup is that you can change variables like the number of snowflakes very easily.

We now need to initialise all of our loop variables:

We’ll need these to make the snowflakes fall and drift randomly. Be aware that these are scoped to their parent function.

Set these as you would a normal SCSS variable, within the parent loop (see the example for clarity).

  • $time : The number of seconds it takes a snowflake to fall to the bottom of the screen.
  • $delay : The number of seconds a snowflake delays its fall for – to create a constant snowfall.
  • $size :  The scaled size of each snowflake – to add variety.
  • $positiony : The vertical starting position of the snowflake.
  • $positionx : The horizontal starting position of the snowflake.
  • $drift : The rate of horizontal drift of the snowflake.

Note that we can return negative or minimum values in our random() statements by doing a calculation to the return value.

For example, to give us a fall time of at least 10 and up to 18…

$time: random(8) + 10 +s;

Or to get a value range from -5 to +5…

$delay: random(10) - 5 +s;

Next, the animation needs to be set up:

We will use a separate loop to declare our snowfall keyframes. These are the unique animations which will be applied to each separate snowflake.

Interpolation is necessary to apply the animation to all of our snowflakes – the aim here is to append the index of the loop iteration to the end of the animation name, creating 20 different animations. If the animation doesn’t have a unique name, then it will simply be overwritten and not random!

The index variables $i and $e are available from when we created our loop, so we can use these to select every snowflake via the CSS nth-child() selector.


'.flake:nth-child(#{$i})'   will return ‘.flake:nth-child(1)’ etc.

'@keyframes snowfall#{$e}  will return ‘@keyframes snowfall-1’ etc.

If we use the same loop iteration value for both loops, then an @keyframe declaration will be created for each snowfall animation declared in the main loop. Here we are using $e as the second loop index value but creating a complimentary $e value in our original loop. This value is increased at each loop iteration to make sure that both $e variables stay consistent.

Finally, we can go ahead and apply the randomised CSS to the individual snowflakes.

This is when it all comes together.

As the loop iterates over each item, we will apply the unique animation and randomised variables to each, creating the illusion of random behaviour.

Try to figure out by yourself which variable matches up with which property from the provided code and connect them.

  • e.g. the $delay variable is used for the animation-delay property etc.

If added correctly, you should get 20 randomly moving snowflakes! Success! :]

Example code:

    @for $i from 1 through 21 {
  $e: $i + 1;
  $time: random(8) + 10 +s;
  $delay: random(10) - 5 +s;
  $size: random(1) - 1.5;
  $positiony: random(150) +px;
  $positionx: random(750) +px;
  .flake:nth-child(#{$i}) {
    animation: snowfall#{$e} $time infinite;
    animation-delay: $delay;
    animation-timing-function: ease-in;
    animation-fill-mode: forwards;
    transform: scale($size);
    top: $positiony;  
    left: $positionx;
    position: absolute;
@for $e from 1 through 21 {
  $drift: random(20)-10 + em;
  @keyframes snowfall#{$e} {
    0% {
      opacity: 0;
    10% {
      opacity: 0.5;
    75% {
      opacity: 0.8;
    100% {
      transform: translate($driftl, 20em);
      opacity: 0.1;

It might be a bit twee but we quite like it 🙂 Take a look at our site to check out how we implemented our version and try hovering over our logo to get a little surprise 🙂

Further Reading:

Latest blog posts

Change is here! Pixel Pixel launches its new website.

You've probably noticed by now, but our site has had a bit of a rebuild - here's ...

Christmas Business Update

As we prepare to say goodbye to 2021 and welcome in the new year we wanted to share some equally ...

Our Work

We evolved an existing forms system to become an integral part of GWR’s business operations.

We created a website for Europe’s leading product management training provider.

Contact us