Creating Falling Snow using HTML 5 and JS

Great question, I actually wrote a snow plugin a while ago that I continually update see it in action. Also a link to the pure js source

I noticed you tagged the question html5 and canvas, however you can do it without using either, and just standard elements with images or different background colors.

Here’s two really simple ones I put together just now for you to mess with. The key in my opinion is using sin to get the nice wavy effect as the flakes fall. The first one uses the canvas element, the 2nd one uses regular dom elements.

Since I’m absolutely addicted to canvas here’s a canvas version that performs quite nicely in my opinion.

Canvas version

Full Screen

(function() {
    var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame ||
    function(callback) {
        window.setTimeout(callback, 1000 / 60);
    };
    window.requestAnimationFrame = requestAnimationFrame;
})();


var flakes = [],
    canvas = document.getElementById("canvas"),
    ctx = canvas.getContext("2d"),
    flakeCount = 200,
    mX = -100,
    mY = -100

    canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

function snow() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    for (var i = 0; i < flakeCount; i++) {
        var flake = flakes[i],
            x = mX,
            y = mY,
            minDist = 150,
            x2 = flake.x,
            y2 = flake.y;

        var dist = Math.sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y)),
            dx = x2 - x,
            dy = y2 - y;

        if (dist < minDist) {
            var force = minDist / (dist * dist),
                xcomp = (x - x2) / dist,
                ycomp = (y - y2) / dist,
                deltaV = force / 2;

            flake.velX -= deltaV * xcomp;
            flake.velY -= deltaV * ycomp;

        } else {
            flake.velX *= .98;
            if (flake.velY <= flake.speed) {
                flake.velY = flake.speed
            }
            flake.velX += Math.cos(flake.step += .05) * flake.stepSize;
        }

        ctx.fillStyle = "rgba(255,255,255," + flake.opacity + ")";
        flake.y += flake.velY;
        flake.x += flake.velX;

        if (flake.y >= canvas.height || flake.y <= 0) {
            reset(flake);
        }


        if (flake.x >= canvas.width || flake.x <= 0) {
            reset(flake);
        }

        ctx.beginPath();
        ctx.arc(flake.x, flake.y, flake.size, 0, Math.PI * 2);
        ctx.fill();
    }
    requestAnimationFrame(snow);
};

function reset(flake) {
    flake.x = Math.floor(Math.random() * canvas.width);
    flake.y = 0;
    flake.size = (Math.random() * 3) + 2;
    flake.speed = (Math.random() * 1) + 0.5;
    flake.velY = flake.speed;
    flake.velX = 0;
    flake.opacity = (Math.random() * 0.5) + 0.3;
}

function init() {
    for (var i = 0; i < flakeCount; i++) {
        var x = Math.floor(Math.random() * canvas.width),
            y = Math.floor(Math.random() * canvas.height),
            size = (Math.random() * 3) + 2,
            speed = (Math.random() * 1) + 0.5,
            opacity = (Math.random() * 0.5) + 0.3;

        flakes.push({
            speed: speed,
            velY: speed,
            velX: 0,
            x: x,
            y: y,
            size: size,
            stepSize: (Math.random()) / 30,
            step: 0,
            angle: 180,
            opacity: opacity
        });
    }

    snow();
};

canvas.addEventListener("mousemove", function(e) {
    mX = e.clientX,
    mY = e.clientY
});

init();​

Standard element version

var flakes = [],
    bodyHeight = getDocHeight(),
    bodyWidth = document.body.offsetWidth;


function snow() {
    for (var i = 0; i < 50; i++) {
        var flake = flakes[i];

        flake.y += flake.velY;

        if (flake.y > bodyHeight - (flake.size + 6)) {
            flake.y = 0;
        }

        flake.el.style.top = flake.y + 'px';
        flake.el.style.left = ~~flake.x + 'px';

        flake.step += flake.stepSize;
        flake.velX = Math.cos(flake.step);

        flake.x += flake.velX;

        if (flake.x > bodyWidth - 40 || flake.x < 30) {
            flake.y = 0;
        }
    }
    setTimeout(snow, 10);
};


function init() {
    var docFrag = document.createDocumentFragment();
    for (var i = 0; i < 50; i++) {
        var flake = document.createElement("div"),
            x = Math.floor(Math.random() * bodyWidth),
            y = Math.floor(Math.random() * bodyHeight),
            size = (Math.random() * 5) + 2,
            speed = (Math.random() * 1) + 0.5;

        flake.style.width = size + 'px';
        flake.style.height = size + 'px';
        flake.style.background = "#fff";

        flake.style.left = x + 'px';
        flake.style.top = y;
        flake.classList.add("flake");

        flakes.push({
            el: flake,
            speed: speed,
            velY: speed,
            velX: 0,
            x: x,
            y: y,
            size: 2,
            stepSize: (Math.random() * 5) / 100,
            step: 0
        });
        docFrag.appendChild(flake);
    }

    document.body.appendChild(docFrag);
    snow();
};

document.addEventListener("mousemove", function(e) {
    var x = e.clientX,
        y = e.clientY,
        minDist = 150;

    for (var i = 0; i < flakes.length; i++) {
        var x2 = flakes[i].x,
            y2 = flakes[i].y;

        var dist = Math.sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y));

        if (dist < minDist) {
            rad = Math.atan2(y2, x2), angle = rad / Math.PI * 180;

            flakes[i].velX = (x2 / dist) * 0.2;
            flakes[i].velY = (y2 / dist) * 0.2;

            flakes[i].x += flakes[i].velX;
            flakes[i].y += flakes[i].velY;
        } else {
            flakes[i].velY *= 0.9;
            flakes[i].velX
            if (flakes[i].velY <= flakes[i].speed) {
                flakes[i].velY = flakes[i].speed;
            }
        }
    }
});

init();

function getDocHeight() {
    return Math.max(
    Math.max(document.body.scrollHeight, document.documentElement.scrollHeight), Math.max(document.body.offsetHeight, document.documentElement.offsetHeight), Math.max(document.body.clientHeight, document.documentElement.clientHeight));
}​

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)