3D equalizer
Pure CSSSeven real cuboids bouncing like an audio meter. Walls are squashed with scaleY and each lid rides down by the same amount, so nothing but transforms ever changes.
Three rings of paired arcs, each tumbling around a different axis while its arcs chase each other around the ring, circling a breathing core.
Your edited version
border-top-color and border-bottom-color — two arcs on opposite sides, no SVG needed.rotateZ(tilt) picks the axis it tumbles around, rotateX does the tumble, and a trailing rotateZ(spin) chases the arcs around the ring as it tumbles — order matters, read it right to left.@keyframes block serves all three rings: var(--tilt) and var(--spin) inside the keyframe let each ring plug in its own numbers.--spin is always a whole number of turns (720deg, -720deg, 1080deg) so the ring's rotation ends exactly where it started — that is what makes the loop seamless.radial-gradient, no blur or filter, which keeps the whole loader cheap to animate.transparent border + two coloured sides = arcsrotateZ(tilt) rotateX(tumble) rotateZ(spin)var() inside @keyframeswhole turns only = seamless<div class="scene">
<div class="rings" role="img" aria-label="Loading">
<b></b>
<i></i>
<i></i>
<i></i>
</div>
</div>
.scene {
perspective: 800px;
}
.rings {
display: grid;
place-items: center;
width: 140px;
height: 140px;
transform-style: preserve-3d;
}
/* glowing core: a plain radial gradient (no blur, no filter), gently breathing */
.rings b {
grid-area: 1 / 1;
width: 70px;
height: 70px;
border-radius: 50%;
background: radial-gradient(
circle,
#fff 0 9%,
#2ee6d6 20%,
color-mix(in srgb, #2ee6d6 35%, transparent) 38%,
transparent 68%
);
animation: rings-core 1.4s ease-in-out infinite alternate;
}
/* a ring is a circle whose border is transparent except top and bottom: two arcs on opposite
sides, tapering at the ends, that chase each other as the ring spins in its own plane */
.rings i {
position: relative;
grid-area: 1 / 1;
width: var(--d);
height: var(--d);
border: 4px solid transparent;
border-top-color: var(--c);
border-bottom-color: var(--c);
border-radius: 50%;
animation: rings-spin var(--t) linear var(--delay) infinite;
}
/* faint full track so the ring still reads when the arcs are edge-on */
.rings i::before {
content: '';
position: absolute;
inset: -3px;
border: 1px solid color-mix(in srgb, var(--c) 28%, transparent);
border-radius: 50%;
}
/* --tilt picks the axis the ring tumbles around (0 = X, 90 = Y, 45 = the diagonal),
--spin is how far the arcs travel around the ring per tumble (whole turns only → seamless) */
.rings i:nth-of-type(1) { --d: 132px; --c: #8b6cff; --tilt: 0deg; --spin: 720deg; --t: 3.6s; --delay: -0.5s; }
.rings i:nth-of-type(2) { --d: 100px; --c: #ff4d9d; --tilt: 90deg; --spin: -720deg; --t: 2.8s; --delay: -1.3s; }
.rings i:nth-of-type(3) { --d: 68px; --c: #ffb547; --tilt: 45deg; --spin: 1080deg; --t: 2.2s; --delay: -0.2s; }
/* read right to left: spin the arcs in the ring's plane, tumble the plane around X,
then turn that X axis to wherever --tilt says */
@keyframes rings-spin {
from {
transform: rotateZ(var(--tilt)) rotateX(0deg) rotateZ(0deg);
}
to {
transform: rotateZ(var(--tilt)) rotateX(360deg) rotateZ(var(--spin));
}
}
@keyframes rings-core {
from {
opacity: 0.7;
transform: scale(0.85);
}
to {
opacity: 1;
transform: scale(1.1);
}
}
.d-rings {
display: grid;
place-items: center;
width: 140px;
height: 140px;
transform-style: preserve-3d;
// glowing core: a plain radial gradient (no blur, no filter), gently breathing
b {
grid-area: 1 / 1;
width: 70px;
height: 70px;
border-radius: 50%;
background: radial-gradient(
circle,
#fff 0 9%,
var(--accent-2) 20%,
color-mix(in srgb, var(--accent-2) 35%, transparent) 38%,
transparent 68%
);
animation: d-rings-core 1.4s ease-in-out infinite alternate;
}
// A ring is a circle whose border is transparent except top and bottom: two arcs on opposite
// sides, tapering at the ends, that chase each other as the ring spins in its own plane.
i {
position: relative;
grid-area: 1 / 1;
width: var(--d);
height: var(--d);
border: 4px solid transparent;
border-top-color: var(--c);
border-bottom-color: var(--c);
border-radius: 50%;
animation: d-rings-spin var(--t) linear var(--delay) infinite;
// faint full track so the ring still reads when the arcs are edge-on
&::before {
content: '';
position: absolute;
inset: -3px;
border: 1px solid color-mix(in srgb, var(--c) 28%, transparent);
border-radius: 50%;
}
// --tilt picks the axis the ring tumbles around (0 = X, 90 = Y, 45 = the diagonal),
// --spin is how far the arcs travel around the ring per tumble (whole turns only → seamless).
&:nth-of-type(1) {
--d: 132px;
--c: var(--accent);
--tilt: 0deg;
--spin: 720deg;
--t: 3.6s;
--delay: -0.5s;
}
&:nth-of-type(2) {
--d: 100px;
--c: var(--hot);
--tilt: 90deg;
--spin: -720deg;
--t: 2.8s;
--delay: -1.3s;
}
&:nth-of-type(3) {
--d: 68px;
--c: var(--warm);
--tilt: 45deg;
--spin: 1080deg;
--t: 2.2s;
--delay: -0.2s;
}
}
}
// Read right to left: spin the arcs in the ring's plane, tumble the plane around X,
// then turn that X axis to wherever --tilt says.
@keyframes d-rings-spin {
from {
transform: rotateZ(var(--tilt)) rotateX(0deg) rotateZ(0deg);
}
to {
transform: rotateZ(var(--tilt)) rotateX(360deg) rotateZ(var(--spin));
}
}
@keyframes d-rings-core {
from {
opacity: 0.7;
transform: scale(0.85);
}
to {
opacity: 1;
transform: scale(1.1);
}
}