Snow Fall
15 CSS circles in free fall, 3 distinct drift trajectories, staggered durations and delays — a snow atmosphere without a single line of JavaScript.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Atmosphere has 57 effects, including 10 free. Explore the category →
3 usage examples



How it works
The effect uses 3 @keyframes sequences to simulate three drift behaviours: snowFall1 drifts 20 px right (translateX(20px)), snowFall2 15 px left (translateX(-15px)), and snowFall3 oscillates via a 50 % waypoint (+10 px) before returning to –10 px. Each flake travels 230 px vertically (translateY(-10px → 220px)) while rotating ±360°. The ease timing function slows the start and end — flakes glide rather than falling in a straight line.
The 15 snowflakes are 15 absolutely positioned <div> elements with border-radius: 50% — simple white circles. Each :nth-child(n) rule individually sets the horizontal position (left from 5% to 95%), diameter (2 px to 5 px), duration (6 s to 9.5 s), delay (0 s to 3.2 s) and opacity (0.5 to 0.9). Staggered speeds and delays create an illusion of depth and density without touching JavaScript.
The .snow-container (position: absolute; inset: 0; overflow: hidden) carries a 4-stop linear gradient — #2c3e50 → #3498db at 40% → #ecf0f1 at 95% → #fff — evoking a twilight sky above a snow-covered ground. White flakes remain legible against the blue upper sky and naturally fade into the pale lower section, mimicking snow touching the ground.
There is no JavaScript: no requestAnimationFrame, no canvas, no particle pool. All animation is driven by the browser's CSS engine — zero main-thread cost. Rendering delegates to the GPU compositor as only transform and opacity are animated, ensuring 60 fps even on modest mobile hardware.
Accessibility
- prefers-reduced-motion not implemented: the effect does not check this OS preference — animations loop regardless of user settings. Fix: add
@media (prefers-reduced-motion: reduce) { .snowflake { animation: none; } }. - No
aria-hiddenattribute is set on.snow-containeror the 15 empty<div class="snowflake">elements — a screen reader will traverse them needlessly. Addaria-hidden="true"to the container. - The animation runs
infinitewith no pause mechanism: this violates WCAG 2.2.2 for motion-sensitive users. Provide a pause button if the effect is displayed for more than 5 seconds. - The
.snow-labelis a<span>with no role or heading level — replace with a semantically appropriate element (<h2>,<p>) depending on context. - No keyboard or pointer interaction: the effect is purely decorative, which is acceptable as long as it carries no functional information.
Browser compatibility
Uses CSS Animations Level 1 and :nth-child selectors — supported in all modern browsers since 2013.
Without CSS Animation support (IE 9 and earlier), the 15 snowflakes stay static at the top of the container — the gradient background still displays, no JS errors.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="demo-preview">
<div class="snow-container">
<div class="snowflake"></div>
<div class="snowflake"></div>
<div class="snowflake"></div>
<!-- × 15 total -->
</div>
<span class="snow-label">Snow Fall</span>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| @keyframes snowFall1/2/3 — translateX | +20 px / –15 px / ±10 px | Lateral drift amplitude. Positive = rightward (snowFall1), negative = leftward (snowFall2). Increase to ±40 px to simulate strong wind. |
| .snowflake:nth-child(n) width / height | 2 px to 5 px | Diameter of each flake. Larger foreground flakes (5–8 px) strengthen depth; keep far-away flakes at 2–3 px. |
| .snowflake:nth-child(n) animation-duration | 6 s to 9.5 s | Individual fall speed. Reduce to 3–5 s for a blizzard, increase to 12–18 s for slow, gentle snowfall. |
| .snowflake:nth-child(n) animation-delay | 0 s to 3.2 s | Start offset: distributes flakes across the height on load. Extending to 6 s spreads out density further. |
| .snowflake:nth-child(n) opacity | 0.5 to 0.9 | Per-flake transparency. Far-away (small) flakes should have lower opacity (0.3–0.5) to maintain depth perception. |
| .snow-container background | linear-gradient(#2c3e50 0, #3498db 40%, #ecf0f1 95%, #fff 100%) | Sky gradient. Change colours to match your brand (polar night, dusk, or transparent for overlay use). |
| Number of .snowflake divs in HTML | 15 divs | Add or remove <div class="snowflake"> elements and create matching :nth-child(n) rules. 8–10 = subtle, 20–25 = dense blizzard. |
FAQ
background: #fff on .snowflake with #a0c4e8 (pale blue) or #90b4d0 to make them visible. If your background is provided by the parent page, set .snow-container { background: transparent; } and move your gradient to the parent element.transform and opacity — the two properties composited by the GPU without triggering repaint or reflow. At 15 flakes, GPU load is negligible even on mobile. If you go above 20 flakes, stay on these two properties only to maintain 60 fps.animation-play-state property. Add .snow-paused .snowflake { animation-play-state: paused; } to your CSS, then call container.classList.toggle('snow-paused') from JS. To restart from the beginning, remove the class and re-add it — each flake's delay resets.