Effects / CSS
Clay 3D
Soft, matte, pastel shapes that look like modelling clay, gently bobbing.
Prompt to build it
Build a clay-style 3D scene of pastel spheres and a capsule. Fake soft matte shading with offset radial gradients and inset shadows, soft ground shadows, and slow staggered bobbing. Make it responsive, cap devicePixelRatio at 2, pause when off-screen, and show a still frame when prefers-reduced-motion is set. No external libraries.
Tested code
One self-contained HTML file with no libraries: the same code that runs above. Your AI gets it from Flow's get_effect tool.
Show the code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Clay 3D | Flow effect</title>
<!--
Flow effect: Clay 3D (https://flow.donta.io/effects/clay-3d)
Self-contained: no libraries. Copy the .fx-stage element, the CSS and the
script into your project and size .fx-stage however you like.
-->
<style>
:root { --display: "Bricolage Grotesque", ui-sans-serif, system-ui, sans-serif; --body: ui-sans-serif, system-ui, sans-serif; }
html, body { margin: 0; min-height: 100%; background: #0e0e10; display: grid; place-items: center; }
.fx-stage { width: min(100vw, 960px); }
/* Stage that every effect fills. Container units (cqw/cqmin) size the art. */
.fx-stage {
position: relative;
aspect-ratio: 4 / 3;
overflow: hidden;
border-radius: 16px;
container-type: size;
background: #111;
isolation: isolate;
}
.fx-fill {
position: absolute;
inset: 0;
}
.fx-fallback {
position: absolute;
inset: 0;
}
.fx-center {
display: grid;
place-items: center;
}
/* ── Clay 3D ──────────────────────────────────── */
.fx-clay { background: linear-gradient(#f8cfdc, #f3b9cc); }
.fx-clay i {
position: absolute;
border-radius: 50%;
animation: fx-bob 3.2s ease-in-out infinite alternate;
}
.fx-clay i::after {
content: "";
position: absolute;
left: 10%;
right: 10%;
bottom: -22%;
height: 14%;
border-radius: 50%;
background: rgba(150, 60, 90, 0.25);
filter: blur(1.4cqmin);
}
.fx-clay .c1 { width: 30cqmin; height: 30cqmin; left: 12%; top: 36%; background: radial-gradient(circle at 32% 30%, #ffc0cf, #f2799a 55%, #c94d72); }
.fx-clay .c2 { width: 25cqmin; height: 25cqmin; left: 40%; top: 26%; background: radial-gradient(circle at 32% 30%, #b8e6ff, #57b5f2 55%, #2f7fc0); animation-delay: -1.2s; }
.fx-clay .c3 { width: 16cqmin; height: 38cqmin; left: 66%; top: 28%; border-radius: 99px; background: radial-gradient(circle at 32% 25%, #fff2a8, #f5cf4a 55%, #c99a1a); animation-delay: -2.1s; }
.fx-clay .c4 { width: 11cqmin; height: 11cqmin; left: 57%; top: 14%; background: radial-gradient(circle at 32% 30%, #c9ffe0, #6fd8a1 55%, #3a9e6b); animation-delay: -0.6s; }
@keyframes fx-bob { to { translate: 0 -3cqmin; } }
@media (prefers-reduced-motion: reduce) {
.fx-stage *, .fx-stage *::before, .fx-stage *::after { animation-play-state: paused !important; }
}
</style>
</head>
<body>
<div class="fx-stage">
<div class="fx-fill fx-clay"><i class="c1"></i><i class="c2"></i><i class="c3"></i><i class="c4"></i></div>
</div>
</body>
</html>
How it works
Radial gradients with an offset highlight and a darker rim fake soft 3D shading; blurred shadows underneath and slow bobbing animation sell the weight.
Guardrails
- Keep contrast up on any labels; pastel on pastel fails easily.
- Respect prefers-reduced-motion: show a still frame instead of the animation.
- Pause when off-screen or when the tab is hidden (IntersectionObserver + visibilitychange).
- Keep text off busy animated areas, or give it a solid backing that passes WCAG contrast.
- One showpiece effect per page. If everything moves, nothing stands out.