tl;dr: I would like to create an actual 3d sphere with CSS - not just an illusion
Note: some of the snippet examples are not responsive. Please use full screen.
With pure CSS you can create and animate a 3d cube like so:
#cube-wrapper {
position: absolute;
left: 50%;
top: 50%;
perspective: 1500px;
}
.cube {
position: relative;
transform-style: preserve-3d;
animation-name: rotate;
animation-duration: 30s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
@keyframes rotate {
0% {
transform: rotate3d(0, 0, 0, 0);
}
100% {
transform: rotate3d(0, 1, 0, 360deg);
;
}
}
.face {
position: absolute;
width: 200px;
height: 200px;
border: solid green 3px;
}
#front_face {
transform: translateX(-100px) translateY(-100px) translateZ(100px);
background: rgba(255, 0, 0, 0.5);
}
#back_face {
transform: translateX(-100px) translateY(-100px) translateZ(-100px);
background: rgba(255, 0, 255, 0.5);
}
#right_face {
transform: translateY(-100px) rotateY(90deg);
background: rgba(255, 255, 0, 0.5);
}
#left_face {
transform: translateY(-100px) translateX(-200px) rotateY(90deg);
background: rgba(0, 255, 0, 0.5);
}
#top_face {
transform: translateX(-100px) translateY(-200px) rotateX(90deg);
background: rgba(0, 255, 255, 0.5);
}
#bottom_face {
transform: translateX(-100px) rotateX(90deg);
background: rgba(255, 255, 255, 0.5);
}
.cube {
transform: rotateX(90deg) rotateY(90deg);
}
<div id="cube-wrapper">
<div class="cube">
<div id="front_face" class="face"></div>
<div id="right_face" class="face"></div>
<div id="back_face" class="face"></div>
<div id="left_face" class="face"></div>
<div id="top_face" class="face"></div>
<div id="bottom_face" class="face"></div>
</div>
</div>
I want to create and animate a 3d sphere in the same manner.
So... the first idea I get is to use border-radius
and...well... it doesn't work.
#cube-wrapper {
position: absolute;
left: 50%;
top: 50%;
perspective: 1500px;
}
.cube {
position: relative;
transform-style: preserve-3d;
animation-name: rotate;
animation-duration: 30s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
@keyframes rotate {
0% {
transform: rotate3d(0, 0, 0, 0);
}
100% {
transform: rotate3d(0, 1, 0, 360deg);
;
}
}
.face {
position: absolute;
width: 200px;
height: 200px;
border: solid green 3px;
border-radius: 100vw
}
#front_face {
transform: translateX(-100px) translateY(-100px) translateZ(100px);
background: rgba(255, 0, 0, 0.5);
}
#back_face {
transform: translateX(-100px) translateY(-100px) translateZ(-100px);
background: rgba(255, 0, 255, 0.5);
}
#right_face {
transform: translateY(-100px) rotateY(90deg);
background: rgba(255, 255, 0, 0.5);
}
#left_face {
transform: translateY(-100px) translateX(-200px) rotateY(90deg);
background: rgba(0, 255, 0, 0.5);
}
#top_face {
transform: translateX(-100px) translateY(-200px) rotateX(90deg);
background: rgba(0, 255, 255, 0.5);
}
#bottom_face {
transform: translateX(-100px) rotateX(90deg);
background: rgba(255, 255, 255, 0.5);
}
.cube {
transform: rotateX(90deg) rotateY(90deg);
}
<div id="cube-wrapper">
<div class="cube">
<div id="front_face" class="face"></div>
<div id="right_face" class="face"></div>
<div id="back_face" class="face"></div>
<div id="left_face" class="face"></div>
<div id="top_face" class="face"></div>
<div id="bottom_face" class="face"></div>
</div>
</div>
So, I reconsidered my approach and looked for a different method.
I looked at:
- Implementing "Pure CSS Sphere" into website - how do I do it?
- How to create a sphere in css?
- Display an image wrapped around a sphere with CSS/Javascript
- cssanimation.rocks - CSS spheres
- MDN - Circle
Then I tried again...the best I got were overly complicated 3d object illusions.
Like this:
body {
overflow: hidden;
background: #333;
}
.wrapper {
margin: 1em;
animation-duration: 20s;
}
.planet,
.planet:before,
.planet:after {
height: 300px;
width: 300px;
border-radius: 100vw;
will-change: transform;
margin: 0 auto;
}
.planet {
box-shadow: inset 0px 0px 10px 10px rgba(0, 0, 0, 0.4);
position: relative;
}
.wrapper,
.planet,
.planet:before {
animation-name: myrotate;
animation-duration: 20s;
}
.wrapper,
.planet,
.planet:before,
.planet:after {
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.planet:before,
.planet:after {
content: '';
position: absolute;
top: 0;
left: 0;
}
.planet:before {
box-shadow: inset 20px 20px 100px 00px rgba(0, 0, 0, .5), 0px 0px 5px 3px rgba(0, 0, 0, .1);
}
.planet:after {
filter: saturate(2.5);
background: linear-gradient(rgba(0, 0, 0, 1), transparent), url("https://i.stack.imgur.com/eDYPN.jpg");
opacity: 0.3;
box-shadow: inset -20px -20px 14px 2px rgba(0, 0, 0, .2);
animation-name: myopacity;
animation-duration: 5000000s;
}
@keyframes myrotate {
0% {
transform: rotatez(0deg);
}
100% {
transform: rotatez(360deg);
}
}
@keyframes myopacity {
0% {
background-position: 0px;
transform: rotatez(0deg);
}
50% {
background-position: 100000000px;
}
100% {
background-position: 0;
transform: rotatez(-360deg);
}
}
<div class="wrapper">
<div class="planet"></div>
</div>
And this:
body {
background: #131418;
}
.wrapper {
margin: 1em;
max-width: 100%;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.planet,
.planet:before,
.planet:after {
height: 500px;
width: 500px;
max-height: 30vw;
max-width: 30vw;
border-radius: 100vw;
will-change: transform;
}
.planet {
box-shadow: inset 0px 0px 100px 10px rgba(0, 0, 0, .5);
position: relative;
float: left;
margin: 0 2em;
}
.planet,
.planet:before,
.planet:after {
animation-name: myrotate;
animation-duration: 10s;
}
.wrapper,
.planet,
.planet:before,
.planet:after {
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.planet:before,
.planet:after {
content: '';
position: absolute;
top: 0;
left: 0;
}
.planet:before {
box-shadow: inset 50px 100px 50px 0 rgba(0, 0, 0, .5), 0 0 50px 3px rgba(0, 0, 0, .25);
background-image: -webkit-radial-gradient( top, circle cover, #ffffff 0%, #000000 80%);
opacity: .5;
}
.planet:after {
opacity: .3;
background-image: -webkit-radial-gradient( bottom, circle, #ffffff 0%, #000000 -200%);
box-shadow: inset 0px 0px 100px 50px rgba(0, 0, 0, .5);
}
@keyframes myrotate {
0% {
transform: rotatez(0deg);
}
100% {
transform: rotatez(-360deg);
}
}
.bg {
background: wheat;
}
<div class="wrapper">
<div class="planet bg"></div>
</div>
Which are okay until you try to actually rotate them on either the x-axis or the y-axis like the cube in my first example...here's what happens then: (simplified example)
.sphere {
background: black;
width: 300px;
height: 300px;
border-radius: 100vw;
animation: myrotate 10s linear infinite
}
@keyframes myrotate {
0% {
transform: rotate3d(0, 0, 0, 0);
}
100% {
transform: rotate3d(0, 1, 0, 360deg);
}
}
<div class="sphere"></div>
All you get is a flat 2d object - which is expeceted considering that it's what the element is
The closest thing I found is the following shape created in a tutorial by Timo Korinth
@-webkit-keyframes animateWorld {
0% {
-webkit-transform: rotateY(0deg) rotateX(0deg) rotateZ(0deg);
}
50% {
-webkit-transform: rotateY(360deg) rotateX(180deg) rotateZ(180deg);
}
100% {
-webkit-transform: rotateY(720deg) rotateX(360deg) rotateZ(360deg);
}
}
html {
background: #FFFFFF;
}
. world {
-webkit-perspective: 1000px;
}
.cube {
margin-left: auto;
margin-right: auto;
position: relative;
width: 200px;
height: 200px;
-webkit-transform-style: preserve-3d;
-webkit-animation-name: animateWorld;
-webkit-animation-duration: 10s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
}
.circle {
position: absolute;
width: 100%;
height: 100%;
border: 2px dashed #009BC2;
border-radius: 50%;
opacity: 0.8;
background: rgba(255, 255, 255, 0);
}
.zero {
-webkit-transform: rotateX(90deg);
}
.two {
-webkit-transform: rotateY(45deg);
}
.three {
-webkit-transform: rotateY(90deg);
}
.four {
-webkit-transform: rotateY(135deg);
}
.five {
width: 173px;
height: 173px;
margin: 14px;
-webkit-transform: rotateX(90deg) translateZ(50px);
}
.six {
width: 173px;
height: 173px;
margin: 14px;
-webkit-transform: rotateX(90deg) translateZ(-50px);
}
<div class="world">
<div class="cube">
<div class="circle zero"></div>
<div class="circle one"></div>
<div class="circle two"></div>
<div class="circle three"></div>
<div class="circle four"></div>
<div class="circle five"></div>
<div class="circle six"></div>
</div>
</div>
So here's my
Question:
How do I create an actual 3 dimensional sphere with pure CSS? More specifically, one that is covered - not just a frame - and doesn't involve hundreds of html elements.
Notes:
- Three dimensional spheres have height, width and depth - just like the cube in my first example snippet
- I don't need any physics and there's no need for any user-interaction. Just an animated spinning sphere.
Additional resources:
How do I create an actual 3 dimensional sphere with pure CSS?
Well, as many have stated in answers and comments it is simply impossible to create a single 3D entity in a browser with html and css only at this point in time, but it is possible to create an illusion of a 3D Object. Below is my approach to solving the problem.
What I have done:
border-radius:50%;
clip-path: circle(96px at center);
on the container, and threw a cool shade/light effect on to seal the dealvs
As a closing thought, I wanted to express my appreciation of the question being asked, it really made me think and was a great project that lead to me learning a lot of things about 3D capabilities of html/css.
Also thanks to all the people who have spent the time to pry this open, and came up with awesome approaches to the problem.
I hope the fruits of my research are of use. Cheers!
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.
Here is an example of an animated sphere / bubble, though this example is more of an illusion. I don't know If everything you are asking for is possible through pure css only, but I may be mistaken.
Making a realistic 3d css sphere, without some level of 2d illusion, would require many elements for it to have a smooth perimeter.
However, I made a version of Timo Korinth's example with:
Can be rotated arbitrarily as long as the shading animation is recalculated.
This page has some of the maths behind implementing spherical shading with CSS that could be used for this.
Edit: Other answers look nicer, so converted it to a Death Star
No, it's not possible under your criteria. All examples of 3D stuff using only HTML and CSS have performance issues, because that's not its purpose.
When it comes to heavy graphical effects, HTML and CSS are really bad at it.
The best way to make a real 3D sphere is using WebGL, which is an JavaScript API for creating 3D content.
As already mentioned above, CSS3 can't provide you with a real 3d shapes but just with the illusion. A good sphere illusion that use minimum HTML elements and use image as a texture can be done by a combination of what you have done and masking all with CSS shadows.
A nice touch that can make the mask more realistic is the use of the
:after
pseudo element for creation of an additional spark in shifted location and smaller size. A key thing for a successful effect is to remember that different materials reflect light differently. meaning if you're trying to create a metallic sphere the lighting created by thebox-shadow
will be different from the lighting of a plastic sphere.Another nice addition is the use of the
:before
pseudo element for creation of reflection effect. Adding a premade image of the world on a sphere with some opacity can create a very persuasive effect. notice also with the reflection the material you are trying to create will determine the amount of opacity you want for the reflection.Notice, I used octagonal prism for having the image behind the scene to look more round when the 3d trasform apply its perspective. Even with using only 8 elements the result is quite realistic. More realistic result can be done with more polygons and more complex shapes and texture mappings but even then no need for too many elements due to the addition of the shadow and the spark above everything.
Last, in order to hide the rest of the octagonal prism and display only the parts inside the sphere bounds I'm using
clip-path: circle(99px at center);
.