Noise Morph
Rotating orb dressed with an animated SVG grain and pulsing glow — three pure CSS layers stacked, zero JavaScript.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Backgrounds has 50 effects, including 6 free. Explore the category →
3 usage examples



How it works
The central shape is a circular <div> filled with a linear gradient (linear-gradient(135deg, #06b6d4, #8b5cf6)) that rotates continuously via @keyframes ngMorphRotate (12 s, linear, infinite). Because the element rotates — not the background — the perceived colour evolves with the angle, simulating a shifting reflection without recalculation.
The glow is rendered by the ::before pseudo-element: a second gradient (magenta → orange) extending 10 px beyond the edge (inset: -10px), blurred to 20 px with filter: blur(), alternating between opacity 0.3 and 0.6 and between scale(0.9) and scale(1.1) via @keyframes ngMorphPulse (4 s, ease-in-out, alternate). The alternation creates a continuous light "breathing" effect.
The grain texture is rendered by the ::after pseudo-element: an inline data: URI SVG containing a <feTurbulence type="turbulence" baseFrequency="0.05" numOctaves="3"> filter applied to a full rectangle. This SVG is composited with mix-blend-mode: overlay at 30% opacity, then jittered by @keyframes ngMorphNoise (±2 px translations, 3 s, steps(5)). The stepped timing produces irregular, non-interpolated filmic grain.
The effect is 100% CSS, zero JavaScript. No initialisation required: place .ng-morph-scene > .ng-morph-shape in the DOM and all three animations start automatically. Can be instantiated as many times as needed — each element is independent.
Accessibility
- prefers-reduced-motion: not natively handled — the CSS contains no
@media (prefers-reduced-motion: reduce)block. All three animations run unconditionally. To fix this, add:@media (prefers-reduced-motion: reduce) { .ng-morph-shape, .ng-morph-shape::before, .ng-morph-shape::after { animation-play-state: paused } }. - The scene
<div>carries noroleoraria-labelby default. Addaria-hidden="true"if the orb is purely decorative, orrole="img" aria-label="…"if it carries meaning. - No interactive elements in the effect — no native focus, no
tabindexto manage. - The effect contains no text of its own — readability of overlaid content is entirely the responsibility of the surrounding markup.
- The grain SVG is rendered via an inline
data: URI— no external resource, no risk of network blocking or timeout.
Browser compatibility
Requires CSS animations, ::before/::after pseudo-elements, mix-blend-mode: overlay, and inline SVG feTurbulence — supported in all modern browsers.
Without <code>mix-blend-mode</code> support, the grain renders in normal blend mode (no compositing) — the orb stays visible. Without CSS animations, the shape remains a static circle with the gradient — no JS errors.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="ng-morph-scene" aria-hidden="true">
<div class="ng-morph-shape"></div>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| .ng-morph-shape { width / height } | 180px | Orb size. Set both to the same value to keep it circular — ::before (inset: -10px) and ::after (inset: 0) adjust automatically. |
| .ng-morph-shape { background } | linear-gradient(135deg, #06b6d4, #8b5cf6) | Body gradient. Swap both colours for your brand palette — the gradient sweeps with the rotation. |
| .ng-morph-shape::before { background } | linear-gradient(135deg, #ec4899, #f97316) | Glow colours. Warm tones for a warm effect, purple/blue for a tech look. |
| .ng-morph-shape { animation-duration } | 12s | Rotation speed. Reduce to 6–8 s for a dynamic feel (loader, processing), increase to 20–30 s for a slow ambient presence. |
| .ng-morph-shape::before { filter: blur() } | blur(20px) | Glow radius. Increase (30–40 px) for a diffuse halo, decrease (8–12 px) for a crisper rim. |
| .ng-morph-shape::after { opacity } | 0.3 | Grain intensity. Raise to 0.5–0.7 for pronounced texture, lower to 0.1 for barely-there grain. |
| baseFrequency='0.05' in SVG feTurbulence | 0.05 | Spatial noise frequency. Low value (0.02–0.04) = coarse organic grain, high value (0.08–0.15) = fine and tight grain. |
| .ng-morph-shape::before { animation-duration } | 4s | Glow pulse speed. 2 s = fast breathing, 8 s = slow organic heartbeat. |
FAQ
.ng-morph-scene > .ng-morph-shape), the CSS, and the @keyframes: all three animations start as soon as the DOM is ready. No import, no npm install, compatible with all frameworks.width and height on .ng-morph-shape to the same value (circle) or different values (ellipse). The ::before pseudo-element uses inset: -10px — it overflows by 10 px on all sides regardless of size. The ::after pseudo-element uses inset: 0 — it covers the shape exactly.prefers-reduced-motion natively. Add this block to your CSS: @media (prefers-reduced-motion: reduce) { .ng-morph-shape, .ng-morph-shape::before, .ng-morph-shape::after { animation-play-state: paused; } }. This pauses all three @keyframes in one rule, with no modification to the effect code.