Backgrounds & Colors
Animated backgrounds, mesh gradients, chromatic colors and noise textures.
Updated
What makes a good animated background for a website?
An animated background adds movement behind content without competing with it. Most are pure CSS: gradients moved with @keyframes, mesh gradients built from stacked radial-gradient() layers, SVG noise for grain. Canvas or WebGL is used for water, rain or particles that react to the pointer.
How to choose, performance and accessibility
- Pick a quiet effect such as Mesh Gradient or Color Breathing Gradient behind text-heavy sections, and keep livelier ones like Pool Caustics, Rain on Glass or Aurora Borealis for heroes and splash screens.
- Check text contrast over the brightest frame, pause canvas effects when they leave the screen, and show a static fallback for
prefers-reduced-motion. - Mesh Gradient, Floating Particles, Pool Caustics and Noise Overlay are among the free effects on this page, with their complete code; the rest come with Premium.
Pool Caustics
The shifting light on a swimming-pool floor — an aqua vein network from dual thresholded feTurbulence (tent function + gamma), continuous SVG offset drift and pointer ripples (glow + expanding rings). Zero canvas, zero libs.
💡 Ideal for: pool residence heroes, spa & wellness landings, 5-star hotels, aquatic cosmetics.
Effect page →View source code
<div class="demo-preview" style="padding:0">
<svg xmlns="http://www.w3.org/2000/svg"
style="position:absolute;width:0;height:0;overflow:hidden;"
aria-hidden="true">
<defs>
<!--
Filtre caustiques : double turbulence seuillée
Couche 1 (pcaTurb1) : grandes ondes, 8 octaves, freq basse
Couche 2 (pcaTurb2) : fines mailles, 5 octaves, freq moyenne
feComponentTransfer "tente" : isole les crêtes à noise ≈ 0.5
feGamma exp=4/5 : étrangle les veines (ne restent que les pics)
feBlend screen : fusionne les deux couches (intersections = spots lumineux)
feGaussianBlur : adoucit légèrement les bords des veines
feColorMatrix : teinte aqua-blanc (eau + lumière froide)
JS anime feOffset dx/dy (sinusoïdal) → dérive naturelle sans saut de seed
-->
<filter id="pca-cf" x="-4%" y="-4%" width="108%" height="108%"
color-interpolation-filters="sRGB">
<feTurbulence id="pcaTurb1" type="fractalNoise"
baseFrequency="0.0044 0.0033" numOctaves="8" seed="3"
result="n1"/>
<feOffset id="pcaOff1" in="n1" dx="0" dy="0" result="no1"/>
<feComponentTransfer in="no1" result="t1">
<feFuncR type="table" tableValues="0 0.10 1 0.10 0"/>
<feFuncG type="table" tableValues="0 0.10 1 0.10 0"/>
<feFuncB type="table" tableValues="0 0.10 1 0.10 0"/>
</feComponentTransfer>
<feComponentTransfer in="t1" result="s1">
<feFuncR type="gamma" amplitude="1" exponent="4" offset="0"/>
<feFuncG type="gamma" amplitude="1" exponent="4" offset="0"/>
<feFuncB type="gamma" amplitude="1" exponent="4" offset="0"/>
</feComponentTransfer>
<feTurbulence id="pcaTurb2" type="fractalNoise"
baseFrequency="0.0074 0.0058" numOctaves="5" seed="19"
result="n2"/>
<feOffset id="pcaOff2" in="n2" dx="0" dy="0" result="no2"/>
<feComponentTransfer in="no2" result="t2">
<feFuncR type="table" tableValues="0 0.15 1 0.15 0"/>
<feFuncG type="table" tableValues="0 0.15 1 0.15 0"/>
<feFuncB type="table" tableValues="0 0.15 1 0.15 0"/>
</feComponentTransfer>
<feComponentTransfer in="t2" result="s2">
<feFuncR type="gamma" amplitude="1" exponent="5" offset="0"/>
<feFuncG type="gamma" amplitude="1" exponent="5" offset="0"/>
<feFuncB type="gamma" amplitude="1" exponent="5" offset="0"/>
</feComponentTransfer>
<!-- Fusion des deux couches (screen = sommation additive lumineuse) -->
<feBlend in="s1" in2="s2" mode="screen" result="merged"/>
<!-- Légère diffusion pour adoucir les bords de veines -->
<feGaussianBlur in="merged" stdDeviation="0.42" result="soft"/>
<!-- Teinte aqua-blanc : matrice réglée pour lumière de piscine -->
<feColorMatrix in="soft" type="matrix"
values="0.14 0.05 0.08 0 0.01
0.30 0.92 0.24 0 0.06
0.34 0.40 0.88 0 0.09
0 0 0 1 0"/>
</filter>
</defs>
</svg>
<div style="width:100%">
<div class="pca-scene" id="pcaScene"
role="img"
aria-label="Caustiques d'eau animées — déplacez la souris pour créer des remous">
<div class="pca-water" aria-hidden="true"></div>
<div class="pca-caustic" aria-hidden="true" id="pcaCaustic"></div>
<div class="pca-glow-layer" aria-hidden="true"></div>
<div class="pca-pointer-glow" aria-hidden="true" id="pcaGlow"></div>
</div>
</div>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 300px;
overflow: hidden;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.pca-scene {
position: relative;
width: 100%;
aspect-ratio: 16 / 9;
border-radius: 12px;
overflow: hidden;
contain: layout style paint;
cursor: crosshair;
-webkit-user-select: none;
user-select: none;
isolation: isolate;
}
.pca-water {
position: absolute;
inset: 0;
background:
radial-gradient(ellipse 80% 60% at 58% 32%, #0e5878 0%, transparent 62%),
radial-gradient(ellipse 55% 45% at 18% 72%, #0a4a3c 0%, transparent 55%),
radial-gradient(ellipse 40% 35% at 82% 75%, #0b3a58 0%, transparent 50%),
linear-gradient(175deg, #062a3e 0%, #031d2c 42%, #041f1a 100%);
transition: background 0.6s ease;
}
.pca-caustic {
position: absolute;
inset: 0;
filter: url(#pca-cf);
mix-blend-mode: screen;
opacity: var(--pca-op, 0.58);
will-change: filter;
background: #000;
}
.pca-glow-layer {
position: absolute;
inset: 0;
background:
radial-gradient(ellipse 50% 35% at 50% 50%, rgba(80, 200, 210, 0.08) 0%, transparent 70%);
mix-blend-mode: screen;
pointer-events: none;
}
.pca-pointer-glow {
position: absolute;
left: var(--pca-px, 50%);
top: var(--pca-py, 50%);
width: 260px;
height: 260px;
transform: translate(-50%, -50%);
border-radius: 50%;
background: radial-gradient(circle,
rgba(130, 220, 210, 0.38) 0%,
rgba(80, 190, 200, 0.12) 40%,
transparent 68%);
mix-blend-mode: screen;
pointer-events: none;
opacity: 0;
transition: opacity 0.45s ease;
will-change: transform, opacity;
}
.pca-pointer-glow.pca-active {
opacity: 1;
}
.pca-pointer-glow::before,
.pca-pointer-glow::after {
content: '';
position: absolute;
inset: 30%;
border-radius: 50%;
border: 1px solid rgba(130, 225, 215, 0.60);
animation: pca-ring 2.4s ease-out infinite;
}
.pca-pointer-glow::after {
border-color: rgba(100, 210, 230, 0.40);
animation-delay: -1.2s;
animation-duration: 2.4s;
}
@supports not (filter: url(#pca-cf)) {
.pca-caustic {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
.pca-pointer-glow {
display: none;
}
.pca-pointer-glow::before,
.pca-pointer-glow::after {
animation: none;
}
}
@media (prefers-reduced-transparency: reduce) {
.pca-caustic {
display: none;
}
.pca-glow-layer {
display: none;
}
.pca-water {
background:
radial-gradient(ellipse 70% 50% at 55% 38%, #0d4c68 0%, transparent 65%),
#042130;
}
}
.pca-scene.pca-solid .pca-caustic {
display: none;
}
.pca-scene.pca-solid .pca-glow-layer {
display: none;
}
.pca-scene.pca-solid .pca-pointer-glow {
display: none;
}
.pca-scene.pca-solid .pca-water {
background:
radial-gradient(ellipse 70% 50% at 55% 38%, #0d4c68 0%, transparent 65%),
#042130;
}
.pca-scene.pca-night .pca-water {
background:
radial-gradient(ellipse 60% 45% at 42% 44%, #081a38 0%, transparent 60%),
linear-gradient(180deg, #010c1c 0%, #02101a 100%);
}
.pca-scene.pca-night .pca-caustic {
filter: url(#pca-cf) hue-rotate(48deg) saturate(0.72);
opacity: calc(var(--pca-op, 0.58) * 0.7);
}
@keyframes pca-ring {
0% {
transform: scale(0.15);
opacity: 0.9;
}
100% {
transform: scale(4.5);
opacity: 0;
}
}(function() {
(function() {
/* fx-0826 — Pool Caustics (gratuit) */
function initPca() {
/* fx-0826 */
(function() {
'use strict';
/* ---------- Détection préférences ---------- */
var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var reduceTrans = window.matchMedia('(prefers-reduced-transparency: reduce)').matches;
/* ---------- Éléments SVG du filtre (communs aux deux scènes) ---------- */
var off1 = document.getElementById('pcaOff1');
var off2 = document.getElementById('pcaOff2');
var turb1 = document.getElementById('pcaTurb1');
var turb2 = document.getElementById('pcaTurb2');
/* ---------- Éléments scène principale ---------- */
var scene = document.getElementById('pcaScene');
var glow = document.getElementById('pcaGlow');
/* ---------- Éléments scène exemple d'usage ---------- */
var sceneEx = document.getElementById('pcaSceneEx');
var glowEx = document.getElementById('pcaGlowEx');
/* ---------- État pointeur (pour chaque scène) ---------- */
var LERP = 0.08;
var ptr = {
x: 0.5,
y: 0.5,
tx: 0.5,
ty: 0.5,
in: false
};
var ptrEx = {
x: 0.5,
y: 0.5,
tx: 0.5,
ty: 0.5,
in: false
};
/* ---------- Paramètres animation caustiques ---------- */
/* Fréquences de base des deux couches de turbulence */
var BF1X = 0.0044,
BF1Y = 0.0033;
var BF2X = 0.0074,
BF2Y = 0.0058;
/* Amplitude des oscillations de baseFrequency (12% env.) */
var DAMP = 0.001;
var DAMP2 = 0.0014;
/* Amplitude dérive feOffset (px). La turbulence se répète tous les ~120px,
donc ±35px offre une dérive riche sans revenir trop vite au départ. */
var AMP1X = 36,
AMP1Y = 28;
var AMP2X = 44,
AMP2Y = 34;
var speedMult = 1.0; /* contrôlé par le slider de review */
var phase = 0,
lastTs = 0; /* phase accumulée : la vitesse module l'AVANCE, jamais la position (aucun saut) */
function lerp(a, b, t) {
return a + (b - a) * t;
}
/* Met à jour les custom props de position du glow dans une scène */
function setGlowPos(glowEl, p) {
if (!glowEl) return;
var x = (p.x * 100).toFixed(1) + '%';
var y = (p.y * 100).toFixed(1) + '%';
glowEl.style.setProperty('--pca-px', x);
glowEl.style.setProperty('--pca-py', y);
if (p.in) {
glowEl.classList.add('pca-active');
} else {
glowEl.classList.remove('pca-active');
}
}
/* ---------- Boucle principale rAF ---------- */
function tick(ts) {
if (!lastTs) lastTs = ts;
var dt = Math.min((ts - lastTs) * 0.001, 0.05);
lastTs = ts;
phase += dt * speedMult;
/* Dérive sinusoïdale des offsets SVG (crée le "courant" des caustiques) */
if (!reduceMotion && !reduceTrans && off1 && off2) {
var dx1 = Math.sin(phase * 0.14) * AMP1X + Math.sin(phase * 0.09) * (AMP1X * 0.4);
var dy1 = Math.cos(phase * 0.11) * AMP1Y + Math.cos(phase * 0.07) * (AMP1Y * 0.35);
off1.setAttribute('dx', dx1.toFixed(1));
off1.setAttribute('dy', dy1.toFixed(1));
var dx2 = Math.sin(phase * 0.19 + 0.8) * AMP2X + Math.sin(phase * 0.13 + 0.4) * (AMP2X * 0.35);
var dy2 = Math.cos(phase * 0.15 + 1.2) * AMP2Y + Math.cos(phase * 0.10 + 0.9) * (AMP2Y * 0.30);
off2.setAttribute('dx', dx2.toFixed(1));
off2.setAttribute('dy', dy2.toFixed(1));
/* Légère variation de baseFrequency → texture change subtilement au fil du temps */
var f1x = (BF1X + Math.sin(phase * 0.06) * DAMP).toFixed(5) + ' ';
var f1y = (BF1Y + Math.cos(phase * 0.05) * (DAMP * 0.8)).toFixed(5);
turb1 && turb1.setAttribute('baseFrequency', f1x + f1y);
var f2x = (BF2X + Math.sin(phase * 0.08 + 0.5) * DAMP2).toFixed(5) + ' ';
var f2y = (BF2Y + Math.cos(phase * 0.07 + 1.1) * (DAMP2 * 0.75)).toFixed(5);
turb2 && turb2.setAttribute('baseFrequency', f2x + f2y);
}
/* Interpolation des positions pointeur */
if (!reduceMotion) {
ptr.x = lerp(ptr.x, ptr.tx, LERP);
ptr.y = lerp(ptr.y, ptr.ty, LERP);
ptrEx.x = lerp(ptrEx.x, ptrEx.tx, LERP);
ptrEx.y = lerp(ptrEx.y, ptrEx.ty, LERP);
}
if (!reduceTrans) {
setGlowPos(glow, ptr);
setGlowPos(glowEx, ptrEx);
}
requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
/* Normalise les coordonnées pointeur dans [0,1] relativement à une scène */
function normPos(e, el) {
var r = el.getBoundingClientRect();
return {
x: Math.max(0, Math.min(1, (e.clientX - r.left) / r.width)),
y: Math.max(0, Math.min(1, (e.clientY - r.top) / r.height))
};
}
/* Attache les événements pointeur à une scène */
function bindPointer(sceneEl, ptrState) {
if (!sceneEl || reduceMotion) return;
sceneEl.addEventListener('pointermove', function(e) {
var p = normPos(e, sceneEl);
ptrState.tx = p.x;
ptrState.ty = p.y;
/* Le pointeur ne touche PAS à la vitesse du champ (retour Anthony :
le boost global secouait tout et fatiguait l'œil). Le remous local
= glow + anneaux uniquement. */
ptrState.in = true;
});
sceneEl.addEventListener('pointerleave', function() {
ptrState.in = false;
});
sceneEl.addEventListener('touchmove', function(e) {
e.preventDefault();
}, {
passive: false
});
}
bindPointer(scene, ptr);
bindPointer(sceneEx, ptrEx);
/* ---- Contrôles de review uniquement (NE PAS copier) ---- */
var intSlider = document.getElementById('pcaInt');
var intV = document.getElementById('pcaIntV');
var speedSlider = document.getElementById('pcaSpeed');
var speedV = document.getElementById('pcaSpeedV');
var nightBtn = document.getElementById('pcaNight');
var a11yBtn = document.getElementById('pcaA11y');
function allScenes() {
var s = [scene, sceneEx].filter(Boolean);
return s;
}
if (intSlider) {
intSlider.addEventListener('input', function() {
intV.textContent = this.value;
var v = (+this.value / 100).toFixed(3);
allScenes().forEach(function(s) {
s.style.setProperty('--pca-op', v);
});
});
}
if (speedSlider) {
speedSlider.addEventListener('input', function() {
speedV.textContent = this.value;
speedMult = +this.value / 100;
});
}
if (nightBtn) {
nightBtn.addEventListener('click', function() {
var on = false;
allScenes().forEach(function(s) {
on = s.classList.toggle('pca-night');
});
this.setAttribute('aria-pressed', on ? 'true' : 'false');
});
}
if (a11yBtn) {
a11yBtn.addEventListener('click', function() {
var on = false;
allScenes().forEach(function(s) {
on = s.classList.toggle('pca-solid');
});
this.setAttribute('aria-pressed', on ? 'true' : 'false');
});
}
})();
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', initPca);
else initPca();
})();
})();Topographic Drift
An fBm noise field discretised into slowly drifting contour lines — the cursor raises a « hill » of concentric rings that follows it (rAF + lerp). 3 palettes (Slate · Teal · Sand), canvas 2D, zero libs.
💡 Ideal for: dev-tools SaaS heroes, analytics dashboards, infra/cloud landings, status pages.
Effect page →Rain on Glass
Rain on glass in 2D canvas: refracting drops (night-city bokeh flipped and zoomed inside each drop), stick-slip physics, coalescence, and evaporating micro-droplet trails. ≤ 80 drops, DPR handled, zero dependencies.
💡 Ideal for: real-estate or autumn editorial heroes, weather landings, night-time login pages.
Effect page →Ink Bloom
Watercolour blooms open with organic fractal edges (feTurbulence + dual feDisplacementMap) and layer into mixed pigments (multiply on cream paper, screen on dark). Edges keep breathing after the bloom; click blooms at the clicked point.
💡 Ideal for: gallery or portfolio heroes, creative landings, editorial covers, luxury sections.
Effect page →Storm Veil
Procedural 2D canvas storm: branching lightning via midpoint displacement (3-pass halo→body→core render), parallax clouds, optional fine rain, soft capped flash (a11y, never a strobe). Every strike is unique; click strikes where you click.
💡 Ideal for: dramatic launch heroes, game landing pages, high-stakes teasers.
Effect page →View source code
<div class="demo-preview bg-mesh">
<span class="bg-label">Mesh</span>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 300px;
overflow: hidden;
position: relative;
background: rgb(10, 10, 15);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.demo-preview.bg-mesh {
background:
radial-gradient(at 40% 20%, #6366f1 0px, transparent 50%),
radial-gradient(at 80% 0%, #d946ef 0px, transparent 50%),
radial-gradient(at 0% 50%, #06b6d4 0px, transparent 50%),
radial-gradient(at 80% 50%, #8b5cf6 0px, transparent 50%),
radial-gradient(at 0% 100%, #6366f1 0px, transparent 50%),
radial-gradient(at 80% 100%, #f43f5e 0px, transparent 50%);
background-color: #0a0a0f;
animation: meshMove 20s ease infinite;
}
.bg-label {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-weight: 700;
font-size: 1.5rem;
color: white;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
z-index: 10;
}
@keyframes meshMove {
0%,
100% {
background-position: 0% 0%, 100% 0%, 0% 100%, 100% 50%, 0% 100%, 100% 100%;
}
50% {
background-position: 100% 100%, 0% 100%, 100% 0%, 0% 50%, 100% 0%, 0% 0%;
}
}/* Pure CSS — no JavaScript required */View source code
<div class="bg-particles" id="particlesBg" style="width:100%;min-height:300px;">
<span class="bg-label">Particles</span>
</div>.bg-particles {
position: relative;
overflow: hidden;
background: rgb(10, 10, 15);
}
.particle {
position: absolute;
bottom: -20px;
width: 4px;
height: 4px;
background: rgb(99, 102, 241);
border-radius: 50%;
animation: 10s ease 0s infinite normal none running float;
}
@keyframes float {
0% {
transform: translateY(0px) rotate(0deg);
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
transform: translateY(-350px) rotate(720deg);
opacity: 0;
}
}
.bg-label {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-weight: 700;
font-size: 1.5rem;
color: white;
text-shadow: rgba(0, 0, 0, 0.5) 0px 2px 10px;
z-index: 10;
}// Generate particles
(function() {
var el = document.getElementById('particlesBg');
if (!el) return;
var colors = ['#6366f1', '#8b5cf6', '#d946ef', '#06b6d4'];
for (var i = 0; i < 30; i++) {
var particle = document.createElement('div');
particle.className = 'particle';
particle.style.left = Math.random() * 100 + '%';
particle.style.animationDelay = Math.random() * 10 + 's';
particle.style.animationDuration = (5 + Math.random() * 10) + 's';
particle.style.width = (2 + Math.random() * 4) + 'px';
particle.style.height = particle.style.width;
particle.style.opacity = 0.3 + Math.random() * 0.7;
particle.style.background = colors[Math.floor(Math.random() * colors.length)];
el.appendChild(particle);
}
})();Aurora Mesh
Aurora borealis mesh with green, cyan and purple gradients that blend and slowly ripple.
Effect page →View source code
<div class="demo-preview">
<div class="aurora-mesh">
<span class="aurora-mesh-label">AURORA</span>
</div>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 300px;
overflow: hidden;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.aurora-mesh {
width: 320px;
height: 220px;
border-radius: 16px;
position: relative;
overflow: hidden;
background: #0a0a0f;
}
.aurora-mesh::before {
content: '';
position: absolute;
inset: -20%;
background:
radial-gradient(ellipse at 25% 30%, rgba(0, 255, 128, 0.6) 0%, transparent 50%),
radial-gradient(ellipse at 70% 20%, rgba(0, 220, 255, 0.5) 0%, transparent 50%),
radial-gradient(ellipse at 50% 70%, rgba(160, 0, 255, 0.5) 0%, transparent 50%),
radial-gradient(ellipse at 15% 80%, rgba(0, 180, 200, 0.4) 0%, transparent 50%),
radial-gradient(ellipse at 85% 60%, rgba(0, 255, 180, 0.35) 0%, transparent 50%);
filter: blur(30px);
animation: auroraDrift 8s ease-in-out infinite alternate;
}
.aurora-mesh::after {
content: '';
position: absolute;
inset: -20%;
background:
radial-gradient(ellipse at 60% 40%, rgba(80, 255, 200, 0.4) 0%, transparent 45%),
radial-gradient(ellipse at 20% 60%, rgba(0, 200, 255, 0.35) 0%, transparent 45%),
radial-gradient(ellipse at 80% 80%, rgba(120, 0, 255, 0.4) 0%, transparent 45%);
filter: blur(25px);
animation: auroraDrift2 10s ease-in-out infinite alternate;
}
.aurora-mesh-label {
position: relative;
z-index: 1;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 800;
font-size: 1.3rem;
letter-spacing: 3px;
color: rgba(255, 255, 255, 0.9);
text-shadow: 0 0 20px rgba(0, 255, 180, 0.5);
}
@keyframes auroraDrift {
0% {
transform: translate(0, 0) scale(1) rotate(0deg);
}
33% {
transform: translate(5%, -8%) scale(1.05) rotate(2deg);
}
66% {
transform: translate(-5%, 5%) scale(0.95) rotate(-2deg);
}
100% {
transform: translate(8%, -3%) scale(1.02) rotate(1deg);
}
}
@keyframes auroraDrift2 {
0% {
transform: translate(0, 0) scale(1) rotate(0deg);
}
50% {
transform: translate(-8%, 6%) scale(1.08) rotate(-3deg);
}
100% {
transform: translate(6%, -6%) scale(0.96) rotate(2deg);
}
}/* Pure CSS — no JavaScript required */Neon Mesh
Vibrant neon mesh with pulsating spots of electric blue, hot pink and lime green.
Effect page →Pastel Mesh
Soft mesh gradient with lavender, mint and peach tones floating in harmony.
Effect page →Monochrome Mesh
Elegant monochrome mesh in shades of blue and indigo, subtle and deep.
Effect page →Gradient Morph
The mesh morphs between two completely different color palettes every few seconds.
Effect page →Noise Mesh
Mesh gradient overlaid with a subtle SVG noise texture for an organic, textured look.
Effect page →Glass Mesh Card
Card with colorful mesh background and glassmorphism overlay with blur and transparency.
Effect page →Animated Border Mesh
Animated border made of a multicolor mesh gradient rotating around the element.
Effect page →Color Breathing Gradient
Breathing gradient animating angle and hues via @property in OKLCH color space.
Effect page →View source code
<div class="demo-preview">
<div class="chroma-demo breathing-gradient">
<span class="label">Breathing Gradient</span>
</div>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 300px;
overflow: hidden;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
@property --hue-angle {
syntax: '<angle>';
initial-value: 0deg;
inherits: false;
}
.chroma-demo {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
border-radius: 8px;
overflow: hidden;
position: relative;
}
.breathing-gradient {
background: linear-gradient(var(--hue-angle),
oklch(0.65 0.25 0),
oklch(0.55 0.28 120),
oklch(0.7 0.22 240),
oklch(0.6 0.3 360));
background-size: 300% 300%;
animation: breathingHue 8s ease-in-out infinite, breathingAngle 12s linear infinite;
}
.breathing-gradient .label {
color: white;
font-weight: 700;
font-size: 1.1rem;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
letter-spacing: 0.05em;
z-index: 1;
}
@keyframes breathingHue {
0%,
100% {
background-position: 0% 50%;
}
25% {
background-position: 100% 0%;
}
50% {
background-position: 100% 100%;
}
75% {
background-position: 0% 100%;
}
}
@keyframes breathingAngle {
from {
--hue-angle: 0deg;
}
to {
--hue-angle: 360deg;
}
}/* Pure CSS — no JavaScript required */Duotone Image Filter
Transforms content to duotone on hover using mix-blend-mode and grayscale.
Effect page →Halftone Effect
SVG filter with feComponentTransfer and feMorphology for an animated halftone pattern on hover.
Effect page →Chromatic Aberration Text
RGB channel separation on hover creating a prismatic effect with pseudo-elements.
Effect page →Color Mix Crossfade
Two colors blended via color-mix() showing oklch vs srgb blending paths.
Effect page →Neon Glow Pulse
Elements with pulsing neon glow via multiple animated box-shadow layers.
Effect page →View source code
<div class="demo-preview">
<div class="chroma-demo neon-glow-container">
<div class="neon-element neon-pink">Pink</div>
<div class="neon-element neon-cyan">Cyan</div>
<div class="neon-element neon-green">Neon</div>
</div>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 300px;
overflow: hidden;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.chroma-demo {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
border-radius: 8px;
overflow: hidden;
position: relative;
}
.neon-glow-container {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
background: #0a0a0f;
flex-wrap: wrap;
padding: 24px;
}
.neon-element {
padding: 12px 24px;
border-radius: 12px;
font-weight: 700;
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.1em;
position: relative;
}
.neon-pink {
color: #ff2d95;
border: 2px solid #ff2d95;
text-shadow: 0 0 10px #ff2d95, 0 0 20px #ff2d95;
animation: neonPulsePink 2s ease-in-out infinite;
}
.neon-cyan {
color: #00f0ff;
border: 2px solid #00f0ff;
text-shadow: 0 0 10px #00f0ff, 0 0 20px #00f0ff;
animation: neonPulseCyan 2.5s ease-in-out infinite;
}
.neon-green {
color: #39ff14;
border: 2px solid #39ff14;
text-shadow: 0 0 10px #39ff14, 0 0 20px #39ff14;
animation: neonPulseGreen 3s ease-in-out infinite;
}
@keyframes neonPulsePink {
0%,
100% {
box-shadow: 0 0 5px #ff2d95, 0 0 10px #ff2d95, 0 0 20px #ff2d95, inset 0 0 10px rgba(255, 45, 149, 0.1);
}
50% {
box-shadow: 0 0 10px #ff2d95, 0 0 25px #ff2d95, 0 0 50px #ff2d95, 0 0 80px #ff2d95, inset 0 0 20px rgba(255, 45, 149, 0.15);
}
}
@keyframes neonPulseCyan {
0%,
100% {
box-shadow: 0 0 5px #00f0ff, 0 0 10px #00f0ff, 0 0 20px #00f0ff, inset 0 0 10px rgba(0, 240, 255, 0.1);
}
50% {
box-shadow: 0 0 10px #00f0ff, 0 0 25px #00f0ff, 0 0 50px #00f0ff, 0 0 80px #00f0ff, inset 0 0 20px rgba(0, 240, 255, 0.15);
}
}
@keyframes neonPulseGreen {
0%,
100% {
box-shadow: 0 0 5px #39ff14, 0 0 10px #39ff14, 0 0 20px #39ff14, inset 0 0 10px rgba(57, 255, 20, 0.1);
}
50% {
box-shadow: 0 0 10px #39ff14, 0 0 25px #39ff14, 0 0 50px #39ff14, 0 0 80px #39ff14, inset 0 0 20px rgba(57, 255, 20, 0.15);
}
}/* Pure CSS — no JavaScript required */Gradient Spotlight
Radial gradient follows the cursor revealing vibrant colors under a muted surface.
Effect page →Color Space Morph
Gradient transitioning between sRGB vs OKLCH color spaces showing midpoint differences.
Effect page →Relative Color Palette
Entire palette derived from a single base color via OKLCH and computed custom properties.
Effect page →