.scene
  - const height = 400
  - const width = 150
  - const inRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min
  mixin lava(amount)
    .lava-lamp__lava
      svg
        ellipse.blob.blob--top(cx='35', cy='0', rx='35', ry='10')
        ellipse.blob.blob--bottom(cx='75', cy='270', rx='75', ry='10')
        - for (let i = 0; i < amount; i++)
          - const speed = inRange(8, 50)
          - const delay = inRange(0, 10) - 10
          - const height = inRange(30, 100)
          - const width = inRange(30, 100)
          - const skewX = inRange(0, 10) - 5
          - const skewY = inRange(0, 10) - 5
          - const rotation = inRange(0, 360)
          - const x = inRange(0, 150)
          - const direction = Math.random() > 0.5 ? 'alternate' : 'alternate-reverse'
          circle.blob(r=(height / 2), cx=x, cy='400', style=`--skewX: ${skewX}; --skewY: ${skewY}; --height: ${height}; --speed: ${speed}; --delay: ${delay}; --direction: ${direction};`)
  .lava-lamp(style=`--height: ${height}; --width: ${width}`)
    .lava-lamp__main
      .lava-lamp__glass
        +lava(3)
        +lava(4)
        +lava(2)
        +lava(1)
    .lava-lamp__base

  svg(style='position: absolute; left: 100%')
    defs
      filter(id=`goo`)
        feGaussianBlur(in='SourceGraphic', stdDeviation='10', result='BLUR')
        feColorMatrix(in='BLUR', mode='matrix', values='1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 18 -7', result='GOO')
        feBlend(in='SourceGraphic', in2=`goo`)
View Compiled
*
  box-sizing border-box

body
  background darken(#8c14fc, 80%)
  min-height 100vh

.scene
  align-items center
  display flex
  justify-content center
  width 100vw
  position relative
  height 100vh
  overflow-x hidden

$lamp = #000
// $shadow = #bf55ec
// $core = #663399
// $lava = lighten(#9f5afd, 30%)
// $lava-bg = radial-gradient(circle at 50% 50%, $core 25%, $shadow)
$shadow = #19b5fe
$core = #3a539b
$lava = #29f1c3
$lava-bg = radial-gradient(circle at 50% 50%, $core 25%, $shadow)

img
  position absolute
  top 50%
  left 50%
  transform translate(-69.5%, -26%)
  opacity .2
  height 1300px

.lava-lamp
  height calc(var(--height) * 1px)
  width calc(var(--width) * 1px)
  position relative

  &:after
    content ''
    height 500px
    width 500px
    position absolute
    background radial-gradient(circle at 50% 50%, $shadow, transparent 60%)
    top 45%
    left 50%
    z-index 4
    transform translate(-50%, -50%)
    filter blur(10px)
    opacity .5

  &:before
    content ''
    height 25%
    width 300%
    border-radius 100%
    background radial-gradient(ellipse at 50% 50%, $shadow, transparent 60%)
    position absolute
    top 85%
    left 50%
    transform translate(-50%, 0)
    opacity .5
    filter blur(10px)

  &__main
    border-radius 50% 50% 50% 50% / 60% 60% 40% 40%
    width 100%
    height 100%
    position relative
    overflow hidden
    z-index 2

    &:after
    &:before
      background $lamp
      content ''
      height 12%
      left 0
      position absolute
      width 100%

    &:after
      top 0

    &:before
      bottom 0
      height 26%

  &__glass
    background $lava-bg
    border-radius 50% / 10%
    overflow hidden
    height 70%
    left 0
    position absolute
    top 10%
    width 100%
    z-index 2

  &__base
    height 20%
    width 100%
    position absolute
    bottom 0
    overflow hidden
    border-radius 0 0 50% 50% / 0 0 35% 35%

    &:before
      content ''
      background $lamp
      border-radius 50% 50% 50% 50% / 60% 60% 40% 40%
      position absolute
      top -60%
      height calc(var(--height) * 1px)
      width calc(var(--width) * 1px)

  &__lava
    border-radius 50% / 10%
    height 100%
    overflow hidden
    position absolute
    width 100%
    filter url('#goo')

    svg
      height 280px
      width 150px

.blob
  animation-delay calc(var(--delay) * 1s)
  animation-direction var(--direction)
  animation-duration calc(var(--speed) * 1s)
  animation-iteration-count infinite
  animation-name blob
  animation-timing-function linear
  fill $lava
  transform-box fill-box

  &--bottom
  &--top
    animation none

  &--top
    animation sway 20s infinite linear

    @keyframes sway
      50%
        transform translate(50%, 0)

@keyframes blob
  from
    transform skew(calc(var(--skewX) * 1deg), calc(var(--skewY) * 1deg)) translate(0, 0)
  to
    transform skew(calc(var(--skewX) * 1deg), calc(var(--skewY) * 1deg)) translate(0, calc((400 + (var(--height) * 2)) * -1px))
View Compiled
// NOPE
/**
 * Possible by randomly generating CSS variables
 * within our markup code.
 * Those variables define the characteristics of
 * each blob.
*/ 
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.