<input type="checkbox" id="shadows" checked /><label for="shadows">Soft shadows</label>
<div class="cubes">
  <!--   row, column, z -->
  <div class="cube" data-cube="111">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-z" data-cube="112"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="121">
    <div class="cube-wrap">
      <div class="cube-top">
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="131">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-z" data-cube="132"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="211">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="111"></div>
        <div class="shadow-y" data-cube="111"></div>
        <div class="shadow-z" data-cube="212"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="221">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="121"></div>
        <div class="shadow-y" data-cube="121"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="231">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="131"></div>
        <div class="shadow-y" data-cube="131"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="311">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="211"></div>
        <div class="shadow-y" data-cube="211"></div>
        <div class="shadow-z" data-cube="312"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="321">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="221"></div>
        <div class="shadow-y" data-cube="221"></div>
        <div class="shadow-z" data-cube="322"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="331">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="231"></div>
        <div class="shadow-y" data-cube="231"></div>
        <div class="shadow-z" data-cube="332"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>

  <!-- top layer -->
  <div class="cube" data-cube="112">
    <div class="cube-wrap">
      <div class="cube-top">

      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="122">
    <div class="cube-wrap">
      <div class="cube-top">
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="132">
    <div class="cube-wrap">
      <div class="cube-top">
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="212">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="112"></div>
        <div class="shadow-y" data-cube="112"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="222">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="122"></div>
        <div class="shadow-y" data-cube="122"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="232">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="132"></div>
        <div class="shadow-y" data-cube="132"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="312">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="212"></div>
        <div class="shadow-y" data-cube="212"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="322">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="222"></div>
        <div class="shadow-y" data-cube="222"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="332">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="232"></div>
        <div class="shadow-y" data-cube="232"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>

  <!-- bottom layer -->
  <div class="cube" data-cube="113">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-z" data-cube="111"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="123">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-z" data-cube="121"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="133">
    <div class="cube-wrap">
      <div class="cube-top">
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="213">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="113"></div>
        <div class="shadow-y" data-cube="113"></div>
        <div class="shadow-z" data-cube="211"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="223">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-y" data-cube="123"></div>
        <div class="shadow-z" data-cube="221"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="233">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-y" data-cube="133"></div>
        <div class="shadow-z" data-cube="231"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="313">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="213"></div>
        <div class="shadow-y" data-cube="213"></div>
        <div class="shadow-z" data-cube="311"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="323">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="223"></div>
        <div class="shadow-y" data-cube="223"></div>
        <div class="shadow-z" data-cube="321"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  <div class="cube" data-cube="333">
    <div class="cube-wrap">
      <div class="cube-top">
        <div class="shadow-flip" data-cube="233"></div>
        <div class="shadow-y" data-cube="233"></div>
        <div class="shadow-z" data-cube="331"></div>
      </div>
      <div class="cube-bottom"></div>
      <div class="cube-front-left"></div>
      <div class="cube-front-right"></div>
      <div class="cube-back-left"></div>
      <div class="cube-back-right"></div>
    </div>
  </div>
  
  <div class="large-shadows">
    <div class="large-shadow" data-cube="113"></div>
    <div class="large-shadow" data-cube="123"></div>
    <div class="large-shadow" data-cube="133"></div>
    <div class="large-shadow" data-cube="213"></div>
    <div class="large-shadow" data-cube="223"></div>
    <div class="large-shadow" data-cube="233"></div>
    <div class="large-shadow" data-cube="313"></div>
    <div class="large-shadow" data-cube="323"></div>
    <div class="large-shadow" data-cube="333"></div>
  </div>
</div>
$duration: 4s;
$delays: ();
$variance: 300;
$easing: cubic-bezier(.5, 0, 0, 1);
$gap: 2px;

@for $row from 1 through 3 {
  @for $col from 1 through 3 {
    @for $layer from 1 through 3 {
      $delays: map-merge($delays, ('#{$row}#{$col}#{$layer}': random($variance)));
    }
  }
}

html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

*, *:before, *:after {
  box-sizing: border-box;
  position: relative;
  animation-timing-function: $easing !important;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
  transform-style: preserve-3d;
  perspective: 1000px;
  background: linear-gradient(to bottom right, #a4c5c7, #2D3F48);
  
  &:hover {
    > label, > input {
      opacity: 1;
    }
  }
}

label, #shadows {
  position: fixed;
  top: 2vmin;
  opacity: 0.5;
}
label {
  left: 6vmin;
  color: white;
  font-weight: bold;
}
#shadows {
  left: 2vmin;
}

#shadows:not(:checked) ~ .cubes {
  --shadow-filter: none;
}

.cubes {
  width: 10vmin;
  height: 10vmin;
  transform: rotateX(60deg) rotateZ(-45deg);
  backface-visibility: visible;
  overflow: visible;
  transform-style: preserve-3d;
  perspective: 9000px;
}

