Twisting puzzle cube
Pure CSSA 3×3×3 cube built as three layers that take turns twisting a quarter turn. Each layer is one flat box with the stickers painted on by gradients.
JS places the dots on a Fibonacci sphere once; the spin itself is a plain CSS animation.
Your edited version
rotateY(longitude) rotateX(latitude) translateZ(radius): aim, then push outward. Same idea as the carousel, in two axes.--u, tied to the canvas. JS writes translateZ(var(--r)) rather than a pixel radius, so the radius (130 units) stays in CSS and the ball is the same share of a gallery card, the editor and a recording canvas.DOM generated from a formularotateY · rotateX · translateZ per dotCSS keyframe spin<div class="scene">
<div class="ball"></div>
</div>
.scene {
/* one base unit: every length below is a multiple of it, so the ball is the same share of a
card, the editor, a full screen and a recording canvas */
--u: 0.245vmin;
perspective: calc(600 * var(--u));
}
.ball {
--r: calc(130 * var(--u));
position: relative;
width: 0;
height: 0;
transform-style: preserve-3d;
animation: ball-spin 16s linear infinite;
}
.ball i {
position: absolute;
top: calc(-4 * var(--u));
left: calc(-4 * var(--u));
width: calc(8 * var(--u));
height: calc(8 * var(--u));
border-radius: 50%;
background: radial-gradient(circle, #fff 15%, #ffb547 60%);
}
@keyframes ball-spin {
from { transform: rotateX(-18deg) rotateY(0deg); }
to { transform: rotateX(-18deg) rotateY(360deg); }
}
const ball = document.querySelector('.ball');
const COUNT = 120;
const GOLDEN_ANGLE = Math.PI * (3 - Math.sqrt(5));
for (let i = 0; i < COUNT; i++) {
const lat = Math.asin(1 - (i / (COUNT - 1)) * 2); // -90° … 90°
const lon = i * GOLDEN_ANGLE;
const dot = document.createElement('i');
dot.style.transform =
'rotateY(' + lon + 'rad) rotateX(' + lat + 'rad) translateZ(var(--r))';
ball.append(dot);
}