Motion Craft
EliasOulkadi/shokunin
Design GPU-accelerated animations with Web Animations API (WAAPI), Scroll-Driven Animations (ScrollTimeline/ViewTimeline), FLIP technique, easing systems, and accessibility (prefers-reduced-motion).
Creates realistic ocean and water wave effects for web using SVG filters (feTurbulence, feDisplacementMap), CSS animations, and layering techniques.
$ npx skills add curiositech/some_claude_skills --skill web-wave-designer -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install curiositech/some_claude_skills web-wave-designer --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/curiositech/some_claude_skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/web-wave-designer .claude/skills/web-wave-designer && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "web-wave-designer" agent skill from https://github.com/curiositech/some_claude_skills/tree/main/.claude/skills/web-wave-designer into .claude/skills/web-wave-designer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "web-wave-designer", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/curiositech/some_claude_skills/tree/main/.claude/skills/web-wave-designerType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add curiositech/some_claude_skills --skill web-wave-designer -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install curiositech/some_claude_skills web-wave-designer --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/curiositech/some_claude_skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/web-wave-designer .agents/skills/web-wave-designer && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "web-wave-designer" agent skill from https://github.com/curiositech/some_claude_skills/tree/main/.claude/skills/web-wave-designer into .agents/skills/web-wave-designer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "web-wave-designer", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add curiositech/some_claude_skills --skill web-wave-designer -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install curiositech/some_claude_skills web-wave-designer --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/curiositech/some_claude_skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/web-wave-designer .cursor/skills/web-wave-designer && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "web-wave-designer" agent skill from https://github.com/curiositech/some_claude_skills/tree/main/.claude/skills/web-wave-designer into .cursor/skills/web-wave-designer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "web-wave-designer", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/curiositech/some_claude_skills.git --path .claude/skills/web-wave-designer--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add curiositech/some_claude_skills --skill web-wave-designer -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install curiositech/some_claude_skills web-wave-designer --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/curiositech/some_claude_skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/web-wave-designer .gemini/skills/web-wave-designer && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "web-wave-designer" agent skill from https://github.com/curiositech/some_claude_skills/tree/main/.claude/skills/web-wave-designer into .gemini/skills/web-wave-designer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "web-wave-designer", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install curiositech/some_claude_skills web-wave-designerInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add curiositech/some_claude_skills --skill web-wave-designer -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/curiositech/some_claude_skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/web-wave-designer .github/skills/web-wave-designer && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "web-wave-designer" agent skill from https://github.com/curiositech/some_claude_skills/tree/main/.claude/skills/web-wave-designer into .github/skills/web-wave-designer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "web-wave-designer", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add curiositech/some_claude_skills --skill web-wave-designer -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install curiositech/some_claude_skills web-wave-designer --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/curiositech/some_claude_skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/web-wave-designer .opencode/skills/web-wave-designer && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "web-wave-designer" agent skill from https://github.com/curiositech/some_claude_skills/tree/main/.claude/skills/web-wave-designer into .opencode/skills/web-wave-designer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "web-wave-designer", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
web-wave-designerCreates realistic ocean and water wave effects for web using SVG filters (feTurbulence, feDisplacementMap), CSS animations, and layering techniques.
Web Wave Designer is an agent skill from curiositech/some_claude_skills. Creates realistic ocean and water wave effects for web using SVG filters (feTurbulence, feDisplacementMap), CSS animations, and layering techniques. Use for ocean backgrounds, underwater distortion, beach scenes, ripple effects, liquid glass, and water-themed UI. Activate on "ocean wave", "water effect", "SVG water", "ripple animation", "underwater distortion", "liquid glass", "wave animation", "feTurbulence water", "beach waves", "sea foam". NOT for 3D ocean simulation (use WebGL/Three.js), video water effects…
Its SKILL.md is about 7k tokens, which your agent loads only when the skill is triggered. The skill folder holds 7 other files, including reference files (for example `.claude-plugin/plugin.json`, `CHANGELOG.md` and `references/animation-patterns.md`).
It sits in Game Development, covering 3D graphics and WebGL, Web animation and motion and Video production. It works with Three.js. The repository describes itself as: Claude skills that make my life easier. The licence is MIT.
5 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 6713fc7. It shows what the files ask for, not the result of running them.
Pre-approves these tools, so the agent can use them without asking each time:
ReadWriteEditWebFetchBashFrom allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are xml, css, svg, html, javascript, typescript and vue).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Web Wave Designer loads about 7k tokens when it runs, and up to ~14k if it reads all its reference files. Until then it costs about 165 tokens; SKILL.md has 732 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
allowed-tools: Read, Write, Edit, WebFetch, BashAutomated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from curiositech/some_claude_skills at commit 6713fc7, republished under its MIT licence (© curiositech). 732 words, ~7,031 tokens.
.claude/skills/web-wave-designer/SKILL.md (or your agent's skills folder). This skill also uses 5 other files; get the full folder from GitHub.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.
Use for:
Do NOT use for:
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 |
<!-- WATER - use turbulence -->
<feTurbulence type="turbulence" baseFrequency="0.01 0.1" />
<!-- CLOUDS - use fractalNoise -->
<feTurbulence type="fractalNoise" baseFrequency="0.01" />The fundamental water effect filter chain:
Source -> feTurbulence -> feDisplacementMap -> feComponentTransfer -> feComposite
(waves) (distortion) (color/opacity) (blend)<feTurbulence
type="turbulence" <!-- MUST be turbulence for water -->
baseFrequency="0.01 0.1" <!-- TWO values: x-freq y-freq -->
numOctaves="3" <!-- 2-5: wave complexity -->
seed="42" <!-- Variation (free performance) -->
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:
Creates the bending/distortion that makes content behind water appear to ripple.
<feDisplacementMap
in="SourceGraphic" <!-- What gets distorted -->
in2="waves" <!-- Distortion pattern (from turbulence) -->
scale="20" <!-- 10-40 for realistic refraction -->
xChannelSelector="R" <!-- Which color channel drives X displacement -->
yChannelSelector="G" <!-- Which color channel drives Y displacement -->
result="refracted"
/>| Scale Value | Effect |
|---|---|
| 10-15 | Gentle pool ripples |
| 15-25 | Standard water refraction |
| 25-40 | Strong wave distortion |
| 40+ | Psychedelic (unrealistic) |
Transform noise into water-like colors by manipulating channels.
<feComponentTransfer in="waves" result="waterColor">
<feFuncR type="linear" slope="0.3" intercept="0"/> <!-- Reduce red -->
<feFuncG type="linear" slope="0.7" intercept="0.2"/> <!-- Boost green -->
<feFuncB type="linear" slope="1.2" intercept="0.3"/> <!-- Strong blue -->
<feFuncA type="linear" slope="0.6" intercept="0.3"/> <!-- Water opacity -->
</feComponentTransfer><feGaussianBlur
in="waves"
stdDeviation="2" <!-- 1-5 for soft caustic patterns -->
result="caustics"
/><feFlood flood-color="#0077be" flood-opacity="0.4" result="baseWater"/>
<feBlend in="caustics" in2="baseWater" mode="screen" result="waterLayer"/>
<feComposite in="waterLayer" in2="SourceGraphic" operator="over"/><svg width="100%" height="300">
<defs>
<filter id="oceanWaves" x="0" y="0" width="100%" height="100%">
<feTurbulence type="turbulence"
baseFrequency="0.005 0.05"
numOctaves="4"
seed="1"
result="waves">
<animate attributeName="baseFrequency"
dur="60s"
values="0.005 0.05;0.007 0.06;0.005 0.05"
repeatCount="indefinite"/>
</feTurbulence>
<feDisplacementMap in="SourceGraphic" in2="waves" scale="25"/>
</filter>
</defs>
<rect width="100%" height="100%" fill="url(#oceanGradient)" filter="url(#oceanWaves)"/>
</svg><filter id="pondRipples">
<feTurbulence type="turbulence"
baseFrequency="0.02 0.02" <!-- Equal = circular -->
numOctaves="2"
seed="10"
result="ripples"/>
<feDisplacementMap in="SourceGraphic" in2="ripples" scale="12"/>
</filter><filter id="shoreWaves">
<feTurbulence type="turbulence"
baseFrequency="0.008 0.12" <!-- Strong vertical motion -->
numOctaves="3"
seed="5"
result="waves"/>
<feGaussianBlur in="waves" stdDeviation="1.5" result="softWaves"/>
<feDisplacementMap in="SourceGraphic" in2="softWaves" scale="30"/>
<!-- Add foam layer -->
<feTurbulence type="fractalNoise"
baseFrequency="0.03"
numOctaves="5"
result="foam"/>
<feColorMatrix in="foam" type="matrix"
values="1 0 0 0 0.9
1 0 0 0 0.95
1 0 0 0 1
0 0 0 0.3 0"/>
</filter><filter id="underwater" x="-10%" y="-10%" width="120%" height="120%">
<feTurbulence type="turbulence"
baseFrequency="0.015 0.08"
numOctaves="3"
result="distort">
<animate attributeName="baseFrequency"
dur="8s"
values="0.015 0.08;0.018 0.09;0.015 0.08"
repeatCount="indefinite"/>
</feTurbulence>
<feDisplacementMap in="SourceGraphic" in2="distort"
scale="20"
xChannelSelector="R"
yChannelSelector="B"/>
<!-- Slight blue tint -->
<feColorMatrix type="matrix"
values="0.9 0 0 0 0
0 0.95 0 0 0.02
0 0 1.1 0 0.05
0 0 0 1 0"/>
</filter><filter id="liquidGlass" x="-5%" y="-5%" width="110%" height="110%">
<feTurbulence type="turbulence"
baseFrequency="0.01 0.05"
numOctaves="2"
seed="99"
result="ripple">
<animate attributeName="seed"
dur="4s"
values="99;100;101;100;99"
repeatCount="indefinite"/>
</feTurbulence>
<feDisplacementMap in="SourceGraphic" in2="ripple" scale="8"/>
<!-- Frosted glass blur -->
<feGaussianBlur stdDeviation="0.5"/>
</filter><filter id="cartoonWater">
<feTurbulence type="turbulence"
baseFrequency="0.02 0.08"
numOctaves="1" <!-- Low octaves = bold shapes -->
result="waves"/>
<feDisplacementMap in="SourceGraphic" in2="waves" scale="15"/>
<!-- Sharp edges, no blur -->
</filter>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();<feTurbulence baseFrequency="0.01 0.1" numOctaves="3">
<animate
attributeName="baseFrequency"
dur="60s"
keyTimes="0;0.5;1"
values="0.008 0.08;0.012 0.12;0.008 0.08"
repeatCount="indefinite"
/>
</feTurbulence>WARNING: SVG animate on filter attributes forces full filter recalculation every frame. Use sparingly.
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); }
}Animate seed for shape variation without baseFrequency cost:
<feTurbulence baseFrequency="0.01 0.1">
<animate
attributeName="seed"
dur="20s"
values="1;50;100;50;1"
repeatCount="indefinite"
/>
</feTurbulence><div class="ocean">
<div class="wave wave-back"></div>
<div class="wave wave-mid"></div>
<div class="wave wave-front"></div>
<div class="foam-layer"></div>
</div>.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 | 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 |
.deep-ocean {
background: linear-gradient(180deg,
#0c4a6e 0%, /* Deep blue */
#075985 30%,
#0369a1 60%,
#0284c7 100% /* Surface shimmer */
);
}#0369a1#0c4a6e#38bdf8.tropical {
background: linear-gradient(180deg,
#06b6d4 0%, /* Cyan surface */
#22d3ee 40%,
#67e8f9 70%,
#a5f3fc 100% /* Shallow sand reflection */
);
}#06b6d4#67e8f9#ecfeff.stormy {
background: linear-gradient(180deg,
#1e293b 0%, /* Dark clouds */
#334155 30%,
#475569 60%,
#64748b 100% /* Whitecaps */
);
}#475569#1e293b#cbd5e1.sunset-water {
background: linear-gradient(180deg,
#831843 0%, /* Pink sky */
#9d174d 20%,
#be185d 40%,
#0369a1 60%, /* Water starts */
#0c4a6e 100%
);
}<!DOCTYPE html>
<html>
<head>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
.ocean-scene {
position: relative;
width: 100%;
height: 100vh;
background: linear-gradient(180deg,
#87CEEB 0%, /* Sky */
#87CEEB 40%,
#0369a1 40%, /* Horizon */
#0c4a6e 100% /* Deep */
);
overflow: hidden;
}
.horizon-line {
position: absolute;
top: 40%;
left: 0;
right: 0;
height: 2px;
background: rgba(255,255,255,0.3);
}
.wave {
position: absolute;
width: 200%;
height: 60%;
bottom: 0;
background: rgba(255,255,255,0.15);
}
.wave-1 {
filter: url(#wave1);
animation: drift 80s linear infinite;
opacity: 0.4;
}
.wave-2 {
filter: url(#wave2);
animation: drift 55s linear infinite;
animation-delay: -20s;
opacity: 0.6;
}
.wave-3 {
filter: url(#wave3);
animation: drift 35s linear infinite;
animation-delay: -10s;
opacity: 0.8;
}
@keyframes drift {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
</style>
</head>
<body>
<svg style="position:absolute;width:0;height:0">
<defs>
<filter id="wave1" x="0" y="0" width="100%" height="100%">
<feTurbulence type="turbulence" baseFrequency="0.004 0.04" numOctaves="3" seed="1"/>
<feDisplacementMap in="SourceGraphic" scale="15"/>
</filter>
<filter id="wave2" x="0" y="0" width="100%" height="100%">
<feTurbulence type="turbulence" baseFrequency="0.006 0.06" numOctaves="3" seed="2"/>
<feDisplacementMap in="SourceGraphic" scale="20"/>
</filter>
<filter id="wave3" x="0" y="0" width="100%" height="100%">
<feTurbulence type="turbulence" baseFrequency="0.01 0.1" numOctaves="4" seed="3"/>
<feDisplacementMap in="SourceGraphic" scale="25"/>
</filter>
</defs>
</svg>
<div class="ocean-scene">
<div class="horizon-line"></div>
<div class="wave wave-1"></div>
<div class="wave wave-2"></div>
<div class="wave wave-3"></div>
</div>
</body>
</html><style>
.underwater-container {
position: relative;
width: 100%;
overflow: hidden;
}
.content-behind-water {
/* Your actual content */
}
.water-overlay {
position: absolute;
inset: 0;
pointer-events: none;
filter: url(#underwaterDistort);
}
.caustics {
position: absolute;
inset: 0;
background: url('data:image/svg+xml,...') repeat;
opacity: 0.3;
mix-blend-mode: overlay;
animation: caustic-shift 10s linear infinite;
}
@keyframes caustic-shift {
from { background-position: 0 0; }
to { background-position: 100px 50px; }
}
</style>
<svg style="display:none">
<defs>
<filter id="underwaterDistort" x="-10%" y="-10%" width="120%" height="120%">
<feTurbulence id="underwaterTurb" type="turbulence"
baseFrequency="0.015 0.08" numOctaves="3"/>
<feDisplacementMap in="SourceGraphic" scale="20"
xChannelSelector="R" yChannelSelector="B"/>
<feColorMatrix type="matrix"
values="0.85 0 0 0 0
0 0.9 0 0 0.02
0 0 1.1 0 0.08
0 0 0 1 0"/>
</filter>
</defs>
</svg>
<script>
// Animate underwater distortion
const turb = document.getElementById('underwaterTurb');
let frame = 0;
function animate() {
frame += 0.005;
const xFreq = 0.015 + Math.sin(frame) * 0.003;
const yFreq = 0.08 + Math.sin(frame * 0.7) * 0.01;
turb.setAttribute('baseFrequency', `${xFreq} ${yFreq}`);
requestAnimationFrame(animate);
}
animate();
</script>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<WaterEffectProps> = ({
type = 'ocean',
intensity = 'medium',
animate = true,
className,
children
}) => {
const turbRef = useRef<SVGFETurbulenceElement>(null);
const frameRef = useRef(0);
const config = WATER_CONFIGS[type];
const mult = INTENSITY_MULTIPLIERS[intensity];
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 (
<>
<svg style={{ position: 'absolute', width: 0, height: 0 }}>
<defs>
<filter id={filterId} x="-10%" y="-10%" width="120%" height="120%">
<feTurbulence
ref={turbRef}
type="turbulence"
baseFrequency={config.baseFreq.join(' ')}
numOctaves={config.octaves}
result="waves"
/>
<feDisplacementMap
in="SourceGraphic"
in2="waves"
scale={config.scale * mult}
xChannelSelector="R"
yChannelSelector="G"
/>
</filter>
</defs>
</svg>
<div className={className} style={{ filter: `url(#${filterId})` }}>
{children}
</div>
</>
);
};
// Usage:
// <WaterEffect type="ocean" intensity="medium" animate>
// <img src="underwater-scene.jpg" />
// </WaterEffect>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);
}
}type="turbulence" - Correct type for water (not fractalNoise)will-change: transform on animated layers<defs> block, multiple filters| Tier | Technique | FPS Target | Use Case |
|---|---|---|---|
| Ultra | CSS radial gradients only | 60fps | Mobile, low-end |
| High | SVG filter, CSS transform animation | 60fps | Background waves |
| Medium | SVG filter + seed animation | 45-60fps | Interactive water |
| Low | SVG filter + baseFrequency animation | 30-40fps | Hero sections only |
@media (prefers-reduced-motion: reduce) {
.wave {
animation: none !important;
}
}
@media (max-width: 768px) {
/* Reduce to 2 wave layers */
.wave-1 { display: none; }
/* Use simpler filter */
.wave { filter: url(#waveSimple); }
}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);
}// 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<SVGFETurbulenceElement>(null);
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 (
<div className={styles.ocean}>
<svg className={styles.filters}>
<defs>
<filter id="oceanWave">
<feTurbulence ref={turbRef} type="turbulence"
baseFrequency="0.008 0.08" numOctaves="4"/>
<feDisplacementMap in="SourceGraphic" scale="25"/>
</filter>
</defs>
</svg>
<div className={styles.waveLayer} />
<div className={styles.content}>{children}</div>
</div>
);
}// 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',
},
},
},
},
};<template>
<div class="ocean-container">
<WaveFilters />
<div
v-for="layer in waveLayers"
:key="layer.id"
class="wave-layer"
:style="layer.style"
/>
<slot />
</div>
</template>
<script setup>
import { computed, onMounted, ref } from 'vue';
import WaveFilters from './WaveFilters.vue';
const waveLayers = computed(() => [
{ id: 1, style: { filter: 'url(#wave1)', animationDuration: '80s', opacity: 0.4 }},
{ id: 2, style: { filter: 'url(#wave2)', animationDuration: '55s', opacity: 0.6 }},
{ id: 3, style: { filter: 'url(#wave3)', animationDuration: '35s', opacity: 0.8 }},
]);
</script><!-- Show each filter step -->
<filter id="debug-water">
<feTurbulence result="step1"/>
<feImage href="#step1" x="0" y="0" width="200" height="200"/>
<feDisplacementMap in="SourceGraphic" in2="step1" result="step2"/>
<feImage href="#step2" x="200" y="0" width="200" height="200"/>
</filter>| 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 |
For complete atmospheric scenes, combine water and cloud effects:
<div class="scene">
<!-- Sky with clouds (from web-cloud-designer) -->
<div class="sky">
<div class="cloud-layer cloud-back"></div>
<div class="cloud-layer cloud-front"></div>
</div>
<!-- Horizon -->
<div class="horizon"></div>
<!-- Ocean with waves (this skill) -->
<div class="ocean">
<div class="wave wave-back"></div>
<div class="wave wave-front"></div>
<div class="reflection"></div>
</div>
</div>
<style>
.scene {
height: 100vh;
display: grid;
grid-template-rows: 60% 40%;
}
.sky {
background: linear-gradient(180deg, #1e3c72 0%, #87CEEB 100%);
}
.ocean {
background: linear-gradient(180deg, #0369a1 0%, #0c4a6e 100%);
}
.reflection {
/* Mirror cloud movement on water surface */
position: absolute;
top: 0;
width: 100%;
height: 30%;
background: inherit;
transform: scaleY(-1);
opacity: 0.3;
filter: url(#waterReflection) blur(2px);
}
</style>Water is the driving force of all nature. - Leonardo da Vinci
© curiositech, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 5 other files (references) in .claude/skills/web-wave-designer of curiositech/some_claude_skills.
Open the folder on GitHubat commit 6713fc7
Web Wave Designer next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Web Wave Designer this skillcuriositech/some_claude_skills | 243 | — | ~7k | Automated safety check: Notes | MIT | |
| Motion CraftEliasOulkadi/shokunin | 114 | — | ~6.4k | Automated safety check: Pass | MIT | |
| Motion Webfeitangyuan/motion-web | 680 | — | ~18k | Automated safety check: Pass | CC-BY-NC-4.0 | |
| Guizang Product Video Skillop7418/guizang-product-video-skill | 740 | — | ~1.1k | Automated safety check: Pass | AGPL-3.0 | |
| Animationssoftware-mansion-labs/skills | 291 | — | ~1.1k | Automated safety check: Pass | None | |
| Build Awwwards Quality Sitesnirholas/three.ws | 228 | 1 repos | ~1.8k | Automated safety check: Pass | Apache-2.0 |
EliasOulkadi/shokunin
Design GPU-accelerated animations with Web Animations API (WAAPI), Scroll-Driven Animations (ScrollTimeline/ViewTimeline), FLIP technique, easing systems, and accessibility (prefers-reduced-motion).
feitangyuan/motion-web
Design and build motion-first creative websites — page design (structure, type scale, palette, spacing) plus interaction choreography: scroll animation, Framer Motion, GSAP, and light 3D via…
op7418/guizang-product-video-skill
制作代码驱动的软件产品宣传片和版本更新片(product promo、release notes video、changelog promo)。从真实功能和产品设计出发定方向,用真实组件、GSAP/Three.js 动效、原创配乐和动作音效完成影片,每支片子的视觉手法都从产品本身推导,不套模板。
software-mansion-labs/skills
Production animation patterns for React Native using Reanimated 4, Skia, WebGPU, and TypeGPU.
nirholas/three.ws
Art-direct and implement distinctive, motion-rich marketing, editorial, portfolio, and landing websites with original reference-inspired imagery, standout heroes, GSAP choreography, one…
echris6/motion-video-kit
Plan, build and quality-check a premium short commercial for a real business (roofing, property, ecommerce, B2B software) using AI-generated footage, code-built motion (HTML/GSAP), selective…
curiositech/some_claude_skills
Detect crisis signals in user content using NLP, mental health sentiment analysis, and safe intervention protocols.
curiositech/some_claude_skills
End-to-end form handling with react-hook-form, Zod schemas, validation patterns, error messaging, field arrays, and multi-step wizards.
curiositech/some_claude_skills
Build production CI/CD pipelines with GitHub Actions. An agent skill from curiositech/some_claude_skills.
curiositech/some_claude_skills
Expert in background job processing with Bull/BullMQ (Redis), Celery, and cloud queues.
curiositech/some_claude_skills
Strategic analyst that maps competitive landscapes, identifies white space opportunities, and provides positioning recommendations.
curiositech/some_claude_skills
Build production computer vision pipelines for object detection, tracking, and video analysis.
Works with
Categories
Creates realistic ocean and water wave effects for web using SVG filters (feTurbulence, feDisplacementMap), CSS animations, and layering techniques. Web Wave Designer is an agent skill from curiositech/some_claude_skills. Creates realistic ocean and water wave effects for web using SVG filters (feTurbulence, feDisplacementMap), CSS animations, and layering techniques.
Web Wave Designer fits situations like: ocean backgrounds; underwater distortion; water-themed UI.
Run `npx skills add curiositech/some_claude_skills --skill web-wave-designer -a claude-code`. Or copy the skill folder (.claude/skills/web-wave-designer in curiositech/some_claude_skills) into .claude/skills/web-wave-designer in your project. Claude Code loads it when a task matches its description.
Run `npx skills add curiositech/some_claude_skills --skill web-wave-designer -a codex`. Or copy the skill folder (.claude/skills/web-wave-designer in curiositech/some_claude_skills) into .agents/skills/web-wave-designer in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add curiositech/some_claude_skills --skill web-wave-designer -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/web-wave-designer, .gemini/skills/web-wave-designer, .github/skills/web-wave-designer and .opencode/skills/web-wave-designer in your project.
SKILL.md names no scripts, command-line tools or credentials: Web Wave Designer is instructions for the agent only. Its frontmatter pre-approves these tools: Read, Write, Edit, WebFetch, Bash.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (pre-approves every shell command (allowed-tools: bash)), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
Web Wave Designer is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 7k tokens (SKILL.md is roughly 28k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 7.1k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Web Wave Designer: Motion Craft (EliasOulkadi/shokunin, 114 stars), Motion Web (feitangyuan/motion-web, 680 stars), Guizang Product Video Skill (op7418/guizang-product-video-skill, 740 stars) and Animations (software-mansion-labs/skills, 291 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
curiositech (a GitHub organization) maintains it in curiositech/some_claude_skills, which has 243 GitHub stars. The repository holds 95 skills in this directory. The repository was last updated on September 6, 2026.
Source: curiositech/some_claude_skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.