//- you can change these two values
- let n_rings = 18 /* must be even */, n_sides = 24;
- let n = n_rings*n_sides;

style
  - for(let i = 0; i < n_rings; i++)
    | .area:nth-child(n + #{n_sides*i + 1}) { --i: #{i} }
  - for(let i = 0; i < n_sides; i++)
    | .area:nth-child(#{n_sides}n + #{i + 1}) { --j: #{i} }
section.scene
  div(class='🌐' style=`--n-rings: ${n_rings}; --n-sides: ${n_sides}`)
    while n--
      .area
  .prompt scroll
View Compiled
$r: 23vmin; // sphere inradius

body { height: 200vh }

section, div { display: grid }

svg[height='0'], .scene { position: fixed }

div {
  grid-area: 1/ 1;
  place-self: center
}

.scene {
  inset: 0;
  perspective: 25em;
  background: 
    url(https://assets.codepen.io/2017/night_sky.jpg) 
      50%/ cover #212121;
  background-blend-mode: multiply;
  
  &::before, &::after {
    grid-area: 1/ 1;
    margin: 0 auto;
    padding: .5em;
    max-width: 18em;
    color: #121212;
    font: clamp(.625em, 6.5vmin, 3.25em) neonderthaw, cursive;
    text-align: center;
    content: 'Dedicated to the person who taught me about Jupiter some 35 years ago. Missing you.'
  }
  
  &::after {
    align-self: end;
    mix-blend-mode: multiply;
    content: '1 June 1927 - 18 January 2019'
  }
}

.🌐 {
  transform-style: preserve-3d;
  rotate: x -60deg;
  will-change: transform;
  animation: 
    ry 16s linear infinite, 
    rx 1s linear forwards;
}

@supports (animation-timeline: auto, scroll()) {
  .🌐 { animation-timeline: auto, scroll() }
  
  @keyframes rx { to { rotate: x 60deg } }
}

@supports not (animation-timeline: auto, scroll()) {
  .prompt { display: none }
}

@keyframes ry { to { transform: rotatey(1turn) } }

.area { 
  --dif-i: calc(var(--i) - .5*(var(--n-rings) - 1));
  --sgn-i: clamp(-1, 2*var(--dif-i), 1);
  --rnd-i: round(to-zero, var(--dif-i), 1);
  /* get around lack of support for abs() in Firefox */
  --abs-i: max(var(--rnd-i), -1*var(--rnd-i));
  
  --bay: calc(360deg/var(--n-sides));
  --hay: calc(180deg/var(--n-sides));
  --bax: calc(180deg/var(--n-rings));
  --hax: calc(90deg/var(--n-rings));
  
  --l: calc(2*#{$r}*tan(var(--hax)));
  
  --f0: cos(var(--abs-i)*var(--bax));
  --f1: cos((var(--abs-i) + 1)*var(--bax));
  
  --y0: sin(var(--abs-i)*var(--bax));
  --y1: sin((var(--abs-i) + 1)*var(--bax));
  
  --rx: atan2(var(--f0) - var(--f1), var(--y1) - var(--y0));
  
  --oy0: calc((var(--sgn-i) - 1)*-50%);
  --oy1: calc((var(--sgn-i) + 1)*50%);
  
  --w: calc(var(--f0)*2*#{$r}*tan(var(--hay)));
  width: var(--w); 
  height: var(--l);
  //backface-visibility: hidden;
  perspective-origin: 50% var(--oy1);
  perspective: calc(max(.1, var(--f1))*var(--l)/(var(--f0) - var(--f1)));
  transform-origin: 50% var(--oy0);
  transform: 
    rotatey(calc(var(--j)*var(--bay))) 
    translate3d(
      0, 
      calc(var(--sgn-i)*(50% + #{$r}*var(--y0))), 
      calc(var(--f0)*#{$r})) 
    rotatex(calc(-1*var(--sgn-i)*var(--rx)));
  clip-path: 
    polygon(
      calc((1 - var(--f1)/var(--f0))*50%) var(--oy1), 
      calc((1 + var(--f1)/var(--f0))*50%) var(--oy1), 
      100% var(--oy0), 
      0 var(--oy0));
  filter: 
    drop-shadow(0 0 1px wheat) 
    drop-shadow(0 0 1px peachpuff);
  
  &::after {
    transform-origin: inherit;
    transform: 
      matrix3d(1, 0, 0, 0, 
               0, 1, calc(-1*var(--sgn-i)), 0, 
               0, 0, 1, 0, 
               0, 0, 0, 1);
    background: 
      url(https://assets.codepen.io/2017/jupiter_projection.jpg) 
        calc(-1*var(--j)*var(--w)) calc(-1*var(--i)*var(--l))/ 
        calc(var(--n-sides)*100%) calc(var(--n-rings)*100%);
    content: ''
  }
}

/* fallback if round() is not supported in Chrome */
@supports not (z-index: Round(to-zero, 8.5, 1)) {
  @property --rnd-i {
    syntax: '<integer>';
    initial-value: 0;
    inherits: true
  }
  
  .area { --rnd-i: calc(var(--dif-i) - var(--sgn-i)*.5) }
}

.prompt {
  grid-auto-flow: column;
  align-items: center;
  justify-self: end;
  padding: .5em;
  color: ivory;
  font: 1.25em montserrat, sans-serif;
  writing-mode: vertical-rl;
  text-orientation: upright;
  text-transform: uppercase;
  
  &::before, &::after { font-size: 1.5em }
  
  &::before { content: '↑' }
  &::after { content: '↓' }
  
  &::selection { background: darkorange }
}
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.