Large, blurred, organic colored shapes drift behind the content. Adds depth and color without being a literal image.
Embedded technical partnership for small teams.
.section { position: relative; overflow: hidden; isolation: isolate; }
.blob {
position: absolute;
border-radius: 50%;
filter: blur(40px);
opacity: 0.6;
z-index: -1;
}
.blob.b1 { background: var(--accent); top: -40px; left: -30px;
width: 200px; height: 200px;
animation: drift1 18s ease-in-out infinite alternate; }
.blob.b2 { background: #7c5dfa; bottom: -60px; right: -40px;
width: 240px; height: 240px;
animation: drift2 22s ease-in-out infinite alternate; }
@keyframes drift1 { to { transform: translate(40px, 30px); } }
@keyframes drift2 { to { transform: translate(-30px, -40px); } }
Browser blur is a per-pixel operation. A 200x200 blurred element costs ~40,000 operations per frame. At 60fps, that's 2.4M operations/second per blob. For a hero with 3 blobs, you're at 7M+ ops/sec. Cheap on Apple Silicon — taxing on a 4-year-old Android.
Mitigations:
will-change: transform on the blob to promote it to its own GPU layer.transform for animation (GPU-accelerated), not top/left (CPU layout).@media (prefers-reduced-motion: reduce) to drop the animations.Blur amount: 30–60px. Less = sharper, more "circle." More = more "atmosphere."
Opacity: 0.3–0.7 depending on how loud you want it.
Drift speed: 18–24s. Should feel like ambient breathing, not movement.
Source: Jono Catliff masterclass, 2026-05-19. See masterclass notes.