Dust Motes
Ten golden dust particles drift through an oblique light beam — pure CSS animation, no JavaScript, staggered durations and delays for organic, independent movement.
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 relies entirely on three CSS @keyframes (dustFloat1, dustFloat2, dustFloat3) applied to ten circular <div class="dust-mote"> elements (1–3 px, border-radius:50%). Each keyframe defines 4 waypoints (0%, 25%, 50%, 75%) with offset X/Y translations (±3–8 px horizontally, −5 to −30 px vertically) and opacity shifts (0.3–0.9). The ease-in-out timing decelerates at both ends of each segment, mimicking the real inertia of airborne particles.
Trajectory diversity comes entirely from CSS: each .dust-mote:nth-child(N) rule assigns a different duration (6–10 s) and a staggered delay (0–3 s). This desynchronisation breaks the repeating pattern and creates the illusion of independent motion. No values are drawn randomly at runtime — variety is entirely baked into static rules.
The light beam is an absolutely-positioned <div class="dust-beam"> (top:−20%; right:10%; width:120px; height:140%) with a linear-gradient from rgba(255,220,150,.08) to transparent, rotated 15° from transform-origin:right top. The contrast between this golden veil and the dark background (#1a1a2e → #16213e) produces the light-cutting-through-darkness effect.
With no JavaScript, no requestAnimationFrame, and no canvas, the browser's animation engine handles everything via GPU compositing. Motes are position:absolute with fixed px dimensions: they don't affect layout and trigger no reflow. Performance impact is negligible even on mobile.
Accessibility
- No
prefers-reduced-motionhandling in the delivered code: CSS animations run regardless of system preference. For accessible integrations, add@media (prefers-reduced-motion: reduce) { .dust-mote { animation: none; } .dust-beam { opacity: 0.5; } }. - The
.dust-containercarries norole,aria-label, oraria-hidden. Since the effect is purely decorative, addaria-hidden="true"to the wrapper to exclude it from the accessibility tree. - No keyboard or mouse interaction is exposed: no clickable area, no focus trap. Overlay content must remain outside
.dust-containerto stay tabbable. - Mote color:
rgba(255,220,150,.7)on a dark background — clearly visible (contrast > 4.5:1 in normal mode) but, being a non-text decorative element, WCAG 1.4.11 does not apply. - The container background can be removed when overlaying the effect on an existing page — add
pointer-events:noneso neither motes nor the beam capture clicks.
Browser compatibility
Requires CSS @keyframes and the animation property — available since Chrome 43, Firefox 16, Safari 9. No JavaScript dependency.
Without CSS animation support (browsers before 2015), motes remain static at their initial positions with their starting opacity — no errors, the effect degrades to a static background.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="dust-container" aria-hidden="true">
<div class="dust-beam"></div>
<div class="dust-mote"></div>
<div class="dust-mote"></div>
<div class="dust-mote"></div>
<div class="dust-mote"></div>
<div class="dust-mote"></div>
<div class="dust-mote"></div>
<div class="dust-mote"></div>
<div class="dust-mote"></div>
<div class="dust-mote"></div>
<div class="dust-mote"></div>
</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 |
|---|---|---|
| .dust-mote { background } | rgba(255, 220, 150, .7) | Mote color and opacity — change the hue (e.g. blue rgba(150,190,255,.7)) to match your palette without touching the keyframes. |
| .dust-beam { background } (first stop) | rgba(255, 220, 150, .08) | Beam intensity and color — raise to .16 for a clearly visible beam, lower to .04 for subtle. |
| .dust-beam { width } | 120px | Beam width — 80 px for a fine shaft of light, 200 px for a broad luminous wash across the scene. |
| .dust-beam { transform: rotate(Ndeg) } | 15deg | Beam angle from transform-origin:right top — 0° = vertical drop, 45° = pronounced diagonal. |
| .dust-mote:nth-child(N) { animation-duration } | 6s – 10s (per nth-child) | Individual speed per mote — low values (3–5 s) = turbulent dust, high values (12–18 s) = atmospheric slowness. |
| .dust-mote:nth-child(N) { width / height } | 1px – 3px (per nth-child) | Individual mote size — stay under 6 px to keep the 'dust grain' look; larger values give bokeh-style particles. |
| .dust-container { background } | linear-gradient(135deg, #1a1a2e 0, #16213e 100%) | Scene background color — remove the property or set background:transparent to overlay the effect on your own background (also add pointer-events:none). |
FAQ
<div class="dust-mote"></div> inside .dust-container, then write a .dust-mote:nth-child(N) rule with a unique position (top/left), size (width/height), and animation (dustFloat1, dustFloat2, or dustFloat3) with a distinct duration and delay. To remove one, delete its div and matching nth-child rule..dust-container position:absolute; inset:0 inside a position:relative parent, add pointer-events:none to the container, then place your content in a sibling with position:relative; z-index:1. The content stays interactive and accessible.