Strictly speaking, any “3D” shape on a flat screen is more an illusion of a 3D object. All we see is a 2D projection of that shape on the screen plane, and our brain does its best to guess which shape could give the projection we see. If the projection changes, our brain interprets it as a 3D object changing its orientation, which helps it to determine the shape of this object better.
It works well with non-symmetric objects and objects made from polygons (e.g., cubes), but the sphere is a very special case: its projection on the plane always gives just a circle. The static sphere and the rotating one have the same projection, the same circle. Even in real life, if we look at a sphere with a uniform surface without any marks on it (e.g., a polished metal ball), it is hard to determine if it stands still or rotates. Our eyes need some hints, some details that are moving along the surface of the sphere according to its geometry. The more such details move the way you expect from points on the spherical surface to move, the clearer is the perception (well, illusion) of the rotating sphere.
And here is the key to the problem in making a CSS scene that would give such perception: to make this illusion strong enough, we need many marks moving along the paths that lie in different planes. And the only way to get this in CSS is to have each mark as a separate CSS box (element or pseudo-element). If our sphere consists only of moving marks, we really need many of them to see it as a sphere — thus “hundreds of elements” in most demos you have seen.
So if you want to make a sphere look realistic with a reasonably small number of elements, you would probably need to combine the effects that make an “illusion” of a static basic spherical shape (a circle with radial gradients, inner shadows etc.) with some elements that are relatively small (to make it less obvious that they are actually flat), oriented along the surface of the sphere with 3D transforms, and animated — basically the same way as the faces of the cube in your first demo.
Below is my own attempt to put this approach into practice. I used 20 circular elements oriented roughly as the faces of the regular icosahedron (like white hexagons on the classic soccer ball). I grouped them into two groups each making one hemisphere for convenience (it was not necessary, but it made styling a bit simpler). The whole 3D scene consists of the sphere itself and the background frame (pseudo element) which crosses the sphere near its centre (a bit closer, to reduce “flickering” of the circles as they go from the near side to the far side and back) and always faces to the screen. So 24 elements in total (not literally “hundreds”, at least:). To make the circles look more “bulging” (like spherical segments), I added two pseudo elements to each of them and elevated them slightly one above other. Works best in Chrome and Firefox 57+ (in Firefox 56- and iOS Safari there is still some “flickering” near the edges). If you hover the circle, you can see the scene without the background frame (and also without “flickering”). A slightly modified version is also available on Codepen.
.scene {
perspective: 400vmin;
transform-style: preserve-3d;
position: absolute;
width: 80vmin;
height: 80vmin;
top: 10vmin;
left: 10vmin;
}
.sphere {
transform-style: preserve-3d;
position: absolute;
animation: rotate 20s infinite linear;
width: 100%;
height: 100%;
transform-origin: 50% 50%;
top: 0;
left: 0;
}
.scene::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
top: 0%;
left: 0%;
background: radial-gradient(circle farthest-corner at 33% 33%, rgba(240, 240, 220, 0.85) 0%, rgba(30, 30, 40, 0.85) 80%), radial-gradient(circle farthest-corner at 45% 45%, rgba(0, 0, 0, 0) 50%, #000000 80%);
border-radius: 50%;
transform: translateZ(2vmin);
}
.scene:hover::before {
display: none;
}
.hemisphere {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform-style: preserve-3d;
transform-origin: 50% 50%;
transform: rotateX(90deg);
}
.hemisphere:nth-child(2) {
transform: rotateX(-90deg);
}
.face {
position: absolute;
width: 40vmin;
height: 40vmin;
background: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1) 48%, #ff0000 49%, #ff0000 50%, rgba(0, 0, 0, 0) 51%);
transform-style: preserve-3d;
transform-origin: 50% 0;
top: 50%;
left: 20vmin;
}
.face::before, .face::after {
content: '';
position: absolute;
border-radius: 50%;
box-sizing: border-box;
}
.face::before {
width: 50%;
height: 50%;
top: 25%;
left: 25%;
border: 2px solid #333;
background: rgba(255, 255, 255, 0.3);
transform: translateZ(1.6vmin);
}
.face::after {
width: 20%;
height: 20%;
top: 40%;
left: 40%;
background: rgba(0, 0, 0, 0.2);
transform: translateZ(2.8vmin);
}
.face:nth-child(1) {
transform: translateZ(-41.6vmin) rotateZ(36deg) translateY(-6.8vmin) rotateX(143deg);
}
.face:nth-child(2) {
transform: translateZ(-41.6vmin) rotateZ(108deg) translateY(-6.8vmin) rotateX(143deg);
}
.face:nth-child(3) {
transform: translateZ(-41.6vmin) rotateZ(180deg) translateY(-6.8vmin) rotateX(143deg);
}
.face:nth-child(4) {
transform: translateZ(-41.6vmin) rotateZ(252deg) translateY(-6.8vmin) rotateX(143deg);
}
.face:nth-child(5) {
transform: translateZ(-41.6vmin) rotateZ(-36deg) translateY(-6.8vmin) rotateX(143deg);
}
.face:nth-child(6) {
transform: translateZ(-26.8vmin) rotateZ(36deg) translateY(-33.2vmin) rotateX(100deg);
}
.face:nth-child(7) {
transform: translateZ(-26.8vmin) rotateZ(108deg) translateY(-33.2vmin) rotateX(100deg);
}
.face:nth-child(8) {
transform: translateZ(-26.8vmin) rotateZ(180deg) translateY(-33.2vmin) rotateX(100deg);
}
.face:nth-child(9) {
transform: translateZ(-26.8vmin) rotateZ(252deg) translateY(-33.2vmin) rotateX(100deg);
}
.face:nth-child(10) {
transform: translateZ(-26.8vmin) rotateZ(-36deg) translateY(-33.2vmin) rotateX(100deg);
}
.face:nth-child(11) {
transform: translateZ(-26.8vmin) rotateZ(36deg) translateY(-33.2vmin) rotateX(100deg);
}
@keyframes rotate {
0% {
transform: rotateZ(25deg) rotateX(20deg) rotateY(0deg);
}
50% {
transform: rotateZ(-25deg) rotateX(-20deg) rotateY(180deg);
}
100% {
transform: rotateZ(25deg) rotateX(20deg) rotateY(360deg);
}
}
body {
background: #555;
overflow: hidden;
}
<div class="scene">
<div class="sphere">
<div class="hemisphere">
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
</div>
<div class="hemisphere">
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
<div class="face"></div>
</div>
</div>
</div>