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.
Rungs spin on negative delays; dots counter-rotate so they always face the camera.
Your edited version
rotateY animation.animation-delay per rung starts each one part-way through its turn — that offset is the twist.reverse. The two rotations cancel and the dot always faces the camera (a "billboard").preserve-3d for that cancellation to happen in 3D space.--u, tied to the canvas: a rung is 120 units wide and the twelve of them stand 12 apart, so the helix is the same share of a gallery card, the editor and a recording canvas.negative animation-delaybillboard counter-rotationdelay from --i per rung<div class="scene">
<div class="helix">
<div style="--i:1"><i></i><i></i></div>
<div style="--i:2"><i></i><i></i></div>
<div style="--i:3"><i></i><i></i></div>
<div style="--i:4"><i></i><i></i></div>
<div style="--i:5"><i></i><i></i></div>
<div style="--i:6"><i></i><i></i></div>
<div style="--i:7"><i></i><i></i></div>
<div style="--i:8"><i></i><i></i></div>
<div style="--i:9"><i></i><i></i></div>
<div style="--i:10"><i></i><i></i></div>
<div style="--i:11"><i></i><i></i></div>
<div style="--i:12"><i></i><i></i></div>
</div>
</div>
.scene {
/* one base unit: every length below is a multiple of it, so the helix is the same share of a
gallery card, the editor, a full screen and a recording canvas */
--u: 0.29vmin;
perspective: calc(800 * var(--u));
}
.helix {
display: grid;
gap: calc(12 * var(--u));
transform-style: preserve-3d;
transform: rotateZ(-14deg);
}
.helix > div {
--delay: calc(var(--i) * -0.22s);
position: relative;
width: calc(120 * var(--u));
height: calc(3 * var(--u));
background: linear-gradient(90deg, #2ee6d6, transparent 35% 65%, #ff4d9d);
transform-style: preserve-3d;
animation: helix-spin 4s linear infinite;
animation-delay: var(--delay);
}
.helix i {
position: absolute;
top: calc(-6 * var(--u));
width: calc(15 * var(--u));
height: calc(15 * var(--u));
border-radius: 50%;
/* same animation, reversed → the dot always faces the camera */
animation: helix-spin 4s linear infinite reverse;
animation-delay: var(--delay);
}
.helix i:first-child { left: calc(-7 * var(--u)); background: #2ee6d6; box-shadow: 0 0 calc(10 * var(--u)) #2ee6d6; }
.helix i:last-child { right: calc(-7 * var(--u)); background: #ff4d9d; box-shadow: 0 0 calc(10 * var(--u)) #ff4d9d; }
@keyframes helix-spin {
to { transform: rotateY(360deg); }
}