Web Wave Designer
Expert in creating realistic, performant ocean and water wave effects for web applications using SVG filters, CSS animations, and layering techniques. Specializes in aquatic visuals from gentle ripples to dramatic ocean swells, with particular expertise in the physics of light refraction through water.
When to Use This Skill
Use for:
Ocean wave backgrounds and seascapes Underwater distortion/refraction effects Beach shore waves with foam Pond/pool ripple animations Liquid glass UI effects Water-themed loading states Parallax ocean layers with depth Stylized/cartoon water for games Reflection effects on water surfaces
Do NOT use for:
3D volumetric ocean rendering -> use WebGL/Three.js/Ocean.js Real-time fluid simulation -> use canvas physics engines Video effects -> use video editing software Simple blue gradients without motion Water droplet physics -> use particle systems Core Distinction: turbulence vs fractalNoise
CRITICAL: For water effects, use type="turbulence" (NOT fractalNoise like clouds):
Type Visual Best For turbulence Continuous flow patterns, starts from transparent black Water, waves, liquid fractalNoise Random cloudlike patches, opaque Clouds, smoke, terrain
SVG Filter Pipeline for Water
The fundamental water effect filter chain:
Source -> feTurbulence -> feDisplacementMap -> feComponentTransfer -> feComposite (waves) (distortion) (color/opacity) (blend)
- feTurbulence - Wave Pattern Generation
baseFrequency="0.01 0.1" \ numOctaves="3" \ seed="42" \ result="waves" />
baseFrequency Explained (TWO Values):
X-Frequency Y-Frequency Result 0.01 0.1 Long horizontal waves with vertical oscillation 0.005 0.05 Deep ocean swells 0.02 0.15 Choppy surface waves 0.03 0.03 Square ripples (pond)
The ratio matters:
X << Y: Stretched horizontal waves (ocean) X == Y: Circular ripples (pond, pool) X >> Y: Vertical striations (waterfall) 2. feDisplacementMap - Refraction Effect
Creates the bending/distortion that makes content behind water appear to ripple.
Scale Value Effect 10-15 Gentle pool ripples 15-25 Standard water refraction 25-40 Strong wave distortion 40+ Psychedelic (unrealistic) 3. feComponentTransfer - Water Color
Transform noise into water-like colors by manipulating channels.
-
feGaussianBlur - Caustics (Underwater Light)
result="caustics" /> -
Compositing - Layer Assembly
Wave Type Recipes Ocean Surface Waves
Pond Ripples (Circular)
Beach Shore Waves (Breaking)
Underwater Distortion (Looking Through Water)
Liquid Glass Effect (Modern UI)
Stylized/Cartoon Waves
Animation Techniques JavaScript requestAnimationFrame (Smoothest) const turbulence = document.querySelector('#seaFilter feTurbulence'); let frame = 0;
function animateWaves() { frame += 0.003;
// Gentle breathing motion const xFreq = 0.006 + Math.sin(frame) * 0.002; const yFreq = 0.05 + Math.sin(frame * 0.7) * 0.01;
turbulence.setAttribute('baseFrequency', ${xFreq} ${yFreq});
requestAnimationFrame(animateWaves);
}
animateWaves();
SVG animate (Declarative, CPU-Heavy)
WARNING: SVG animate on filter attributes forces full filter recalculation every frame. Use sparingly.
CSS Transform Animation (Best Performance)
Move the water element, not the filter:
.wave-layer { animation: wave-drift 20s linear infinite; }
@keyframes wave-drift { from { transform: translateX(0) translateY(0); } to { transform: translateX(-50%) translateY(5px); } }
Seed Animation (Morphing Waves)
Animate seed for shape variation without baseFrequency cost:
Layering Strategy Multi-Layer Ocean
.ocean { position: relative; height: 100vh; background: linear-gradient(180deg, #0c4a6e 0%, #0369a1 40%, #0ea5e9 100% ); overflow: hidden; }
.wave { position: absolute; width: 200%; height: 100%; background: rgba(255,255,255,0.1); }
.wave-back { filter: url(#waveBack); opacity: 0.3; animation: drift 90s linear infinite; bottom: 0; }
.wave-mid { filter: url(#waveMid); opacity: 0.5; animation: drift 60s linear infinite; bottom: -5%; }
.wave-front { filter: url(#waveFront); opacity: 0.7; animation: drift 35s linear infinite; bottom: -10%; }
.foam-layer { position: absolute; bottom: 0; width: 100%; height: 20%; background: linear-gradient(to top, rgba(255,255,255,0.8) 0%, transparent 100% ); filter: url(#foamFilter); }
@keyframes drift { from { transform: translateX(0); } to { transform: translateX(-50%); } }
Layer Parameter Guide Layer Opacity Speed Filter Scale baseFrequency Back (deep) 0.2-0.4 80-100s 15 0.004 0.04 Mid 0.4-0.6 50-70s 20 0.006 0.06 Front (surface) 0.6-0.8 30-45s 25 0.01 0.1 Foam 0.7-0.9 25-35s 10 0.02 0.02 Color Palettes Deep Ocean .deep-ocean { background: linear-gradient(180deg, #0c4a6e 0%, / Deep blue / #075985 30%, #0369a1 60%, #0284c7 100% / Surface shimmer / ); }
Primary: #0369a1 Deep: #0c4a6e Highlight: #38bdf8 Tropical/Caribbean .tropical { background: linear-gradient(180deg, #06b6d4 0%, / Cyan surface / #22d3ee 40%, #67e8f9 70%, #a5f3fc 100% / Shallow sand reflection / ); }
Primary: #06b6d4 Shallow: #67e8f9 Foam: #ecfeff Stormy Sea .stormy { background: linear-gradient(180deg, #1e293b 0%, / Dark clouds / #334155 30%, #475569 60%, #64748b 100% / Whitecaps / ); }
Primary: #475569 Depth: #1e293b Whitecap: #cbd5e1 Sunset Reflection .sunset-water { background: linear-gradient(180deg, #831843 0%, / Pink sky / #9d174d 20%, #be185d 40%, #0369a1 60%, / Water starts / #0c4a6e 100% ); }
Complete Implementation Templates Template 1: Full Ocean Scene
Template 2: Underwater View (Content Behind Water)
Template 3: React Water Component import React, { useEffect, useRef, useMemo } from 'react';
interface WaterEffectProps { type?: 'ocean' | 'pool' | 'stream' | 'glass'; intensity?: 'subtle' | 'medium' | 'strong'; animate?: boolean; className?: string; children?: React.ReactNode; }
const WATER_CONFIGS = { ocean: { baseFreq: [0.008, 0.08], octaves: 4, scale: 25 }, pool: { baseFreq: [0.02, 0.02], octaves: 2, scale: 12 }, stream: { baseFreq: [0.01, 0.15], octaves: 3, scale: 20 }, glass: { baseFreq: [0.01, 0.05], octaves: 2, scale: 8 }, };
const INTENSITY_MULTIPLIERS = { subtle: 0.5, medium: 1.0, strong: 1.5, };
export const WaterEffect: React.FC
const filterId = useMemo(() =>
water-${type}-${Date.now()}, [type]
);
useEffect(() => { if (!animate || !turbRef.current) return;
let animationId: number;
const animateWater = () => {
frameRef.current += 0.004;
const frame = frameRef.current;
const xFreq = config.baseFreq[0] + Math.sin(frame) * 0.002;
const yFreq = config.baseFreq[1] + Math.sin(frame * 0.7) * 0.01;
turbRef.current?.setAttribute('baseFrequency', `${xFreq} ${yFreq}`);
animationId = requestAnimationFrame(animateWater);
};
animateWater();
return () => cancelAnimationFrame(animationId);
}, [animate, config]);
return ( <>
// Usage:
//
//
Template 4: CSS-Only Waves (No SVG)
For simple, high-performance waves without SVG filters:
.css-waves { position: relative; height: 300px; background: linear-gradient(180deg, #0369a1 0%, #0c4a6e 100%); overflow: hidden; }
.css-wave { position: absolute; width: 200%; height: 100%; bottom: 0; left: -50%; background: radial-gradient(ellipse 100% 50% at 50% 100%, rgba(255,255,255,0.3) 0%, transparent 60% ); animation: css-wave-move 8s ease-in-out infinite; transform-origin: center bottom; }
.css-wave:nth-child(1) { animation-duration: 7s; opacity: 0.5; }
.css-wave:nth-child(2) { animation-duration: 10s; animation-delay: -3s; opacity: 0.3; }
.css-wave:nth-child(3) { animation-duration: 13s; animation-delay: -5s; opacity: 0.2; }
@keyframes css-wave-move { 0%, 100% { transform: translateX(0) scaleY(1); } 50% { transform: translateX(25%) scaleY(1.1); } }
Performance Optimization
Critical Rules
Use type="turbulence" - Correct type for water (not fractalNoise)
numOctaves <= 4 - Above 4 minimal visual gain, exponential CPU cost
Scale 10-30 - Above 40 becomes unrealistic and slower
Avoid animating baseFrequency - Use CSS transforms or seed animation instead
GPU hints - Add will-change: transform on animated layers
Batch SVG defs - One
@media (max-width: 768px) { / Reduce to 2 wave layers / .wave-1 { display: none; }
/ Use simpler filter / .wave { filter: url(#waveSimple); } }
Performance Detection const canHandleWaterEffects = () => { // Check for GPU const canvas = document.createElement('canvas'); const gl = canvas.getContext('webgl'); if (!gl) return 'ultra'; // CSS only
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); const renderer = debugInfo ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : '';
// Integrated graphics = medium tier if (renderer.includes('Intel') || renderer.includes('Mali')) { return 'medium'; }
return 'high'; };
// Apply appropriate tier const tier = canHandleWaterEffects(); document.body.dataset.waterTier = tier;
/ Tier-based styling / [data-water-tier="ultra"] .wave { filter: none; background: linear-gradient(/ simple gradient /); }
[data-water-tier="medium"] .wave { filter: url(#waveSimple); }
[data-water-tier="high"] .wave { filter: url(#waveFull); }
Framework Integration Next.js / React // components/OceanBackground.tsx 'use client';
import { useEffect, useState, useRef } from 'react'; import styles from './OceanBackground.module.css';
export function OceanBackground({ children }: { children: React.ReactNode }) {
const [mounted, setMounted] = useState(false);
const turbRef = useRef
useEffect(() => { setMounted(true);
// Animate after mount
let frame = 0;
const animate = () => {
frame += 0.003;
if (turbRef.current) {
const xFreq = 0.008 + Math.sin(frame) * 0.002;
const yFreq = 0.08 + Math.sin(frame * 0.7) * 0.01;
turbRef.current.setAttribute('baseFrequency', `${xFreq} ${yFreq}`);
}
requestAnimationFrame(animate);
};
const id = requestAnimationFrame(animate);
return () => cancelAnimationFrame(id);
}, []);
if (!mounted) return null;
return (
Tailwind CSS // tailwind.config.js module.exports = { theme: { extend: { animation: { 'wave-drift': 'wave-drift 60s linear infinite', 'wave-slow': 'wave-drift 90s linear infinite', 'wave-fast': 'wave-drift 35s linear infinite', }, keyframes: { 'wave-drift': { from: { transform: 'translateX(0)' }, to: { transform: 'translateX(-50%)' }, }, }, colors: { ocean: { deep: '#0c4a6e', mid: '#0369a1', surface: '#0ea5e9', foam: '#f0f9ff', }, }, }, }, };
Vue 3
Debugging Tips Visualize Filter Pipeline
Common Issues Problem Cause Solution Effect disappears Filter region too small Add x="-20%" y="-20%" width="140%" height="140%" Square/boxy waves Using fractalNoise Change to type="turbulence" Waves too uniform Same seed across layers Use different seed values No horizontal motion Equal baseFrequency values Use baseFrequency="0.01 0.1" (different x/y) Animation stuttering Animating filter attributes Use CSS transform animations instead Edge artifacts Displacement at boundaries Increase filter region with x/y/width/height Browser DevTools Elements panel > Select SVG filter > Inspect attributes Performance panel > Record > Check for layout thrashing Layers panel (Chrome) > Verify GPU acceleration on wave layers Integration with web-cloud-designer
For complete atmospheric scenes, combine water and cloud effects:
Reference Sources Red Stapler: "Realistic Water Effect SVG Turbulence" Mitkov Systems: "Liquid Glass Water Animation" (2025) O'Reilly SVG Book: feTurbulence Chapter MDN: SVG Filter Primitives Documentation CSS-Tricks: "Underwater Blur Effect" Codrops: "Water Distortion Effect"
Water is the driving force of all nature. - Leonardo da Vinci