
Web Weather Creator
- 112 installs
- 178 repo stars
- Updated July 14, 2026
- erichowens/some_claude_skills
Create stylized atmospheric effects (clouds, waves, lightning, aurora) using SVG filters and CSS.
About
Mastery of premium stylized weather effects via layered SVG and CSS animations. Covers 3-layer parallax technique, split z-index strategy, and performance optimization.
- Split-layer solution for clouds floating through content
- Organic motion with varied speeds and cubic-bezier easing
Web Weather Creator by the numbers
- 112 all-time installs (skills.sh)
- Ranked #1,083 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/erichowens/some_claude_skills --skill web-weather-creatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 112 |
|---|---|
| repo stars | ★ 178 |
| Last updated | July 14, 2026 |
| Repository | erichowens/some_claude_skills ↗ |
What it does
Create stylized atmospheric effects (clouds, waves, lightning, aurora) using SVG filters and CSS.
Files
Web Weather Creator
Master of stylized atmospheric effects using SVG filters and CSS animations. Creates clouds, waves, lightning, rain, fog, aurora borealis, god rays, lens flares, twilight skies, and ocean spray—all with a premium aesthetic that's stylized but never cheap-looking.
Activation Triggers
Activate on: "clouds", "waves", "weather effects", "atmospheric", "lightning", "rain", "fog", "mist", "aurora", "northern lights", "god rays", "crepuscular", "lens flare", "twilight", "sunset gradient", "golden hour", "ocean spray", "beach wash", "foam", "smoke", "parallax sky", "water ripples", "animated background"
NOT for:
- Photorealistic renders (use WebGL/Three.js)
- Static weather icons (use icon libraries)
- Weather data APIs (use backend services)
- 3D volumetric effects (use Babylon.js/Three.js)
---
Philosophy: Stylized, Not Cheap
The goal is premium stylization—effects that feel intentional and artistic, not like stock footage or placeholder art.
The Three Principles
1. Layering Creates Depth - Multiple semi-transparent layers with parallax motion 2. Organic Motion - Varied speeds, easing, and subtle randomization 3. Restraint - One hero effect, subtle supporting elements
Quality Spectrum
CHEAP ──────────────────────── STYLIZED ──────────────────────── REALISTIC
CSS gradient blob SVG filter + layered opacity WebGL particles
Single div animation 3-5 layers with parallax Fluid simulation
Linear easing Cubic-bezier + varied timing Physics engineTarget Zone: The middle—stylized enough to feel crafted, performant enough for production.
---
Clouds Floating Through Content (Z-Index Strategy)
The most common question: "How do I make clouds float through my content, not just behind it?"
The Split-Layer Solution
<div class="atmosphere-container">
<!-- BACK clouds (behind content) -->
<div class="clouds clouds--back"></div>
<!-- Content sits in the middle -->
<main class="content">
<div class="card">Your content here</div>
</main>
<!-- FRONT clouds (in front of content, but transparent) -->
<div class="clouds clouds--front"></div>
</div>.atmosphere-container {
position: relative;
isolation: isolate; /* Creates stacking context */
}
.clouds {
position: absolute;
inset: 0;
pointer-events: none; /* CRITICAL: Let clicks through */
filter: url(#cloud-cumulus);
}
.clouds--back {
z-index: -1;
opacity: 0.3;
transform: scale(1.5);
animation: drift 120s linear infinite;
}
.content {
position: relative;
z-index: 1;
}
.clouds--front {
z-index: 2;
opacity: 0.12; /* LOW opacity so content is readable */
transform: scale(0.8);
animation: drift 50s linear infinite reverse;
/* Optional: Mask to clear space around content */
mask-image: linear-gradient(
to bottom,
black 0%,
transparent 30%,
transparent 70%,
black 100%
);
}Key Principles
1. Split clouds into 2-3 layers (back, mid, front) 2. Front clouds use LOW opacity (10-15%) so content remains visible 3. Always use `pointer-events: none` on cloud layers 4. Use masks to clear space around important content 5. Vary animation speeds for parallax depth (back = slow, front = fast)
Blend Modes for Natural Integration
.clouds--front {
mix-blend-mode: soft-light; /* Blends naturally with content */
opacity: 0.25;
}
/* Or for white clouds on dark backgrounds */
.clouds--front {
mix-blend-mode: screen;
opacity: 0.15;
}Content Readability
/* Add subtle backdrop to cards so text is always readable */
.card {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(4px); /* Blurs back clouds for extra depth */
}→ See `references/layering-strategies.md` for complete patterns including parallax, content-aware masking, and React components.
---
Core Technique: SVG Filter Chain
All atmospheric effects build on this filter architecture:
<svg width="0" height="0">
<defs>
<filter id="atmosphere" x="-50%" y="-50%" width="200%" height="200%">
<!-- 1. Generate noise texture -->
<feTurbulence
type="fractalNoise" <!-- or "turbulence" for water -->
baseFrequency="0.01" <!-- Lower = larger shapes -->
numOctaves="4" <!-- 3-5 for good detail -->
seed="1" <!-- Change for variation -->
result="noise"
/>
<!-- 2. Distort the source -->
<feDisplacementMap
in="SourceGraphic"
in2="noise"
scale="30" <!-- Distortion intensity -->
xChannelSelector="R"
yChannelSelector="G"
result="displaced"
/>
<!-- 3. Soften edges -->
<feGaussianBlur
in="displaced"
stdDeviation="2"
result="blurred"
/>
<!-- 4. Add lighting (optional) -->
<feDiffuseLighting
in="noise"
lighting-color="white"
surfaceScale="2"
result="lit"
>
<feDistantLight azimuth="45" elevation="60" />
</feDiffuseLighting>
<!-- 5. Composite layers -->
<feComposite in="blurred" in2="lit" operator="multiply" />
</filter>
</defs>
</svg>Key Parameter Reference
| Parameter | Effect | Range | Notes |
|---|---|---|---|
type="fractalNoise" | Soft, cloudy | - | Clouds, smoke, fog |
type="turbulence" | Wavy, liquid | - | Water, waves, ripples |
baseFrequency | Shape size | 0.001-0.1 | Lower = larger shapes |
numOctaves | Detail level | 1-8 | 3-5 optimal, >5 diminishing returns |
scale (displacement) | Distortion | 0-100 | Start at 20-30 |
stdDeviation (blur) | Softness | 0-20 | 2-5 for clouds, 0-2 for water |
---
SECTION A: CLOUD EFFECTS
Cloud Type Recipes
| Cloud Type | baseFrequency | numOctaves | Blur | Character |
|---|---|---|---|---|
| Cumulus (fluffy) | 0.008 | 4 | 3-5 | Puffy, cotton-like |
| Cirrus (wispy) | 0.003 | 5 | 1-2 | Thin, streaky |
| Stratus (flat) | 0.015 | 2 | 5-8 | Low, uniform |
| Storm (dramatic) | 0.006 | 5 | 2-3 | Dense, textured |
| Cartoon | 0.02 | 2 | 8-12 | Soft, simple |
Multi-Layer Cloud System
.cloud-layer {
position: absolute;
inset: 0;
background: radial-gradient(
ellipse 200% 100% at 50% 100%,
rgba(255, 255, 255, 0.9) 0%,
transparent 70%
);
filter: url(#cloud-filter);
}
/* Layer hierarchy: back → front */
.cloud-back {
opacity: 0.3;
transform: scale(1.5);
animation: drift 120s linear infinite;
}
.cloud-mid {
opacity: 0.5;
transform: scale(1.2);
animation: drift 80s linear infinite;
}
.cloud-front {
opacity: 0.7;
transform: scale(1);
animation: drift 50s linear infinite;
}
@keyframes drift {
from { transform: translateX(-10%); }
to { transform: translateX(10%); }
}Cloud Color Palettes
:root {
/* Daylight clouds */
--cloud-white: rgba(255, 255, 255, 0.9);
--cloud-highlight: rgba(255, 250, 240, 1);
--cloud-shadow: rgba(180, 190, 210, 0.6);
/* Sunset clouds */
--cloud-gold: rgba(255, 200, 100, 0.8);
--cloud-pink: rgba(255, 150, 180, 0.7);
--cloud-purple: rgba(180, 100, 200, 0.5);
/* Storm clouds */
--storm-dark: rgba(60, 70, 90, 0.9);
--storm-purple: rgba(80, 60, 100, 0.7);
--storm-highlight: rgba(200, 210, 230, 0.4);
}---
SECTION B: WAVE & WATER EFFECTS
Critical Difference: Waves Use turbulence
<!-- WATER uses type="turbulence" NOT fractalNoise -->
<feTurbulence
type="turbulence"
baseFrequency="0.01 0.03" <!-- X << Y for horizontal waves -->
numOctaves="3"
result="waves"
/>Wave Type Recipes
| Wave Type | baseFrequency | numOctaves | Scale | Character |
|---|---|---|---|---|
| Ocean swell | 0.005 0.015 | 3 | 40 | Broad, rolling |
| Lake ripples | 0.02 0.04 | 2 | 15 | Small, regular |
| Beach shore | 0.008 0.02 | 4 | 30 | Approaching, foam |
| Pool reflection | 0.03 0.03 | 2 | 10 | Subtle, uniform |
Animated Wave SVG
<svg viewBox="0 0 1440 320" preserveAspectRatio="none">
<defs>
<filter id="wave-distort">
<feTurbulence
type="turbulence"
baseFrequency="0.01 0.02"
numOctaves="3"
result="turbulence"
>
<animate
attributeName="baseFrequency"
dur="20s"
values="0.01 0.02; 0.015 0.025; 0.01 0.02"
repeatCount="indefinite"
/>
</feTurbulence>
<feDisplacementMap
in="SourceGraphic"
in2="turbulence"
scale="20"
xChannelSelector="R"
yChannelSelector="G"
/>
</filter>
</defs>
<path
fill="var(--ocean-color)"
filter="url(#wave-distort)"
d="M0,160 C360,220 720,100 1080,180 C1260,220 1380,140 1440,160 L1440,320 L0,320 Z"
>
<animate
attributeName="d"
dur="10s"
repeatCount="indefinite"
values="
M0,160 C360,220 720,100 1080,180 C1260,220 1380,140 1440,160 L1440,320 L0,320 Z;
M0,180 C360,120 720,200 1080,140 C1260,100 1380,180 1440,140 L1440,320 L0,320 Z;
M0,160 C360,220 720,100 1080,180 C1260,220 1380,140 1440,160 L1440,320 L0,320 Z
"
/>
</path>
</svg>Ocean Color Palettes
:root {
/* Deep ocean */
--ocean-deep: #0c4a6e;
--ocean-mid: #0369a1;
--ocean-surface: #38bdf8;
--ocean-foam: rgba(255, 255, 255, 0.8);
/* Tropical */
--tropical-deep: #047857;
--tropical-mid: #10b981;
--tropical-surface: #6ee7b7;
/* Stormy */
--stormy-deep: #1e3a5f;
--stormy-mid: #334155;
--stormy-surface: #64748b;
/* Sunset reflection */
--sunset-water: linear-gradient(
180deg,
rgba(251, 146, 60, 0.4) 0%,
rgba(14, 165, 233, 0.6) 50%,
#0c4a6e 100%
);
}---
SECTION C: LIGHTNING EFFECTS
SVG Stroke-Dashoffset Lightning
The most effective technique for lightning uses animated stroke-dashoffset on SVG paths:
<svg viewBox="0 0 100 200" class="lightning">
<defs>
<filter id="lightning-glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="3" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<path
class="bolt"
d="M50,0 L45,40 L55,45 L40,90 L60,95 L35,150 L70,100 L50,95 L65,50 L45,45 L60,10 Z"
fill="none"
stroke="white"
stroke-width="2"
filter="url(#lightning-glow)"
/>
</svg>.lightning {
position: absolute;
pointer-events: none;
opacity: 0;
}
.bolt {
stroke-dasharray: 500;
stroke-dashoffset: 500;
}
.lightning.flash {
animation: lightning-flash 0.5s ease-out forwards;
}
.lightning.flash .bolt {
animation: bolt-draw 0.15s ease-out forwards;
}
@keyframes lightning-flash {
0% { opacity: 0; }
10% { opacity: 1; }
20% { opacity: 0.5; }
30% { opacity: 1; }
100% { opacity: 0; }
}
@keyframes bolt-draw {
to { stroke-dashoffset: 0; }
}Lightning Flash Overlay
.lightning-flash-overlay {
position: fixed;
inset: 0;
background: rgba(255, 255, 255, 0);
pointer-events: none;
z-index: 1000;
}
.lightning-flash-overlay.active {
animation: screen-flash 0.3s ease-out;
}
@keyframes screen-flash {
0% { background: rgba(255, 255, 255, 0); }
10% { background: rgba(200, 220, 255, 0.4); }
20% { background: rgba(255, 255, 255, 0); }
30% { background: rgba(200, 220, 255, 0.2); }
100% { background: rgba(255, 255, 255, 0); }
}Branching Lightning Pattern
function generateLightningPath(startX, startY, endY, segments = 8) {
let path = `M${startX},${startY}`;
let x = startX;
let y = startY;
const segmentHeight = (endY - startY) / segments;
for (let i = 0; i < segments; i++) {
const jitter = (Math.random() - 0.5) * 30;
x += jitter;
y += segmentHeight;
path += ` L${x},${y}`;
// Random branch (20% chance)
if (Math.random() < 0.2) {
const branchX = x + (Math.random() - 0.5) * 40;
const branchY = y + segmentHeight * 0.5;
path += ` M${x},${y} L${branchX},${branchY} M${x},${y}`;
}
}
return path;
}---
SECTION D: RAIN EFFECTS
CSS-Only Rain with Custom Properties
.rain-container {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.raindrop {
position: absolute;
width: 2px;
background: linear-gradient(
180deg,
transparent 0%,
rgba(174, 194, 224, 0.5) 50%,
rgba(174, 194, 224, 0.8) 100%
);
border-radius: 0 0 2px 2px;
animation: rain-fall var(--fall-duration) linear infinite;
animation-delay: var(--delay);
left: var(--x);
height: var(--length);
opacity: var(--opacity);
}
@keyframes rain-fall {
0% {
transform: translateY(-100vh);
}
100% {
transform: translateY(100vh);
}
}Rain Generator (JavaScript)
function createRain(container, dropCount = 100) {
for (let i = 0; i < dropCount; i++) {
const drop = document.createElement('div');
drop.className = 'raindrop';
drop.style.setProperty('--x', `${Math.random() * 100}%`);
drop.style.setProperty('--delay', `${Math.random() * 2}s`);
drop.style.setProperty('--fall-duration', `${0.5 + Math.random() * 0.5}s`);
drop.style.setProperty('--length', `${15 + Math.random() * 20}px`);
drop.style.setProperty('--opacity', `${0.3 + Math.random() * 0.5}`);
container.appendChild(drop);
}
}Rain Intensity Presets
/* Light drizzle */
.rain-light {
--drop-count: 50;
--fall-duration-base: 1.2s;
--drop-opacity: 0.3;
}
/* Moderate rain */
.rain-moderate {
--drop-count: 150;
--fall-duration-base: 0.8s;
--drop-opacity: 0.5;
}
/* Heavy downpour */
.rain-heavy {
--drop-count: 300;
--fall-duration-base: 0.5s;
--drop-opacity: 0.7;
}Rain + Wind Angle
.raindrop.windy {
animation: rain-fall-diagonal var(--fall-duration) linear infinite;
}
@keyframes rain-fall-diagonal {
0% {
transform: translate(-50vw, -100vh) rotate(15deg);
}
100% {
transform: translate(50vw, 100vh) rotate(15deg);
}
}---
SECTION E: FOG & MIST EFFECTS
Multi-Layer Fog System
.fog-container {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.fog-layer {
position: absolute;
width: 200%;
height: 100%;
background: url("data:image/svg+xml,...") repeat-x;
opacity: 0.3;
filter: url(#fog-filter);
}
.fog-back {
animation: fog-drift 60s linear infinite;
opacity: 0.2;
transform: scale(1.5);
}
.fog-mid {
animation: fog-drift 40s linear infinite reverse;
opacity: 0.3;
transform: scale(1.2);
}
.fog-front {
animation: fog-drift 25s linear infinite;
opacity: 0.4;
}
@keyframes fog-drift {
from { transform: translateX(-50%); }
to { transform: translateX(0%); }
}SVG Fog Filter
<filter id="fog-filter" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence
type="fractalNoise"
baseFrequency="0.003"
numOctaves="4"
seed="5"
result="noise"
/>
<feGaussianBlur in="noise" stdDeviation="20" result="blur" />
<feColorMatrix
type="matrix"
values="1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 0.5 0"
result="faded"
/>
</filter>Fog Density Variations
:root {
/* Light haze */
--fog-light: rgba(255, 255, 255, 0.2);
--fog-blur-light: 10px;
/* Morning mist */
--fog-mist: rgba(200, 210, 230, 0.4);
--fog-blur-mist: 20px;
/* Dense fog */
--fog-dense: rgba(180, 190, 210, 0.6);
--fog-blur-dense: 40px;
/* Eerie fog (horror) */
--fog-eerie: rgba(100, 120, 140, 0.5);
--fog-blur-eerie: 30px;
}Ground-Hugging Mist
.ground-mist {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 30%;
background: linear-gradient(
to top,
rgba(255, 255, 255, 0.6) 0%,
rgba(255, 255, 255, 0.3) 40%,
transparent 100%
);
filter: url(#fog-filter);
animation: mist-breathe 8s ease-in-out infinite;
}
@keyframes mist-breathe {
0%, 100% {
opacity: 0.5;
transform: scaleY(1);
}
50% {
opacity: 0.7;
transform: scaleY(1.1);
}
}---
SECTION F: TWILIGHT & SKY GRADIENTS
Sky Time Progression
:root {
/* Pre-dawn (5:00-5:30) */
--sky-predawn: linear-gradient(
to top,
#1a1a2e 0%,
#16213e 30%,
#0f3460 60%,
#1a1a2e 100%
);
/* Dawn (5:30-6:30) */
--sky-dawn: linear-gradient(
to top,
#ff6b6b 0%,
#feca57 20%,
#ff9ff3 40%,
#54a0ff 70%,
#1a1a2e 100%
);
/* Golden hour (6:30-7:30) */
--sky-golden: linear-gradient(
to top,
#ff9a56 0%,
#ffbe76 30%,
#ffeaa7 50%,
#74b9ff 80%,
#0984e3 100%
);
/* Midday (10:00-14:00) */
--sky-midday: linear-gradient(
to top,
#74b9ff 0%,
#0984e3 50%,
#0652dd 100%
);
/* Sunset (17:30-18:30) */
--sky-sunset: linear-gradient(
to top,
#e17055 0%,
#fdcb6e 20%,
#f8a5c2 40%,
#686de0 70%,
#30336b 100%
);
/* Dusk (18:30-19:30) */
--sky-dusk: linear-gradient(
to top,
#4a69bd 0%,
#6a89cc 30%,
#b8e994 10%,
#e55039 5%,
#1e3799 60%,
#0c2461 100%
);
/* Night (20:00+) */
--sky-night: linear-gradient(
to top,
#0c2461 0%,
#1e3799 40%,
#0a1931 100%
);
}Animated Sky Transition
.sky-canvas {
background: var(--sky-midday);
transition: background 3s ease-in-out;
}
.sky-canvas[data-time="dawn"] { background: var(--sky-dawn); }
.sky-canvas[data-time="golden"] { background: var(--sky-golden); }
.sky-canvas[data-time="midday"] { background: var(--sky-midday); }
.sky-canvas[data-time="sunset"] { background: var(--sky-sunset); }
.sky-canvas[data-time="dusk"] { background: var(--sky-dusk); }
.sky-canvas[data-time="night"] { background: var(--sky-night); }Sun/Moon Position System
.celestial-body {
position: absolute;
width: 60px;
height: 60px;
border-radius: 50%;
transition: all 2s cubic-bezier(0.4, 0, 0.2, 1);
}
.sun {
background: radial-gradient(
circle,
#fff7e6 0%,
#ffd93d 40%,
#ff8c00 100%
);
box-shadow:
0 0 40px 10px rgba(255, 200, 50, 0.6),
0 0 80px 30px rgba(255, 150, 0, 0.3);
}
.moon {
background: radial-gradient(
circle at 30% 30%,
#f5f5f5 0%,
#e0e0e0 50%,
#bdbdbd 100%
);
box-shadow:
0 0 20px 5px rgba(255, 255, 255, 0.3),
inset -10px -10px 20px rgba(0, 0, 0, 0.1);
}---
SECTION G: AURORA BOREALIS
Stacked Gradient Aurora
.aurora-container {
position: absolute;
inset: 0;
overflow: hidden;
background: linear-gradient(to top, #0a0a1a 0%, #1a1a3a 100%);
}
.aurora-band {
position: absolute;
top: 10%;
left: -50%;
width: 200%;
height: 40%;
opacity: 0.6;
mix-blend-mode: screen;
filter: blur(30px);
}
.aurora-green {
background: linear-gradient(
90deg,
transparent 0%,
rgba(0, 255, 128, 0.4) 20%,
rgba(0, 255, 200, 0.6) 50%,
rgba(0, 255, 128, 0.4) 80%,
transparent 100%
);
animation: aurora-wave 15s ease-in-out infinite;
}
.aurora-purple {
background: linear-gradient(
90deg,
transparent 0%,
rgba(138, 43, 226, 0.3) 25%,
rgba(75, 0, 130, 0.5) 50%,
rgba(138, 43, 226, 0.3) 75%,
transparent 100%
);
animation: aurora-wave 20s ease-in-out infinite reverse;
animation-delay: -5s;
}
.aurora-blue {
background: linear-gradient(
90deg,
transparent 0%,
rgba(0, 150, 255, 0.3) 30%,
rgba(0, 200, 255, 0.4) 50%,
rgba(0, 150, 255, 0.3) 70%,
transparent 100%
);
animation: aurora-wave 18s ease-in-out infinite;
animation-delay: -8s;
}
@keyframes aurora-wave {
0%, 100% {
transform: translateX(-20%) scaleY(1) skewX(-5deg);
opacity: 0.4;
}
25% {
transform: translateX(-10%) scaleY(1.2) skewX(5deg);
opacity: 0.7;
}
50% {
transform: translateX(0%) scaleY(0.8) skewX(-3deg);
opacity: 0.5;
}
75% {
transform: translateX(10%) scaleY(1.1) skewX(3deg);
opacity: 0.6;
}
}Aurora Curtain Effect
.aurora-curtain {
position: absolute;
top: 0;
width: 100%;
height: 60%;
background: repeating-linear-gradient(
90deg,
transparent 0px,
rgba(0, 255, 150, 0.1) 2px,
transparent 4px
);
filter: blur(2px);
animation: curtain-shimmer 3s ease-in-out infinite;
}
@keyframes curtain-shimmer {
0%, 100% { opacity: 0.3; }
50% { opacity: 0.6; }
}Aurora Color Palettes
:root {
/* Classic green */
--aurora-green-1: rgba(0, 255, 128, 0.6);
--aurora-green-2: rgba(50, 255, 180, 0.4);
/* Purple/violet */
--aurora-purple-1: rgba(138, 43, 226, 0.5);
--aurora-purple-2: rgba(186, 85, 211, 0.4);
/* Blue tones */
--aurora-blue-1: rgba(0, 191, 255, 0.4);
--aurora-blue-2: rgba(65, 105, 225, 0.3);
/* Rare red (high activity) */
--aurora-red-1: rgba(255, 69, 0, 0.3);
--aurora-red-2: rgba(255, 99, 71, 0.2);
}---
SECTION H: GOD RAYS (CREPUSCULAR RAYS)
Conic Gradient God Rays
.god-rays-container {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.god-rays {
position: absolute;
top: -50%;
left: 50%;
transform: translateX(-50%);
width: 200%;
height: 200%;
background: conic-gradient(
from 180deg at 50% 0%,
transparent 0deg,
rgba(255, 248, 220, 0.2) 5deg,
transparent 10deg,
transparent 20deg,
rgba(255, 248, 220, 0.15) 25deg,
transparent 30deg,
transparent 45deg,
rgba(255, 248, 220, 0.2) 50deg,
transparent 55deg,
transparent 70deg,
rgba(255, 248, 220, 0.1) 75deg,
transparent 80deg,
transparent 100deg,
rgba(255, 248, 220, 0.15) 105deg,
transparent 110deg,
transparent 130deg,
rgba(255, 248, 220, 0.2) 135deg,
transparent 140deg,
transparent 160deg,
rgba(255, 248, 220, 0.1) 165deg,
transparent 170deg,
transparent 180deg
);
mask-image: radial-gradient(
ellipse 100% 100% at 50% 0%,
black 0%,
transparent 70%
);
animation: rays-pulse 8s ease-in-out infinite;
}
@keyframes rays-pulse {
0%, 100% { opacity: 0.6; }
50% { opacity: 0.9; }
}Through-Window God Rays
.window-rays {
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(
135deg,
transparent 0%,
transparent 30%,
rgba(255, 248, 220, 0.3) 30%,
rgba(255, 248, 220, 0.1) 35%,
transparent 35%,
transparent 45%,
rgba(255, 248, 220, 0.25) 45%,
rgba(255, 248, 220, 0.05) 52%,
transparent 52%,
transparent 60%,
rgba(255, 248, 220, 0.2) 60%,
rgba(255, 248, 220, 0.05) 68%,
transparent 68%
);
filter: blur(20px);
mix-blend-mode: overlay;
}Animated Ray Rotation
.rotating-rays {
animation: rays-rotate 120s linear infinite;
}
@keyframes rays-rotate {
from { transform: translateX(-50%) rotate(0deg); }
to { transform: translateX(-50%) rotate(360deg); }
}---
SECTION I: LENS FLARE & CAMERA EFFECTS
Radial Gradient Lens Flare
.lens-flare-container {
position: absolute;
inset: 0;
pointer-events: none;
overflow: hidden;
}
.flare-source {
position: absolute;
width: 80px;
height: 80px;
border-radius: 50%;
background: radial-gradient(
circle,
rgba(255, 255, 255, 1) 0%,
rgba(255, 240, 200, 0.8) 20%,
rgba(255, 200, 100, 0.4) 40%,
transparent 70%
);
box-shadow:
0 0 60px 30px rgba(255, 200, 100, 0.5),
0 0 120px 60px rgba(255, 150, 50, 0.3);
}
.flare-artifact {
position: absolute;
border-radius: 50%;
opacity: 0.3;
}
.flare-artifact.hexagon {
width: 40px;
height: 40px;
background: radial-gradient(
circle,
rgba(100, 200, 255, 0.4) 0%,
rgba(100, 200, 255, 0.1) 50%,
transparent 70%
);
clip-path: polygon(
50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%
);
}
.flare-artifact.streak {
width: 200px;
height: 2px;
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 200, 150, 0.6) 30%,
rgba(255, 255, 255, 0.8) 50%,
rgba(255, 200, 150, 0.6) 70%,
transparent 100%
);
transform-origin: center;
}
.flare-artifact.ring {
width: 100px;
height: 100px;
border: 2px solid rgba(255, 200, 150, 0.2);
background: transparent;
}Anamorphic Flare (Cinematic)
.anamorphic-flare {
position: absolute;
width: 100%;
height: 4px;
top: 50%;
transform: translateY(-50%);
background: linear-gradient(
90deg,
transparent 0%,
rgba(100, 180, 255, 0.3) 20%,
rgba(255, 255, 255, 0.6) 50%,
rgba(100, 180, 255, 0.3) 80%,
transparent 100%
);
filter: blur(2px);
animation: anamorphic-pulse 4s ease-in-out infinite;
}
@keyframes anamorphic-pulse {
0%, 100% {
opacity: 0.4;
transform: translateY(-50%) scaleX(0.8);
}
50% {
opacity: 0.7;
transform: translateY(-50%) scaleX(1.2);
}
}Dynamic Flare Following Mouse
function createDynamicFlare(container, sourcePosition) {
const center = { x: window.innerWidth / 2, y: window.innerHeight / 2 };
const direction = {
x: center.x - sourcePosition.x,
y: center.y - sourcePosition.y
};
// Create artifacts along the flare line
const artifactCount = 5;
for (let i = 0; i < artifactCount; i++) {
const t = (i + 1) / (artifactCount + 1);
const artifact = document.createElement('div');
artifact.className = 'flare-artifact hexagon';
artifact.style.left = `${sourcePosition.x + direction.x * t * 1.5}px`;
artifact.style.top = `${sourcePosition.y + direction.y * t * 1.5}px`;
artifact.style.opacity = 0.3 - (i * 0.05);
artifact.style.transform = `scale(${1 - i * 0.15})`;
container.appendChild(artifact);
}
}---
SECTION J: OCEAN SPRAY & SPLASH
Particle-Based Spray
.spray-container {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 40%;
overflow: hidden;
pointer-events: none;
}
.spray-particle {
position: absolute;
width: var(--size, 4px);
height: var(--size, 4px);
border-radius: 50%;
background: radial-gradient(
circle,
rgba(255, 255, 255, 0.9) 0%,
rgba(200, 230, 255, 0.6) 50%,
transparent 100%
);
animation: spray-rise var(--duration, 1s) ease-out forwards;
animation-delay: var(--delay, 0s);
}
@keyframes spray-rise {
0% {
transform: translateY(0) scale(1);
opacity: 0.8;
}
50% {
opacity: 0.6;
}
100% {
transform: translateY(-100px) translateX(var(--drift, 20px)) scale(0.3);
opacity: 0;
}
}Spray Generator
function createSpray(container, intensity = 'medium') {
const config = {
light: { count: 20, maxSize: 6, maxDuration: 1.2 },
medium: { count: 50, maxSize: 10, maxDuration: 1.5 },
heavy: { count: 100, maxSize: 15, maxDuration: 2 }
}[intensity];
for (let i = 0; i < config.count; i++) {
const particle = document.createElement('div');
particle.className = 'spray-particle';
particle.style.setProperty('--size', `${2 + Math.random() * config.maxSize}px`);
particle.style.setProperty('--duration', `${0.5 + Math.random() * config.maxDuration}s`);
particle.style.setProperty('--delay', `${Math.random() * 0.5}s`);
particle.style.setProperty('--drift', `${(Math.random() - 0.5) * 60}px`);
particle.style.left = `${Math.random() * 100}%`;
particle.style.bottom = '0';
container.appendChild(particle);
// Remove after animation
setTimeout(() => particle.remove(), (0.5 + config.maxDuration) * 1000 + 500);
}
}Impact Splash Ring
.splash-ring {
position: absolute;
border-radius: 50%;
border: 3px solid rgba(255, 255, 255, 0.6);
background: transparent;
animation: splash-expand 0.8s ease-out forwards;
}
@keyframes splash-expand {
0% {
width: 10px;
height: 10px;
opacity: 1;
transform: translate(-50%, -50%) rotateX(70deg);
}
100% {
width: 150px;
height: 150px;
opacity: 0;
transform: translate(-50%, -50%) rotateX(70deg);
}
}---
SECTION K: BEACH WASH & FOAM
Shore Wash Animation
<svg class="beach-wave" viewBox="0 0 1440 200" preserveAspectRatio="none">
<defs>
<linearGradient id="foam-gradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="rgba(255,255,255,0.9)" />
<stop offset="30%" stop-color="rgba(200,230,255,0.6)" />
<stop offset="100%" stop-color="rgba(56,189,248,0.4)" />
</linearGradient>
<filter id="foam-texture">
<feTurbulence type="turbulence" baseFrequency="0.02 0.05" numOctaves="3" />
<feDisplacementMap in="SourceGraphic" scale="5" />
</filter>
</defs>
<path
class="wave-foam"
fill="url(#foam-gradient)"
filter="url(#foam-texture)"
>
<animate
attributeName="d"
dur="4s"
repeatCount="indefinite"
values="
M0,100 Q360,50 720,100 T1440,100 L1440,200 L0,200 Z;
M0,80 Q360,130 720,80 T1440,80 L1440,200 L0,200 Z;
M0,100 Q360,50 720,100 T1440,100 L1440,200 L0,200 Z
"
/>
</path>
</svg>.beach-container {
position: relative;
height: 300px;
overflow: hidden;
}
.wet-sand {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 100px;
background: linear-gradient(
to top,
#c2b280 0%,
#d4c896 50%,
#e6dbb0 100%
);
}
.wet-sand::before {
content: '';
position: absolute;
inset: 0;
background: rgba(56, 189, 248, 0.2);
animation: wet-recede 4s ease-in-out infinite;
}
@keyframes wet-recede {
0%, 100% {
clip-path: polygon(0 0, 100% 0, 100% 80%, 0 60%);
}
50% {
clip-path: polygon(0 30%, 100% 50%, 100% 100%, 0 100%);
}
}Foam Bubble Details
.foam-bubbles {
position: absolute;
width: 100%;
height: 30px;
bottom: 60px;
}
.foam-bubble {
position: absolute;
width: var(--size, 8px);
height: var(--size, 8px);
border-radius: 50%;
background: radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.9) 0%,
rgba(200, 230, 255, 0.5) 50%,
transparent 100%
);
animation: bubble-pop var(--duration, 2s) ease-out infinite;
animation-delay: var(--delay, 0s);
}
@keyframes bubble-pop {
0% {
transform: scale(1);
opacity: 0.8;
}
80% {
transform: scale(1.1);
opacity: 0.6;
}
100% {
transform: scale(0);
opacity: 0;
}
}Receding Wave Trail
.wave-trail {
position: absolute;
bottom: 60px;
left: 0;
right: 0;
height: 5px;
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 255, 255, 0.6) 10%,
rgba(255, 255, 255, 0.8) 50%,
rgba(255, 255, 255, 0.6) 90%,
transparent 100%
);
animation: trail-recede 4s ease-in-out infinite;
}
@keyframes trail-recede {
0% {
transform: translateY(0) scaleX(1);
opacity: 0.8;
}
50% {
transform: translateY(-30px) scaleX(0.95);
opacity: 0.4;
}
100% {
transform: translateY(0) scaleX(1);
opacity: 0.8;
}
}---
SECTION L: PERFORMANCE OPTIMIZATION
Performance Tiers
/* Tier 1: Ultra Performance (CSS-only) */
@media (prefers-reduced-motion: reduce) {
.weather-effect {
animation: none !important;
filter: none !important;
}
}
/* Tier 2: High Performance (static filters) */
.weather-effect--performance {
filter: url(#static-filter);
animation-play-state: paused;
}
/* Tier 3: Full Effects */
.weather-effect--full {
filter: url(#animated-filter);
animation-play-state: running;
}GPU Acceleration
.weather-layer {
/* Force GPU compositing */
will-change: transform, opacity;
transform: translateZ(0);
backface-visibility: hidden;
/* Avoid triggering repaints */
contain: layout paint;
}
/* Avoid animating these properties */
.weather-layer {
/* BAD: causes repaints */
/* animation: filter-change 10s; */
/* GOOD: GPU-accelerated */
animation: transform-change 10s;
}Intersection Observer for Off-Screen
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
const effect = entry.target;
if (entry.isIntersecting) {
effect.classList.add('weather-effect--active');
} else {
effect.classList.remove('weather-effect--active');
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.weather-effect').forEach(el => observer.observe(el));Mobile Optimization
@media (max-width: 768px) {
/* Reduce layer count on mobile */
.cloud-back,
.fog-back {
display: none;
}
/* Simplify filters */
.weather-filter {
filter: blur(var(--blur)) opacity(0.5);
}
/* Reduce particle count */
.rain-container {
--drop-count: 50;
}
}---
SECTION M: REACT INTEGRATION
Weather Effect Provider
import React, { createContext, useContext, useState, useEffect } from 'react';
type WeatherType =
| 'clear'
| 'cloudy'
| 'rainy'
| 'stormy'
| 'foggy'
| 'snowy'
| 'aurora';
type TimeOfDay = 'dawn' | 'morning' | 'noon' | 'afternoon' | 'dusk' | 'night';
interface WeatherContextType {
weather: WeatherType;
timeOfDay: TimeOfDay;
intensity: number; // 0-1
setWeather: (w: WeatherType) => void;
setTimeOfDay: (t: TimeOfDay) => void;
setIntensity: (i: number) => void;
}
const WeatherContext = createContext<WeatherContextType | null>(null);
export function WeatherProvider({ children }: { children: React.ReactNode }) {
const [weather, setWeather] = useState<WeatherType>('clear');
const [timeOfDay, setTimeOfDay] = useState<TimeOfDay>('noon');
const [intensity, setIntensity] = useState(0.5);
// Auto-detect time of day
useEffect(() => {
const hour = new Date().getHours();
if (hour >= 5 && hour < 7) setTimeOfDay('dawn');
else if (hour >= 7 && hour < 12) setTimeOfDay('morning');
else if (hour >= 12 && hour < 14) setTimeOfDay('noon');
else if (hour >= 14 && hour < 17) setTimeOfDay('afternoon');
else if (hour >= 17 && hour < 20) setTimeOfDay('dusk');
else setTimeOfDay('night');
}, []);
return (
<WeatherContext.Provider value={{
weather, timeOfDay, intensity,
setWeather, setTimeOfDay, setIntensity
}}>
{children}
</WeatherContext.Provider>
);
}
export const useWeather = () => {
const context = useContext(WeatherContext);
if (!context) throw new Error('useWeather must be used within WeatherProvider');
return context;
};Atmospheric Canvas Component
import React from 'react';
import { useWeather } from './WeatherProvider';
import { CloudLayer } from './CloudLayer';
import { RainEffect } from './RainEffect';
import { LightningEffect } from './LightningEffect';
import { FogLayer } from './FogLayer';
import { AuroraEffect } from './AuroraEffect';
import { SkyGradient } from './SkyGradient';
export function AtmosphericCanvas() {
const { weather, timeOfDay, intensity } = useWeather();
return (
<div className="atmospheric-canvas">
{/* SVG Filters */}
<svg width="0" height="0" aria-hidden="true">
<defs>
<filter id="cloud-filter">...</filter>
<filter id="fog-filter">...</filter>
<filter id="wave-filter">...</filter>
</defs>
</svg>
{/* Sky base */}
<SkyGradient timeOfDay={timeOfDay} />
{/* Weather layers */}
{(weather === 'cloudy' || weather === 'rainy' || weather === 'stormy') && (
<CloudLayer variant={weather === 'stormy' ? 'storm' : 'cumulus'} />
)}
{weather === 'foggy' && <FogLayer intensity={intensity} />}
{weather === 'aurora' && <AuroraEffect intensity={intensity} />}
{(weather === 'rainy' || weather === 'stormy') && (
<RainEffect intensity={weather === 'stormy' ? 1 : intensity} />
)}
{weather === 'stormy' && <LightningEffect frequency={intensity} />}
</div>
);
}Individual Effect Components
// CloudLayer.tsx
interface CloudLayerProps {
variant: 'cumulus' | 'cirrus' | 'stratus' | 'storm';
opacity?: number;
}
export function CloudLayer({ variant, opacity = 0.7 }: CloudLayerProps) {
const configs = {
cumulus: { baseFrequency: 0.008, blur: 4, layers: 3 },
cirrus: { baseFrequency: 0.003, blur: 2, layers: 2 },
stratus: { baseFrequency: 0.015, blur: 6, layers: 2 },
storm: { baseFrequency: 0.006, blur: 3, layers: 4 }
};
const config = configs[variant];
return (
<div className="cloud-system" style={{ '--opacity': opacity } as React.CSSProperties}>
{Array.from({ length: config.layers }, (_, i) => (
<div
key={i}
className={`cloud-layer cloud-layer-${i}`}
style={{
'--layer-index': i,
'--animation-duration': `${50 + i * 30}s`
} as React.CSSProperties}
/>
))}
</div>
);
}---
SECTION N: TAILWIND INTEGRATION
Custom Tailwind Utilities
// tailwind.config.js
module.exports = {
theme: {
extend: {
animation: {
'drift': 'drift 60s linear infinite',
'drift-slow': 'drift 120s linear infinite',
'drift-fast': 'drift 30s linear infinite',
'wave': 'wave 10s ease-in-out infinite',
'rain-fall': 'rain-fall 0.8s linear infinite',
'lightning': 'lightning-flash 0.5s ease-out',
'aurora': 'aurora-wave 15s ease-in-out infinite',
'fog-breathe': 'fog-breathe 8s ease-in-out infinite',
},
keyframes: {
drift: {
'0%': { transform: 'translateX(-10%)' },
'100%': { transform: 'translateX(10%)' },
},
wave: {
'0%, 100%': { transform: 'translateY(0)' },
'50%': { transform: 'translateY(-10px)' },
},
'rain-fall': {
'0%': { transform: 'translateY(-100vh)' },
'100%': { transform: 'translateY(100vh)' },
},
'lightning-flash': {
'0%': { opacity: '0' },
'10%': { opacity: '1' },
'20%': { opacity: '0.5' },
'30%': { opacity: '1' },
'100%': { opacity: '0' },
},
'aurora-wave': {
'0%, 100%': { transform: 'translateX(-20%) skewX(-5deg)', opacity: '0.4' },
'50%': { transform: 'translateX(0%) skewX(5deg)', opacity: '0.7' },
},
'fog-breathe': {
'0%, 100%': { opacity: '0.5', transform: 'scaleY(1)' },
'50%': { opacity: '0.7', transform: 'scaleY(1.1)' },
},
},
backdropBlur: {
'fog': '20px',
'mist': '10px',
},
},
},
plugins: [
function({ addUtilities }) {
addUtilities({
'.filter-cloud': {
filter: 'url(#cloud-filter)',
},
'.filter-fog': {
filter: 'url(#fog-filter)',
},
'.filter-wave': {
filter: 'url(#wave-filter)',
},
'.mix-screen': {
mixBlendMode: 'screen',
},
'.mix-overlay': {
mixBlendMode: 'overlay',
},
});
},
],
};Tailwind Component Classes
<!-- Cloud layer -->
<div class="absolute inset-0 opacity-50 filter-cloud animate-drift-slow"></div>
<!-- Rain container -->
<div class="absolute inset-0 overflow-hidden pointer-events-none">
<div class="w-0.5 h-5 bg-gradient-to-b from-transparent via-sky-200/50 to-sky-300/80 rounded-b animate-rain-fall"></div>
</div>
<!-- Fog overlay -->
<div class="absolute inset-0 bg-white/30 backdrop-blur-fog filter-fog animate-fog-breathe"></div>
<!-- Aurora band -->
<div class="absolute top-1/4 -left-1/2 w-[200%] h-2/5 opacity-60 mix-screen blur-3xl animate-aurora bg-gradient-to-r from-transparent via-emerald-400/40 to-transparent"></div>---
Quick Reference Card
Effect Selection Matrix
| Effect | Best For | Complexity | Performance |
|---|---|---|---|
| Clouds | Hero sections, headers | Medium | High |
| Waves | Footers, section dividers | Medium | High |
| Lightning | Dramatic moments, gaming | Low | Very High |
| Rain | Mood, atmosphere | Medium | Medium |
| Fog | Mystery, depth | Low | High |
| Aurora | Night scenes, magical | High | Medium |
| God rays | Divine, cinematic | Low | High |
| Lens flare | Realistic, photography | Medium | High |
| Ocean spray | Beach, action | High | Low |
| Beach wash | Coastal, calm | Medium | Medium |
| Twilight sky | Time indication | Low | Very High |
Common Combinations
| Scene | Effects | Notes |
|---|---|---|
| Dramatic storm | Storm clouds + Rain + Lightning | Stagger lightning timing |
| Peaceful beach | Light clouds + Beach wash + God rays | Slow animations |
| Foggy morning | Fog + Ground mist + Soft clouds | Reduce saturation |
| Northern night | Aurora + Stars + Light fog | Dark background essential |
| Cinematic moment | God rays + Lens flare + Light haze | Don't overdo flares |
Performance Budget
| Device | Max Layers | Max Particles | Filter Complexity |
|---|---|---|---|
| Desktop | 5-7 | 200 | Full |
| Tablet | 3-5 | 100 | Simplified |
| Mobile | 2-3 | 50 | Minimal |
---
Resources & References
Tutorials Referenced
- CSS-Tricks: Lens Flare
- Red Stapler: SVG Water Turbulence
- CSS In Real Life: Animated Sun with God Rays
- Pyxofy: Sunset Animation
- Medium: God Rays with Sass
Tools
- Magic Pattern: God Rays Generator
- UI Surgeon: CSS Wave Generator
- uiGradients: Sky Gradients
- iColorPalette: Sunset Gradients
Code Collections
---
Version History
- v1.0.0 (2026-01-25): Merged from web-cloud-designer + web-wave-designer, added lightning, rain, fog, twilight, aurora, god rays, lens flare, ocean spray, beach wash effects
---
Remember: Stylized weather effects are about restraint and layering. One well-crafted effect beats five mediocre ones. When in doubt, reduce intensity and increase subtlety.
Layering Strategies: Making Clouds Float Through Content
One of the most common challenges with atmospheric effects is making clouds (or other weather elements) appear to float through visual elements rather than just sitting behind or in front of everything.
The Problem
Default Z-Index Stack:
┌─────────────────────────────────────┐
│ Nav/Header (z-index: 1000) │ ← Always on top
├─────────────────────────────────────┤
│ Clouds (z-index: ???) │ ← Where do they go?
├─────────────────────────────────────┤
│ Content Cards (z-index: 1) │
├─────────────────────────────────────┤
│ Background (z-index: 0) │
└─────────────────────────────────────┘If clouds are above content, they obscure text. If clouds are below content, they look flat and unrealistic.
Solution 1: Split Cloud Layers (Recommended)
The most effective approach: split your clouds into BACK and FRONT layers.
<div class="atmosphere-container">
<!-- Layer 1: Distant clouds (BEHIND content) -->
<div class="cloud-layer cloud-back"></div>
<!-- Content sits in the middle -->
<main class="content">
<div class="card">...</div>
</main>
<!-- Layer 2: Near clouds (IN FRONT of content) -->
<div class="cloud-layer cloud-front"></div>
</div>.atmosphere-container {
position: relative;
isolation: isolate; /* Creates stacking context */
}
.cloud-layer {
position: absolute;
inset: 0;
pointer-events: none; /* CRITICAL: Let clicks through */
}
.cloud-back {
z-index: -1;
opacity: 0.3;
transform: scale(1.5);
animation: drift 120s linear infinite;
}
.content {
position: relative;
z-index: 1;
}
.cloud-front {
z-index: 2;
opacity: 0.15; /* More transparent so content is visible */
transform: scale(0.8);
animation: drift 60s linear infinite reverse;
}Visual Result
┌─────────────────────────────────────┐
│ ☁️ Front clouds (z:2, 15% opacity) │
├─────────────────────────────────────┤
│ 📄 Content cards (z:1) │
├─────────────────────────────────────┤
│ ☁️ Back clouds (z:-1, 30% opacity) │
└─────────────────────────────────────┘The front clouds are transparent enough to see through, creating depth perception where clouds appear to pass in front of AND behind content.
---
Solution 2: CSS Mask on Front Clouds
For more control, mask the front clouds so they only appear in specific areas:
.cloud-front {
z-index: 2;
opacity: 0.4;
/* Only show clouds at edges and top */
mask-image: linear-gradient(
to bottom,
black 0%, /* Full opacity at top */
black 20%,
transparent 40%, /* Fade out in content area */
transparent 60%,
black 80%, /* Fade back in at bottom */
black 100%
);
/* Or radial mask - clouds visible around edges only */
mask-image: radial-gradient(
ellipse 80% 80% at 50% 50%,
transparent 0%, /* Clear in center where content is */
black 100% /* Visible at edges */
);
}---
Solution 3: Content-Aware Masking
For complex layouts, create masks that match your content positions:
.cloud-front {
/* Multiple mask gradients combined */
mask-image:
/* Clear area for nav */
linear-gradient(to bottom, transparent 0%, transparent 80px, black 120px),
/* Clear area for hero text */
radial-gradient(ellipse 400px 200px at 50% 300px, transparent 0%, black 100%),
/* Clear area for footer */
linear-gradient(to top, transparent 0%, transparent 100px, black 140px);
mask-composite: intersect;
}---
Solution 4: Clip-Path Animation
Animate the visible region of clouds based on scroll:
const cloudFront = document.querySelector('.cloud-front');
window.addEventListener('scroll', () => {
const scrollY = window.scrollY;
const viewportHeight = window.innerHeight;
// Move the "clear zone" based on scroll position
const clearTop = Math.max(0, 200 - scrollY * 0.5);
const clearBottom = Math.min(100, 60 + scrollY * 0.1);
cloudFront.style.maskImage = `linear-gradient(
to bottom,
black 0%,
black ${clearTop}px,
transparent ${clearTop + 100}px,
transparent ${clearBottom}%,
black 100%
)`;
});---
Solution 5: Mix-Blend-Mode Magic
Use blend modes to make clouds interact with content colors:
.cloud-front {
z-index: 2;
opacity: 0.3;
mix-blend-mode: soft-light; /* Blends with underlying colors */
}
/* Or for more dramatic effect */
.cloud-front.dramatic {
mix-blend-mode: overlay;
}
/* For subtle white clouds on any background */
.cloud-front.subtle {
mix-blend-mode: screen;
opacity: 0.2;
}Blend Mode Reference
| Mode | Effect | Best For |
|---|---|---|
screen | Lightens, ignores dark | White clouds on dark bg |
overlay | Contrast boost | Dramatic weather |
soft-light | Gentle blend | Subtle atmospheric |
multiply | Darkens, ignores light | Storm clouds |
color-dodge | Bright glow | God rays, light beams |
---
Solution 6: Backdrop-Filter Interaction
Make content cards slightly blur clouds behind them:
.card {
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
/* This blurs the back clouds, creating depth */
}
/* Clouds in front are NOT blurred by the card */
.cloud-front {
z-index: 2;
/* Passes over cards crisply while back clouds blur */
}---
The Complete Three-Layer System
For maximum realism, use three cloud layers:
<div class="sky-container">
<!-- Distant (parallax slowest) -->
<div class="clouds clouds--distant"></div>
<!-- Mid-layer (catches interaction) -->
<div class="clouds clouds--mid"></div>
<!-- Content layer -->
<main class="content">...</main>
<!-- Foreground (parallax fastest) -->
<div class="clouds clouds--near"></div>
</div>.sky-container {
position: relative;
min-height: 100vh;
overflow: hidden;
isolation: isolate;
}
.clouds {
position: fixed; /* Fixed for parallax effect */
inset: 0;
pointer-events: none;
filter: url(#cloud-cumulus);
}
.clouds--distant {
z-index: -2;
opacity: 0.2;
transform: scale(2) translateZ(-100px);
animation: drift 180s linear infinite;
}
.clouds--mid {
z-index: -1;
opacity: 0.35;
transform: scale(1.3) translateZ(-50px);
animation: drift 90s linear infinite reverse;
}
.content {
position: relative;
z-index: 1;
}
.clouds--near {
z-index: 2;
opacity: 0.12;
transform: scale(0.7) translateZ(50px);
animation: drift 45s linear infinite;
/* Mask so content is readable */
mask-image: linear-gradient(
to bottom,
black 0%,
transparent 30%,
transparent 70%,
black 100%
);
}---
Parallax Enhancement
Add scroll-based parallax for extra depth:
const layers = {
distant: document.querySelector('.clouds--distant'),
mid: document.querySelector('.clouds--mid'),
near: document.querySelector('.clouds--near'),
};
window.addEventListener('scroll', () => {
const scrollY = window.scrollY;
// Slower movement for distant clouds (parallax)
layers.distant.style.transform = `scale(2) translateY(${scrollY * 0.1}px)`;
layers.mid.style.transform = `scale(1.3) translateY(${scrollY * 0.3}px)`;
layers.near.style.transform = `scale(0.7) translateY(${scrollY * 0.6}px)`;
});---
Accessibility Considerations
/* Respect reduced motion preferences */
@media (prefers-reduced-motion: reduce) {
.clouds {
animation: none;
}
.clouds--near {
opacity: 0.05; /* Barely visible, less distracting */
}
}
/* Ensure text remains readable */
.content {
/* Add subtle text shadow for contrast against clouds */
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* Or add a subtle backdrop to cards */
.card {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(4px);
}---
React Component Example
interface CloudSystemProps {
intensity?: 'subtle' | 'normal' | 'dramatic';
enableForeground?: boolean;
}
export function CloudSystem({
intensity = 'normal',
enableForeground = true
}: CloudSystemProps) {
const config = {
subtle: { backOpacity: 0.15, frontOpacity: 0.05 },
normal: { backOpacity: 0.3, frontOpacity: 0.12 },
dramatic: { backOpacity: 0.5, frontOpacity: 0.2 },
}[intensity];
return (
<>
{/* Back clouds - rendered before content in DOM */}
<div
className="clouds clouds--back"
style={{ opacity: config.backOpacity }}
/>
{/* Front clouds - rendered as portal or after content */}
{enableForeground && (
<div
className="clouds clouds--front"
style={{ opacity: config.frontOpacity }}
/>
)}
</>
);
}
// Usage
function Page() {
return (
<div className="sky-container">
<CloudSystem intensity="normal" />
<main className="content">
<h1>Welcome</h1>
<p>Clouds float through this content!</p>
</main>
</div>
);
}---
Summary: Best Practices
1. Always use `pointer-events: none` on cloud layers 2. Split into 2-3 layers for depth (back, mid, front) 3. Front clouds should be ~10-15% opacity to see through 4. Use masks to clear space around important content 5. Mix-blend-mode for natural color integration 6. Backdrop-filter on content blurs back clouds, adding depth 7. Respect prefers-reduced-motion for accessibility 8. Add text-shadow or card backgrounds to ensure readability
The key insight: depth comes from layering with varied opacity and speed, not from a single cloud layer with high opacity.
/**
* WEB WEATHER CREATOR - React Component Library
*
* Ready-to-use React components for atmospheric effects.
* Copy these into your project and customize.
*/
import React, { useEffect, useRef, useState, useMemo } from 'react';
// ============================================
// SVG FILTERS (include once in your app)
// ============================================
export function WeatherFilters() {
return (
<svg
width="0"
height="0"
aria-hidden="true"
style={{ position: 'absolute', pointerEvents: 'none' }}
>
<defs>
{/* Cumulus clouds */}
<filter id="cloud-cumulus" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="fractalNoise" baseFrequency="0.008" numOctaves="4" seed="1" result="noise"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="30" xChannelSelector="R" yChannelSelector="G" result="displaced"/>
<feGaussianBlur in="displaced" stdDeviation="4"/>
</filter>
{/* Storm clouds */}
<filter id="cloud-storm" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="fractalNoise" baseFrequency="0.006" numOctaves="5" seed="4" result="noise"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="40" xChannelSelector="R" yChannelSelector="G" result="displaced"/>
<feGaussianBlur in="displaced" stdDeviation="3"/>
<feColorMatrix type="matrix" values="0.6 0 0 0 0 0 0.6 0 0 0 0 0 0.7 0 0 0 0 0 1 0"/>
</filter>
{/* Wave distortion */}
<filter id="wave-ocean" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="turbulence" baseFrequency="0.005 0.015" numOctaves="3" seed="10" result="turbulence">
<animate attributeName="baseFrequency" dur="20s" values="0.005 0.015; 0.007 0.018; 0.005 0.015" repeatCount="indefinite"/>
</feTurbulence>
<feDisplacementMap in="SourceGraphic" in2="turbulence" scale="40" xChannelSelector="R" yChannelSelector="G"/>
</filter>
{/* Fog texture */}
<filter id="fog-filter" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="fractalNoise" baseFrequency="0.003" numOctaves="4" seed="20" result="noise"/>
<feGaussianBlur in="noise" stdDeviation="25"/>
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.5 0"/>
</filter>
{/* Lightning glow */}
<filter id="lightning-glow" x="-100%" y="-100%" width="300%" height="300%">
<feGaussianBlur stdDeviation="4" result="blur"/>
<feMerge>
<feMergeNode in="blur"/>
<feMergeNode in="blur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
</svg>
);
}
// ============================================
// CLOUD SYSTEM
// ============================================
interface CloudLayerProps {
variant?: 'cumulus' | 'cirrus' | 'stratus' | 'storm';
layer?: 'back' | 'mid' | 'front';
className?: string;
}
const cloudConfig = {
cumulus: { filter: 'url(#cloud-cumulus)', blur: 4 },
cirrus: { filter: 'url(#cloud-cumulus)', blur: 2 }, // Reuse filter, adjust blur
stratus: { filter: 'url(#cloud-cumulus)', blur: 6 },
storm: { filter: 'url(#cloud-storm)', blur: 3 },
};
const layerConfig = {
back: { zIndex: -1, opacity: 0.3, scale: 1.5, duration: 120 },
mid: { zIndex: 0, opacity: 0.4, scale: 1.2, duration: 80 },
front: { zIndex: 2, opacity: 0.12, scale: 0.8, duration: 50 },
};
export function CloudLayer({
variant = 'cumulus',
layer = 'back',
className = '',
}: CloudLayerProps) {
const cloud = cloudConfig[variant];
const layerSettings = layerConfig[layer];
return (
<div
className={`cloud-layer ${className}`}
style={{
position: 'absolute',
inset: 0,
pointerEvents: 'none',
zIndex: layerSettings.zIndex,
opacity: layerSettings.opacity,
transform: `scale(${layerSettings.scale})`,
filter: cloud.filter,
background: `radial-gradient(
ellipse 200% 100% at 50% 100%,
rgba(255, 255, 255, 0.9) 0%,
transparent 70%
)`,
animation: `drift ${layerSettings.duration}s linear infinite${layer === 'front' ? ' reverse' : ''}`,
}}
/>
);
}
interface CloudSystemProps {
variant?: 'cumulus' | 'storm';
layers?: ('back' | 'mid' | 'front')[];
className?: string;
}
export function CloudSystem({
variant = 'cumulus',
layers = ['back', 'front'],
className = '',
}: CloudSystemProps) {
return (
<div className={`cloud-system ${className}`} style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
{layers.map((layer) => (
<CloudLayer key={layer} variant={variant} layer={layer} />
))}
</div>
);
}
// ============================================
// WAVE COMPONENT
// ============================================
interface WaveProps {
color?: string;
height?: number;
speed?: 'slow' | 'normal' | 'fast';
className?: string;
}
export function Wave({
color = '#38bdf8',
height = 200,
speed = 'normal',
className = '',
}: WaveProps) {
const duration = { slow: 15, normal: 10, fast: 6 }[speed];
return (
<svg
viewBox="0 0 1440 320"
preserveAspectRatio="none"
className={className}
style={{ width: '100%', height, display: 'block' }}
>
<path
fill={color}
filter="url(#wave-ocean)"
>
<animate
attributeName="d"
dur={`${duration}s`}
repeatCount="indefinite"
values="
M0,160 C360,220 720,100 1080,180 C1260,220 1380,140 1440,160 L1440,320 L0,320 Z;
M0,180 C360,120 720,200 1080,140 C1260,100 1380,180 1440,140 L1440,320 L0,320 Z;
M0,160 C360,220 720,100 1080,180 C1260,220 1380,140 1440,160 L1440,320 L0,320 Z
"
/>
</path>
</svg>
);
}
interface WaveStackProps {
colors?: string[];
className?: string;
}
export function WaveStack({
colors = ['#0c4a6e', '#0369a1', '#38bdf8'],
className = '',
}: WaveStackProps) {
return (
<div className={className} style={{ position: 'relative' }}>
{colors.map((color, i) => (
<div
key={i}
style={{
position: 'absolute',
bottom: i * 20,
left: 0,
right: 0,
zIndex: colors.length - i,
}}
>
<Wave
color={color}
height={150 - i * 20}
speed={i === 0 ? 'slow' : i === 1 ? 'normal' : 'fast'}
/>
</div>
))}
</div>
);
}
// ============================================
// RAIN EFFECT
// ============================================
interface RainEffectProps {
intensity?: 'light' | 'moderate' | 'heavy';
windAngle?: number; // degrees
className?: string;
}
export function RainEffect({
intensity = 'moderate',
windAngle = 0,
className = '',
}: RainEffectProps) {
const dropCount = { light: 50, moderate: 150, heavy: 300 }[intensity];
const drops = useMemo(() =>
Array.from({ length: dropCount }, (_, i) => ({
id: i,
x: Math.random() * 100,
delay: Math.random() * 2,
duration: 0.5 + Math.random() * 0.5,
length: 15 + Math.random() * 20,
opacity: 0.3 + Math.random() * 0.5,
})),
[dropCount]
);
return (
<div
className={`rain-container ${className}`}
style={{
position: 'absolute',
inset: 0,
overflow: 'hidden',
pointerEvents: 'none',
transform: `rotate(${windAngle}deg)`,
}}
>
{drops.map((drop) => (
<div
key={drop.id}
style={{
position: 'absolute',
left: `${drop.x}%`,
width: 2,
height: drop.length,
background: `linear-gradient(180deg, transparent 0%, rgba(174, 194, 224, 0.5) 50%, rgba(174, 194, 224, 0.8) 100%)`,
borderRadius: '0 0 2px 2px',
opacity: drop.opacity,
animation: `rain-fall ${drop.duration}s linear infinite`,
animationDelay: `${drop.delay}s`,
}}
/>
))}
</div>
);
}
// ============================================
// LIGHTNING EFFECT
// ============================================
interface LightningEffectProps {
frequency?: number; // 0-1, chance per interval
className?: string;
}
export function LightningEffect({
frequency = 0.3,
className = '',
}: LightningEffectProps) {
const [isFlashing, setIsFlashing] = useState(false);
const [boltPath, setBoltPath] = useState('');
useEffect(() => {
const interval = setInterval(() => {
if (Math.random() < frequency) {
// Generate random lightning path
const startX = 30 + Math.random() * 40;
let path = `M${startX},0`;
let x = startX;
let y = 0;
for (let i = 0; i < 8; i++) {
x += (Math.random() - 0.5) * 30;
y += 25 + Math.random() * 15;
path += ` L${x},${y}`;
}
setBoltPath(path);
setIsFlashing(true);
setTimeout(() => setIsFlashing(false), 500);
}
}, 2000);
return () => clearInterval(interval);
}, [frequency]);
if (!isFlashing) return null;
return (
<>
{/* Screen flash */}
<div
className={className}
style={{
position: 'fixed',
inset: 0,
background: 'rgba(200, 220, 255, 0.3)',
pointerEvents: 'none',
zIndex: 1000,
animation: 'lightning-flash 0.3s ease-out forwards',
}}
/>
{/* Lightning bolt */}
<svg
viewBox="0 0 100 200"
style={{
position: 'absolute',
top: 0,
left: '20%',
width: '60%',
height: '50%',
pointerEvents: 'none',
zIndex: 1001,
}}
>
<path
d={boltPath}
fill="none"
stroke="white"
strokeWidth="3"
filter="url(#lightning-glow)"
style={{
strokeDasharray: 500,
strokeDashoffset: 500,
animation: 'bolt-draw 0.15s ease-out forwards',
}}
/>
</svg>
</>
);
}
// ============================================
// FOG LAYER
// ============================================
interface FogLayerProps {
density?: 'light' | 'medium' | 'dense';
position?: 'full' | 'ground';
className?: string;
}
export function FogLayer({
density = 'medium',
position = 'full',
className = '',
}: FogLayerProps) {
const opacityMap = { light: 0.2, medium: 0.4, dense: 0.6 };
return (
<div
className={`fog-layer ${className}`}
style={{
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
top: position === 'ground' ? '60%' : 0,
pointerEvents: 'none',
background: position === 'ground'
? 'linear-gradient(to top, rgba(255,255,255,0.6) 0%, rgba(255,255,255,0.3) 40%, transparent 100%)'
: 'rgba(255, 255, 255, 0.4)',
filter: 'url(#fog-filter)',
opacity: opacityMap[density],
animation: 'fog-breathe 8s ease-in-out infinite',
}}
/>
);
}
// ============================================
// AURORA BOREALIS
// ============================================
interface AuroraEffectProps {
colors?: string[];
intensity?: number; // 0-1
className?: string;
}
export function AuroraEffect({
colors = ['rgba(0, 255, 128, 0.4)', 'rgba(138, 43, 226, 0.3)', 'rgba(0, 150, 255, 0.3)'],
intensity = 0.6,
className = '',
}: AuroraEffectProps) {
return (
<div
className={`aurora-container ${className}`}
style={{
position: 'absolute',
inset: 0,
overflow: 'hidden',
background: 'linear-gradient(to top, #0a0a1a 0%, #1a1a3a 100%)',
}}
>
{colors.map((color, i) => (
<div
key={i}
style={{
position: 'absolute',
top: `${10 + i * 5}%`,
left: '-50%',
width: '200%',
height: '40%',
opacity: intensity,
mixBlendMode: 'screen',
filter: 'blur(30px)',
background: `linear-gradient(90deg, transparent 0%, ${color} 30%, ${color} 70%, transparent 100%)`,
animation: `aurora-wave ${15 + i * 5}s ease-in-out infinite${i % 2 ? ' reverse' : ''}`,
animationDelay: `${-i * 3}s`,
}}
/>
))}
</div>
);
}
// ============================================
// SKY GRADIENT
// ============================================
type TimeOfDay = 'dawn' | 'morning' | 'noon' | 'afternoon' | 'dusk' | 'night';
const skyGradients: Record<TimeOfDay, string> = {
dawn: 'linear-gradient(to top, #ff6b6b 0%, #feca57 20%, #ff9ff3 40%, #54a0ff 70%, #1a1a2e 100%)',
morning: 'linear-gradient(to top, #ff9a56 0%, #ffbe76 30%, #ffeaa7 50%, #74b9ff 80%, #0984e3 100%)',
noon: 'linear-gradient(to top, #74b9ff 0%, #0984e3 50%, #0652dd 100%)',
afternoon: 'linear-gradient(to top, #74b9ff 0%, #0984e3 40%, #6c5ce7 80%, #2d3436 100%)',
dusk: 'linear-gradient(to top, #e17055 0%, #fdcb6e 20%, #f8a5c2 40%, #686de0 70%, #30336b 100%)',
night: 'linear-gradient(to top, #0c2461 0%, #1e3799 40%, #0a1931 100%)',
};
interface SkyGradientProps {
timeOfDay?: TimeOfDay;
className?: string;
}
export function SkyGradient({
timeOfDay = 'noon',
className = '',
}: SkyGradientProps) {
return (
<div
className={`sky-gradient ${className}`}
style={{
position: 'absolute',
inset: 0,
background: skyGradients[timeOfDay],
transition: 'background 3s ease-in-out',
}}
/>
);
}
// ============================================
// COMPLETE ATMOSPHERE WRAPPER
// ============================================
interface AtmosphereProps {
weather?: 'clear' | 'cloudy' | 'rainy' | 'stormy' | 'foggy' | 'aurora';
timeOfDay?: TimeOfDay;
children: React.ReactNode;
className?: string;
}
export function Atmosphere({
weather = 'clear',
timeOfDay = 'noon',
children,
className = '',
}: AtmosphereProps) {
return (
<div
className={`atmosphere ${className}`}
style={{
position: 'relative',
minHeight: '100vh',
isolation: 'isolate',
overflow: 'hidden',
}}
>
{/* Include filters once */}
<WeatherFilters />
{/* Sky background */}
<SkyGradient timeOfDay={timeOfDay} />
{/* Weather-specific layers (back) */}
{(weather === 'cloudy' || weather === 'rainy' || weather === 'stormy') && (
<CloudLayer variant={weather === 'stormy' ? 'storm' : 'cumulus'} layer="back" />
)}
{weather === 'foggy' && <FogLayer density="medium" position="full" />}
{weather === 'aurora' && <AuroraEffect intensity={0.6} />}
{/* Content */}
<div style={{ position: 'relative', zIndex: 1 }}>
{children}
</div>
{/* Weather-specific layers (front) */}
{(weather === 'cloudy' || weather === 'rainy' || weather === 'stormy') && (
<CloudLayer variant={weather === 'stormy' ? 'storm' : 'cumulus'} layer="front" />
)}
{(weather === 'rainy' || weather === 'stormy') && (
<RainEffect intensity={weather === 'stormy' ? 'heavy' : 'moderate'} />
)}
{weather === 'stormy' && <LightningEffect frequency={0.3} />}
{weather === 'foggy' && <FogLayer density="light" position="ground" />}
</div>
);
}
// ============================================
// REQUIRED CSS (add to your global styles)
// ============================================
/*
@keyframes drift {
from { transform: translateX(-10%); }
to { transform: translateX(10%); }
}
@keyframes rain-fall {
0% { transform: translateY(-100vh); }
100% { transform: translateY(100vh); }
}
@keyframes lightning-flash {
0% { opacity: 0; }
10% { opacity: 1; }
20% { opacity: 0.5; }
30% { opacity: 1; }
100% { opacity: 0; }
}
@keyframes bolt-draw {
to { stroke-dashoffset: 0; }
}
@keyframes fog-breathe {
0%, 100% { opacity: 0.5; transform: scaleY(1); }
50% { opacity: 0.7; transform: scaleY(1.1); }
}
@keyframes aurora-wave {
0%, 100% { transform: translateX(-20%) skewX(-5deg); opacity: 0.4; }
25% { transform: translateX(-10%) skewX(5deg); opacity: 0.7; }
50% { transform: translateX(0%) skewX(-3deg); opacity: 0.5; }
75% { transform: translateX(10%) skewX(3deg); opacity: 0.6; }
}
@media (prefers-reduced-motion: reduce) {
.cloud-layer,
.fog-layer,
.rain-container > div,
[class*="aurora"] {
animation: none !important;
}
}
*/
<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
<defs>
<!--
WEB WEATHER CREATOR - SVG FILTER LIBRARY
Copy these filters to your project and reference with filter="url(#filter-name)"
-->
<!-- ============================================ -->
<!-- CLOUD FILTERS -->
<!-- ============================================ -->
<!-- Cumulus (fluffy) clouds -->
<filter id="cloud-cumulus" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="fractalNoise" baseFrequency="0.008" numOctaves="4" seed="1" result="noise"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="30" xChannelSelector="R" yChannelSelector="G" result="displaced"/>
<feGaussianBlur in="displaced" stdDeviation="4" result="blurred"/>
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1.2 0"/>
</filter>
<!-- Cirrus (wispy) clouds -->
<filter id="cloud-cirrus" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="fractalNoise" baseFrequency="0.003" numOctaves="5" seed="2" result="noise"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="15" xChannelSelector="R" yChannelSelector="G" result="displaced"/>
<feGaussianBlur in="displaced" stdDeviation="2"/>
</filter>
<!-- Stratus (flat) clouds -->
<filter id="cloud-stratus" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="fractalNoise" baseFrequency="0.015" numOctaves="2" seed="3" result="noise"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="20" xChannelSelector="R" yChannelSelector="G" result="displaced"/>
<feGaussianBlur in="displaced" stdDeviation="6"/>
</filter>
<!-- Storm clouds -->
<filter id="cloud-storm" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="fractalNoise" baseFrequency="0.006" numOctaves="5" seed="4" result="noise"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="40" xChannelSelector="R" yChannelSelector="G" result="displaced"/>
<feGaussianBlur in="displaced" stdDeviation="3" result="blurred"/>
<feColorMatrix type="matrix" values="0.6 0 0 0 0 0 0.6 0 0 0 0 0 0.7 0 0 0 0 0 1 0"/>
</filter>
<!-- Cartoon clouds -->
<filter id="cloud-cartoon" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="fractalNoise" baseFrequency="0.02" numOctaves="2" seed="5" result="noise"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="10" xChannelSelector="R" yChannelSelector="G" result="displaced"/>
<feGaussianBlur in="displaced" stdDeviation="10"/>
</filter>
<!-- ============================================ -->
<!-- WAVE/WATER FILTERS -->
<!-- ============================================ -->
<!-- Ocean swell -->
<filter id="wave-ocean" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="turbulence" baseFrequency="0.005 0.015" numOctaves="3" seed="10" result="turbulence">
<animate attributeName="baseFrequency" dur="20s" values="0.005 0.015; 0.007 0.018; 0.005 0.015" repeatCount="indefinite"/>
</feTurbulence>
<feDisplacementMap in="SourceGraphic" in2="turbulence" scale="40" xChannelSelector="R" yChannelSelector="G"/>
</filter>
<!-- Lake ripples -->
<filter id="wave-lake" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="turbulence" baseFrequency="0.02 0.04" numOctaves="2" seed="11" result="turbulence">
<animate attributeName="baseFrequency" dur="8s" values="0.02 0.04; 0.025 0.05; 0.02 0.04" repeatCount="indefinite"/>
</feTurbulence>
<feDisplacementMap in="SourceGraphic" in2="turbulence" scale="15" xChannelSelector="R" yChannelSelector="G"/>
</filter>
<!-- Beach shore -->
<filter id="wave-beach" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="turbulence" baseFrequency="0.008 0.02" numOctaves="4" seed="12" result="turbulence">
<animate attributeName="baseFrequency" dur="15s" values="0.008 0.02; 0.01 0.025; 0.008 0.02" repeatCount="indefinite"/>
</feTurbulence>
<feDisplacementMap in="SourceGraphic" in2="turbulence" scale="30" xChannelSelector="R" yChannelSelector="G"/>
</filter>
<!-- Pool reflection -->
<filter id="wave-pool" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="turbulence" baseFrequency="0.03 0.03" numOctaves="2" seed="13" result="turbulence">
<animate attributeName="baseFrequency" dur="5s" values="0.03 0.03; 0.035 0.035; 0.03 0.03" repeatCount="indefinite"/>
</feTurbulence>
<feDisplacementMap in="SourceGraphic" in2="turbulence" scale="10" xChannelSelector="R" yChannelSelector="G"/>
</filter>
<!-- ============================================ -->
<!-- FOG/MIST FILTERS -->
<!-- ============================================ -->
<!-- Light haze -->
<filter id="fog-light" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="fractalNoise" baseFrequency="0.005" numOctaves="3" seed="20" result="noise"/>
<feGaussianBlur in="noise" stdDeviation="15" result="blur"/>
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.3 0"/>
</filter>
<!-- Morning mist -->
<filter id="fog-mist" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="fractalNoise" baseFrequency="0.003" numOctaves="4" seed="21" result="noise"/>
<feGaussianBlur in="noise" stdDeviation="25" result="blur"/>
<feColorMatrix type="matrix" values="0.9 0 0 0 0.05 0 0.9 0 0 0.05 0 0 0.95 0 0.08 0 0 0 0.5 0"/>
</filter>
<!-- Dense fog -->
<filter id="fog-dense" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="fractalNoise" baseFrequency="0.002" numOctaves="5" seed="22" result="noise"/>
<feGaussianBlur in="noise" stdDeviation="40" result="blur"/>
<feColorMatrix type="matrix" values="0.8 0 0 0 0.1 0 0.85 0 0 0.1 0 0 0.9 0 0.12 0 0 0 0.7 0"/>
</filter>
<!-- Eerie fog (horror) -->
<filter id="fog-eerie" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="fractalNoise" baseFrequency="0.004" numOctaves="4" seed="23" result="noise"/>
<feGaussianBlur in="noise" stdDeviation="30" result="blur"/>
<feColorMatrix type="matrix" values="0.5 0 0 0 0.1 0 0.55 0 0 0.12 0 0 0.65 0 0.15 0 0 0 0.6 0"/>
</filter>
<!-- ============================================ -->
<!-- LIGHTNING GLOW -->
<!-- ============================================ -->
<filter id="lightning-glow" x="-100%" y="-100%" width="300%" height="300%">
<feGaussianBlur stdDeviation="4" result="blur"/>
<feMerge>
<feMergeNode in="blur"/>
<feMergeNode in="blur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="lightning-intense" x="-100%" y="-100%" width="300%" height="300%">
<feGaussianBlur stdDeviation="8" result="blur"/>
<feColorMatrix type="matrix" values="1 0 0 0 0.2 0 1 0 0 0.3 0 0 1.5 0 0.5 0 0 0 1 0" result="tinted"/>
<feMerge>
<feMergeNode in="tinted"/>
<feMergeNode in="blur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<!-- ============================================ -->
<!-- FOAM/SPRAY TEXTURE -->
<!-- ============================================ -->
<filter id="foam-texture" x="-20%" y="-20%" width="140%" height="140%">
<feTurbulence type="turbulence" baseFrequency="0.02 0.05" numOctaves="3" seed="30" result="noise"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="5" xChannelSelector="R" yChannelSelector="G"/>
</filter>
<filter id="spray-particles" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence type="turbulence" baseFrequency="0.1" numOctaves="2" seed="31" result="noise"/>
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 15 -7" result="particles"/>
<feGaussianBlur in="particles" stdDeviation="0.5"/>
</filter>
<!-- ============================================ -->
<!-- AURORA GLOW -->
<!-- ============================================ -->
<filter id="aurora-glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="30" result="blur"/>
<feColorMatrix type="saturate" values="1.5" result="saturated"/>
<feMerge>
<feMergeNode in="saturated"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<!-- ============================================ -->
<!-- GOD RAYS SOFT EDGE -->
<!-- ============================================ -->
<filter id="godrays-mask" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="20"/>
</filter>
<!-- ============================================ -->
<!-- LENS FLARE GLOW -->
<!-- ============================================ -->
<filter id="flare-glow" x="-100%" y="-100%" width="300%" height="300%">
<feGaussianBlur stdDeviation="15" result="blur1"/>
<feGaussianBlur stdDeviation="30" result="blur2"/>
<feMerge>
<feMergeNode in="blur2"/>
<feMergeNode in="blur1"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
</svg>