<div class="scene" id="js-scene">
  <div class="el el--small" id="js-el-small"></div>
  <div class="el" id="js-el"></div>
  <div class="shadow" id="js-shadow"></div>
</div>
body, html {
    margin:  0;
    padding: 0;
    background: #FFF5E4;
  }

.el {
  position:     absolute;
  left:         50%;
  top:          50%;
  width:        100px;
  height:       100px;
  margin-left: -50px;
  margin-top:  -50px;
  background:   #572B53;
  border-radius: 3px;
  z-index:      1;
  /*transform-origin: center bottom;*/
}

.el--small {
  width:        50px;
  height:       50px;
  margin-left: -25px;
  margin-top:  -50px;
  background:   #230323;
  z-index:      2;
}

.scene {
  position: absolute;
  left:     50%;
  top:      50%;
  width:    100px;
  height:   100px;
  margin-left: -50px;
  margin-top:  -50px;
}

.shadow {
  position:     absolute;
  left:         50%;
  top:          100%;
  width:        80px;
  height:       7px;
  margin-left: -40px;
  margin-top:  -2px;
  background:   #333;
  opacity:      .25;
  border-radius: 50%;
  z-index:      0;
}
const DURATION = 1000;

const scalePath = mojs.easing.path('M0, 0 C0, 0 16.857142857142854, -5.248486282060085e-15 31.142857142857142, 0 C30.80272108843543, -113.14285714285715 50, -100 50, -100 C50, -100 65.31357300419697, -100.13604825191986 70, -0.2857142857142857 C84.28506645158541, -0.14966603379441404 100, 0 100, 0 ');

const CUTSOM_PROPERTIES = {
  originY: 50,
  draw (el, props) {
    el.style.transformOrigin = `50% ${props.originY}%`;
  }
}

const SQUARE_OPTS = {
  customProperties: CUTSOM_PROPERTIES,
  y:  { [-200]: -200, curve: 'M0, 0 C16.815811159807826, -3.2443825883792443 27.57166399691211, 35.51058598192055 30, 100 C31.90230129013731, 99.91798544665086 67.88545167517314, 99.9146334942176 70, 100 C72.54311975339816, 46.08536650578238 87.89847455447789, 2.672954016950679 100, 0 ' },
  scaleX: { 1: 1, curve: scalePath },
  scaleY: { 1: 1, curve: function (k) {
    return 1 + (1-scalePath(k))/1.5;
    }
  },
  originY: { 100: 100, curve: 'M0, 50 C0, 50 30, 50 30, 50 C30, 50 31.559139784946236, 0 31.559139784946236, 0 C31.559139784946236, 0 55.71492892960311, -0.1355624091374383 70, 0 C74.60765171555815, 54.42127669485172 100, 50 100, 50 ' },
  duration: DURATION
}

const square1 = new mojs.Html({
  ...SQUARE_OPTS,
  el: '#js-el',
  angleZ: { 90: 90, curve: 'M0, 100 C0, 100 30, 0 30, 0 C30, 0 30, 100 30, 100 C30, 100 70, 100 70, 100 C70, 100 100, 0 100, 0 ' }
});

const square2 = new mojs.Html({
  ...SQUARE_OPTS,
  el: '#js-el-small',
  y:  { [-300]: -300, curve: 'M0, 0 C10.101525445522112, -1.5300968740935288 24.578551642263555, 1.7142857142857142 30, 87.42857142857143 C33.06687772061731, 107.14285714285714 50, 107 50, 107 C50, 107 62.917147228930936, 104.99294035155644 70, 87.14285714285714 C74.31276966857592, 6.435631077014981 90.03697870960255, 0.67295401695068 100, 0 ' },
  angleZ: { [-90]: [-90], curve: 'M0, 100 C0, 100 30, 0 30, 0 C30, 0 30, 100 30, 100 C30, 100 70, 100 70, 100 C70, 100 100, 0 100, 0 ' }
});

const FILLS = [ '#7b7b7b', '#6b6b6b', '#5b5b5b', '#4a4a4a' ];

const DUST_OPTS = {
  parent: '#js-scene',
  count:  5,
  top: '100%',
  left: '15%',
  x:     { 0: 150, easing: 'cubic.in' },
  degree: 10,
  angle: { 90: 10, easing: 'cubic.inout' },
  radius: {0: 150},
  opacity: .35,
  timeline: { speed: .75 },
  children: {
    fill:   FILLS,
    radius: 'rand(12,18)',
    direction: 1,
    pathScale: 'rand(.5, .75)',
    scale: { 1: 0, easing: 'cubic.inout' },
    isSwirl: true,
    swirlSize: 'rand(10, 17)',
    swirlFrequency: 'rand(2,4)',
    duration: 'stagger(300, 35)',
    delay: 235
  }
}

const dust1 = new mojs.Burst({
  ...DUST_OPTS
});

const dust2 = new mojs.Burst({
  ...DUST_OPTS,
  left:  '70%',
  angle: { [-90]: -10, easing: 'cubic.inout' },
  x:     { 0: -150, easing: 'cubic.in' },
  children: {
    ...DUST_OPTS.children,
    direction: -1
  }
});

dust1.el.style.zIndex = 3;
dust2.el.style.zIndex = 3;

const shadow = new mojs.Html({
  el: '#js-shadow',
  opacity: { 1: 1, curve: function (k) {
    return Math.max( (scalePath(k)-1)/4, .05 );
    }
  },
  scaleX: { 1: 1, curve: function (k) {
    return 1.35*scalePath(k);
    }
  },
  duration: DURATION
});

const timeline = new mojs.Timeline({ speed: .63 });
timeline.add( square1, square2, dust1, dust2, shadow );

new MojsPlayer({ add: timeline, isPlaying: true, isRepeat: true });
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdn.jsdelivr.net/mojs/0.288.1/mo.min.js
  2. https://cdn.jsdelivr.net/mojs-player/0.43.15/mojs-player.min.js