<!-- Forked from: https://codepen.io/creativeocean/pen/ByBogvj -->
<div class="pov">
  <div class="tray">
    <div class="die">
      <div class="cube">
        <div class="face" style="font-size:60px">ProductDesign</div>
        <div class="face" style="font-size:58px">UserExperience</div>
        <div class="face" style="font-size:55px">UserInterface</div>      
      </div>
    </div>  
    <!-- clones will go here -->
  </div>
</div>
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@900&display=swap');

html, body {
  font-family: "Montserrat", sans-serif;
  font-optical-sizing: auto;
  font-weight: 900;
  font-style: normal;
  background:#000;
  overflow:hidden;
}

body, pov {
  width:100%;
  height:100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.die {
  width: 400px;
  height: 55px;
  padding-bottom: 9px;
  perspective: 999px;
}

.cube {
  position: absolute;
  width:100%;;
  height:100%;
  transform-style: preserve-3d;
}

.face {
  position: absolute;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
/*   letter-spacing:-1px; */
  backface-visibility: hidden;
/*   overflow:hidden; */
}
const n = 19
const rots = [
  { ry: 270, a:0.5 },
  { ry: 0,   a:0.85 },
  { ry: 90,  a:0.4 },
  { ry: 180, a:0.0 }
]

gsap.set(".face", {
  z: 200,
  rotateY: i => rots[i].ry,
  transformOrigin: "50% 50% -201px"
});

for (let i=0; i<n; i++){
  let die = document.querySelector('.die')
  let cube = die.querySelector('.cube')
  
  if (i>0){    
    let clone = document.querySelector('.die').cloneNode(true);
    document.querySelector('.tray').append(clone);
    cube = clone.querySelector('.cube')
  }
  
  gsap.timeline({repeat:-1, yoyo:true, defaults:{ease:'power3.inOut', duration:1}})
  .fromTo(cube, {
    rotateY:-90
  },{
    rotateY:90,
    ease:'power1.inOut',
    duration:2
  })
  .fromTo(cube.querySelectorAll('.face'), {
    color:(j)=>'hsl('+(i/n*75+130)+', 67%,'+(100*[rots[3].a, rots[0].a, rots[1].a][j])+'%)'
  },{
    color:(j)=>'hsl('+(i/n*75+130)+', 67%,'+(100*[rots[0].a, rots[1].a, rots[2].a][j])+'%)'
  }, 0)
  .to(cube.querySelectorAll('.face'), {
    color:(j)=>'hsl('+(i/n*75+130)+', 67%,'+(100*[rots[1].a, rots[2].a, rots[3].a][j])+'%)'
  }, 1)
  .progress(i/n)
}

gsap.timeline()
  .from('.tray', {yPercent:-3, duration:2, ease:'power1.inOut', yoyo:true, repeat:-1}, 0)
  .fromTo('.tray', {rotate:-15},{rotate:15, duration:4, ease:'power1.inOut', yoyo:true, repeat:-1}, 0)
  .from('.die', {duration:0.01, opacity:0, stagger:{each:-0.05, ease:'power1.in'}}, 0)
  .to('.tray', {scale:1.2, duration:2, ease:'power3.inOut', yoyo:true, repeat:-1}, 0)

window.onload = window.onresize = ()=> {
  const h = n*56
  gsap.set('.tray', {height:h})
  gsap.set('.pov', {scale:innerHeight/h})
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.com/gsap@3/dist/gsap.min.js