.cube, .large-shadow {
  height: 10vmin;
  width: 10vmin;
  transform-style: preserve-3d;
  backface-visibility: visible;
  transform-origin: center center -.5vmin;
  position: absolute;
  top: 0;
  left: 0;
  
  > .cube-wrap {
    animation: cube $duration infinite both;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    transform-style: preserve-3d;
    backface-visibility: visible;
    transform-origin: center center -.5vmin;
    will-change: transform;
    
    @keyframes cube {
      from, to, 20%, 60% {
        animation-timing-function: step-start;
        transform: none;
      }

      40% {
        transform: rotateY(-1turn);
      }
    }
  }
  
  &[data-cube^="1"] {
    top: calc(-10vmin - #{$gap});
    --color-bg-top: #A0EBE8;
    --color-bg-bottom: #89E4E4;
    --color-fl-top: #4EAFBC;
    --color-fl-bottom: #43A5B2;
  }
  &[data-cube^="2"] {
    --color-bg-top: #89E4E4;
    --color-bg-bottom: #76DEE5;
    --color-fl-top: #43A5B2;
    --color-fl-bottom: #3D93A9;
  }
  &[data-cube^="3"] {
    top: calc(10vmin + #{$gap});
    --color-bg-top: #76DEE5;
    --color-bg-bottom: #63D3D4;
    --color-fl-top: #3D93A9;
    --color-fl-bottom: #3B8D9F;
  }
  &[data-cube^="11"],
  &[data-cube^="21"],
  &[data-cube^="31"]{
    left: calc(-10vmin - #{$gap});
    --color-fr-top: #2D505F;
    --color-fr-bottom: #2D4F63;
  }
  &[data-cube^="12"],
  &[data-cube^="22"],
  &[data-cube^="32"]{
    --color-fr-top: #2D4F63;
    --color-fr-bottom: #2A5262;
  }
  &[data-cube^="13"],
  &[data-cube^="23"],
  &[data-cube^="33"]{
    left: calc(10vmin + #{$gap});
    --color-fr-top: #2A5262;
    --color-fr-bottom: #2A5467;
  }
  
  &[data-cube$="2"] {
    transform: translateZ(calc(10vmin + #{$gap}));
  }
  
  &[data-cube$="3"] {
    transform: translateZ(calc(-10vmin - #{$gap}));
  }
}


.large-shadows {
  transform: translateZ(-21vmin);
}
.large-shadow {
  background: black;
  height: 10vmin;
  width: 10vmin;
  transform-origin: top right;
  animation: large-shadow $duration infinite both;
  filter: var(--shadow-filter, blur(3vmin));
  opacity: 0.2;
  will-change: transform;
  
  @keyframes large-shadow {
    from, 80%, to {
      transform: scale(1.5, 3);
    }
    20% {
      transform: scale(1.5, 2);
    }
    40%, 60% {
      transform: scale(1.5, 5);
    }
    50% {
      transform: scale(1, 5);
    }
  }
}

[class^="cube-"] {
  position: absolute;
  width: 100%;
  height: 100%;
  backface-visibility: visible;
  top: 0;
  left: 0;
  
  &, &:before {
    will-change: transform;
    animation: any $duration infinite both;
  }
  
  &:before {
    content: '';
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: inherit;
  }
}

.cube-top {
  animation-name: cube-top;
  overflow: hidden;
  
  &:before, &:after {
    will-change: transform;
  }
  
  &:before {
    background-image:
      linear-gradient(to bottom, #CBFEFF,  transparent),
      linear-gradient(to bottom, var(--color-bg-top), var(--color-bg-bottom));
    background-size: 2px 100%, auto;
    background-repeat: no-repeat;
  }
  
  &:after {
    content: '';
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: #DFF4F0;
    animation: cube-top-flash $duration infinite both;
    
    @keyframes cube-top-flash {
      from, 50%, 60%, to {
        opacity: 0;
      }
      52% {
        opacity: 0.9;
      }
    }
  }
  
  @keyframes cube-top {
    20% {
      transform: translateZ(calc(-10vmin + 2vmin));
    }
    40%, 60%, 80% {
      transform: none;
    }
  }
}

.cube-front-left {
  transform-origin: left center;
  transform: rotateY(90deg);
  overflow: hidden;
  
  &:before {
    background-image:
      linear-gradient(to bottom, var(--color-fl-top), var(--color-fl-bottom)),
      linear-gradient(to bottom, rgba(white, .5), transparent 60%);
    background-size: auto auto 1px 100%;
    background-repeat: no-repeat;
    transform-origin: right;
    animation-name: cube-front-left;
    will-change: transform;
  }
  
  @keyframes cube-front-left {
    20% {
      transform: scaleX(.2);
    }
    40%, 60% {
      transform: translateX(-8vmin) scaleX(.2) ;
    }
    80% {
      transform: none;
    }
  }
}

.cube-front-right {
  transform-origin: bottom center;
  transform: rotateX(90deg);
  
  &:before {
    // background-color: #2C5060;
    background-image:
      linear-gradient(to right, var(--color-fr-top), var(--color-fr-bottom));
    transform-origin: top;
    animation-name: cube-front-right;
    will-change: transform;
  }
  
  @keyframes cube-front-right {
    20% {
      transform: scaleY(.2);
    }
    40%, 60% {
      transform: translateY(8vmin) scaleY(.2) ;
    }
    80% {
      transform: none;
    }
  }
}

.cube-bottom {
  transform: translateZ(-10vmin);
  background-image:
    linear-gradient(to bottom, var(--color-bg-top), var(--color-bg-bottom));
  animation-name: cube-bottom;
  
  @keyframes cube-bottom {
    from, 20%, 80%, to {
      transform: translateZ(calc(-10vmin + 1px)) scale(.95);
    }
    40%, 60% {
      transform: translateZ(-2vmin) scale(.95);
    }
  }
  
  &:after {
    content: '';
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: #DFF4F0;
    animation: cube-bottom-flash $duration infinite both;
    will-change: transform;
    
    @keyframes cube-bottom-flash {
      from, 40%, 50%, to {
        opacity: 0;
      }
      45% {
        opacity: 0.9;
      }
    }
  }
}

[class^="shadow"] {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  filter: var(--shadow-filter, blur(20px));
  
  &, &:before {
    will-change: transform;
  }
  
  &:before {
    content: '';
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: #214354;
    opacity: 0.7;
  }
}

.shadow-y {
  animation: shadow-y $duration infinite both;
  
  &[data-cube^="11"],
  &[data-cube^="21"] {
    left: 10%;
  }
  &[data-cube^="13"],
  &[data-cube^="23"] {
    left: -10%;
  }
  
  @keyframes shadow-y {
    to, 40%, 60%, 80%, from {
      transform: translateY(-100%) scale(2, .75);
    }
    20% {
      transform: scale(2, 1);
    }
  }
  
  &:before {
    animation: shadow-y-inverse $duration infinite both;
    transform-origin: top center;
    
    @keyframes shadow-y-inverse {
      to, 40%, 60%, 80%, from {
        opacity: 0.9;
        transform: scale(2, 1)
      }
      20% {
        opacity: 0;
        transform: translateY(-100%) scale(2, .75);
      }
    }
  }
}

.shadow-flip {
  width: 2vmin;
  height: 50%;
  top: -1vmin;
  left: calc(50% - 1vmin);
  transform-origin: top center;
  filter: var(--shadow-filter, blur(1vmin));
  animation: shadow-flip $duration infinite both;
  overflow: visible;
  
  @keyframes shadow-flip {
    from, 20%, 40%, 50%, to {
      transform: rotate(90deg);
    }
    45% {
      transform: rotate(270deg);
    }
  }
  
  &:before {
    animation: shadow-flip-inverse $duration infinite both;
    transform-origin: top center;
    opacity: 0;
    
    @keyframes shadow-flip-inverse {
      from, 20%, 40%, 50%, to {
        transform: rotate(0deg);
        opacity: 0;
      }
      45% {
        transform: rotate(-180deg);
        opacity: 0.6;
      }
    }
  }
}

.shadow-z {
  animation: shadow-z $duration infinite both;
  overflow: visible;
  will-change: transform;
  transform-origin: top center;
  
  &[data-cube^="11"] {
    --shadow-z-left: 50%;
    --shadow-z-top: 50%;
  }
  &[data-cube^="21"],
  &[data-cube^="31"] {
    --shadow-z-left: 50%;
    --shadow-z-top: 0;
  }
  &[data-cube^="32"] {
    --shadow-z-left: 0;
    --shadow-z-top: 0;
    --shadow-x-scale: 2;
  }
  &[data-cube^="33"] {
    --shadow-z-left: -50%;
    --shadow-z-top: 0;
  }
  
  @keyframes shadow-z {
    from, 40%, 60%, 80%, to {
      transform: scale(var(--shadow-x-scale), 2);
    }
    20% {
      transform: translateX(var(--shadow-z-left)) translateY(var(--shadow-z-top)) scale(calc(var(--shadow-x-scale, 1) / 2), 2);
    }
  }
  
  &:before {
    animation: shadow-z-inverse $duration infinite both;
    
    @keyframes shadow-z-inverse {
      40%, 60% {
        transform: translateX(var(--shadow-z-left)) translateY(var(--shadow-z-top)) scale(var(--shadow-x-scale, 1), 2);
      }
      45% {
        opacity: 0;
      }
      from, 40%, 55% {
        opacity: .5;
      }
      to {
        opacity: 1;
      }
      from, 20%, 80%, to {
        transform: scale(2);
      }
    }
  }
}

@each $key in map-keys($delays) {
  .cube[data-cube="#{$key}"] > .cube-wrap {
    &, > [class^="cube-"] {
      &, &:before, &:after {
        animation-delay: map-get($delays, $key) * 1ms;
      }
    }
    [class^="shadow-"] {
      animation-delay: map-get($delays, $key) * 1ms;
    }
  }
  [class^="shadow-"][data-cube="#{$key}"]:before,
  .large-shadow[data-cube="#{$key}"] {
    animation-delay: map-get($delays, $key) * 1ms;
  }
}
View Compiled
// I was going to use THREE.js but I forgot.
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.