Atmosphere✨ Premium

Snow Fall

One hundred white snowflakes fall on a 2D canvas — random sizes, speeds and opacity, sinusoidal horizontal sway, continuous RAF loop.

JSCanvasSnow

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

Hero / Landing — Winter store or seasonal campaign — Snow Fall example 1

① Hero / Landing — Winter store or seasonal campaign

WhenHomepage active from November through January, or any snow-, cold-, Christmas-, or Valentine-themed landing page.
Why100 flakes fill the background without overwhelming the message: variable opacity (0.3–0.8) and varied sizes (1–4 px) create natural depth. 100% vanilla — no external images, no network dependencies.
SettingsKeep defaults (count 100, speed 0.5–1.5). Set scene background to night-blue (#0b1329) to enhance the winter atmosphere.
Product component — Weather widget or winter conditions alert — Snow Fall example 2

② Product component — Weather widget or winter conditions alert

WhenA dashboard card or real-time weather notification reporting snowfall — the data and animation are consistent.
WhyThe effect visualizes the weather without an external image or animated SVG. Small flake sizes (1–4 px) remain legible inside a compact container.
SettingsReduce count to 60 (change a < 100a < 60) and cap radius at 2 px (3 * Math.random() + 11.5 * Math.random() + 0.5) for a subtle look inside a product component.
Conversion micro-interaction — Seasonal promotional offer — Snow Fall example 3

③ Conversion micro-interaction — Seasonal promotional offer

WhenA time-limited modal or banner: winter sale, flash deal, promo code tied to a 'price blizzard' campaign.
WhyThe snowfall animation reinforces seasonal urgency and visually distinguishes the modal from static page elements. Dense snow increases the perception of scarcity and a unique moment.
SettingsIncrease fall speed (1 * Math.random() + .52 * Math.random() + 1) and wind (.5 * Math.random() - .251 * Math.random() - 0.5) for a dynamic blizzard feel.

How it works

setupCanvas() reads the parent element's bounding box and resizes the canvas accordingly. At startup, 100 particles are created with independent random values: position (x, y) spread across the full surface, radius r between 1 and 4 px, vertical fall speed speed between 0.5 and 1.5 px/frame, constant horizontal drift wind within ±0.25 px/frame, and opacity between 0.3 and 0.8. These values do not change during a flake's lifetime.

The requestAnimationFrame loop clears the canvas and repaints the #0a0a0f background on every frame. For each flake: y += speed (vertical fall) and x += wind + 0.3 × sin(0.01 × y) — the sinusoidal sway is tied to the Y position, not to time: two flakes at the same height will sway in phase. Flakes are solid arc() calls drawn as rgba(255,255,255, opacity).

Edge wrapping is simple and non-interpolated: a flake that exits at the bottom reappears at the top (y = −5) at a random horizontal position; flakes crossing the left or right edge jump to the opposite side. The pool of 100 flakes is constant — no particle is ever created or destroyed mid-loop.

The effect includes no IntersectionObserver (the RAF runs even when off-screen), no ResizeObserver (the canvas is not recalibrated if the container resizes), and no public API. All constants are hardcoded inside the IIFE.

Accessibility

  • prefers-reduced-motion not implemented: the code does not check the OS preference — the animation always runs. To fix: wrap the RAF loop in a window.matchMedia('(prefers-reduced-motion:reduce)').matches guard.
  • The canvas has no aria-hidden in the source HTML — add aria-hidden="true" to prevent screen readers from attempting to explore a non-textual canvas.
  • No role="img" or aria-label on the container: if the effect is decorative, hide the canvas from assistive tech; if informative, add a descriptive label on the parent element.
  • White flakes rgba(255,255,255,…) on a #0a0a0f (near-black) background yield a contrast ratio > 18:1 — no visual readability issue for the flakes themselves.
  • The effect is purely decorative and exposes no keyboard or mouse interaction — no native focus management required.

Browser compatibility

Requires Canvas 2D Context and requestAnimationFrame — supported in all modern browsers since 2013.

Chrome 88+✓ Full
Firefox 87+✓ Full
Safari 15+✓ Full
Edge 88+✓ Full
Mobile iOS✓ Full
Android Chrome✓ Full

If <code>getElementById('psSnow')</code> returns <code>null</code> (element missing or ID changed), <code>setupCanvas()</code> returns <code>null</code> and the IIFE exits silently — no JS error, the container renders with its CSS background.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="demo-preview">
  <!-- Positioned wrapper — set width/height to match your container -->
  <div class="ps-canvas-wrap" role="img" aria-label="Animated falling snowflakes">
    <canvas class="ps-canvas" id="psSnow" aria-hidden="true"></canvas>
  </div>
</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
count (JS: `a < 100`) 100 Number of active flakes. 60 for a light background, 150 for a dense snowstorm. Above 200, monitor performance on low-end mobile.
radius (JS: `3 * Math.random() + 1`) 1–4 px Flake radius range. Change both constants: `1.5 * Math.random() + 0.5` for finer flakes; `5 * Math.random() + 2` for larger ones.
speed (JS: `1 * Math.random() + .5`) 0.5–1.5 px/frame Vertical fall speed per frame. `0.3 * Math.random() + 0.2` for a slow drizzle; `2 * Math.random() + 1` for a heavy storm.
wind (JS: `.5 * Math.random() - .25`) ±0.25 px/frame Constant horizontal drift per flake. `1 * Math.random() - 0.5` for a visible side wind; `0` for a purely vertical fall.
opacity (JS: `.5 * Math.random() + .3`) 0.3–0.8 Flake opacity range. `0.15 * Math.random() + 0.05` for near-transparent flakes (frost-on-glass effect).
sway amplitude (JS: `0.3 * Math.sin`) 0.3 Amplitude of the sinusoidal horizontal sway. 0 = straight vertical fall; 0.8 = turbulent blizzard swirl.
sway frequency (JS: `0.01 * n.y`) 0.01 Frequency of the sine cycle as a function of Y. 0.03 shortens the cycle (rapid flutter); 0.005 lengthens it (slow, wide undulation).

FAQ

The JS repaints #0a0a0f each frame via ctx.fillRect(0, 0, w, h). For transparent snow over a custom background, remove the two lines fillStyle = '#0a0a0f' and fillRect(...) from the JS, set the canvas to position: absolute; background: transparent, and place your background in CSS behind the canvas.
With 100 flakes on a 392×280 canvas, the load is negligible on desktop (< 1% on a modern CPU). On low-end mobile or when multiple canvases are running simultaneously, reduce count to 50 and wrap the RAF loop in an IntersectionObserver to pause it when the canvas scrolls out of view.
The color is hardcoded as rgba(255,255,255, opacity). Replace 255,255,255 with the desired RGB values — for example 173,216,230 for ice-blue or 255,220,180 for cherry blossom petals. For multiple colors, define a color array and pick one randomly when each flake is initialized.