I built a small script that creates a falling snow effect on a webpage. It works without images or libraries. Everything is done with plain HTML, CSS, and a few lines of JavaScript.
You start by placing a fixed container behind the page content. This container holds every snowflake. Each flake is a small div that uses one of several Unicode snow characters.
<div id="snow"></div>
CSS handles the animation. A single keyframe rule moves the flakes from above the viewport to below it. Opacity fades out near the end which gives a softer finish.
#snow {
position: fixed;
inset: 0;
pointer-events: none;
z-index: -1;
overflow: hidden;
}
.snowflake {
position: absolute;
top: -26px;
color: white;
opacity: 0.8;
animation: fall linear infinite;
}
@keyframes fall {
to {
transform: translateY(110vh);
opacity: 0;
}
}
JavaScript creates the flakes. It picks a random character, a random font size, a random horizontal position, and random timing values. These small variations keep the motion from feeling repetitive. The script adjusts the number of flakes based on screen width so a phone shows fewer flakes to keep the content clear while a large desktop can display more without crowding the page.
const container = document.getElementById("snow");
const snowflakeTypes = ["❄", "❅", "❆", "❉", "❋"];
const w = window.innerWidth || document.documentElement.clientWidth;
const numFlakes =
w < 480 ? 20 :
w < 768 ? 40 :
w < 1200 ? 60 :
80;
for (let i = 0; i < numFlakes; i++) {
const f = document.createElement("div");
f.className = "snowflake";
f.textContent = snowflakeTypes[Math.floor(Math.random() * snowflakeTypes.length)];
f.style.fontSize = 8 + Math.random() * 18 + "px";
f.style.left = Math.random() * 100 + "vw";
f.style.animationDuration = 6 + Math.random() * 12 + "s";
f.style.animationDelay = Math.random() * 10 + "s";
container.appendChild(f);
}
You can also add a nice touch by making the snow sway with the horizontal movement of the cursor. Demo
let mouseX = 0;
let targetX = 0;
document.addEventListener("mousemove", e => {
targetX = (e.clientX / window.innerWidth - 0.5) * 80; // max sway
});
function animate() {
mouseX += (targetX - mouseX) * 0.05;
container.style.transform = `translateX(${mouseX}px)`;
requestAnimationFrame(animate);
}
animate();
If you would like a more advanced version of the script, where the snowflakes sway as they fall, pile up at the bottom of the screen, and then slowly melt, you can see a demo here.