- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min
- const baseHue = randomInRange(0, 360)
- const bubbleCount = 30
- let b = 0
while b < bubbleCount
  - const size = randomInRange(10, 50)
  - const delay = randomInRange(1, 10)
  - const speed = randomInRange(2, 20)
  - const distance = randomInRange(50, 250)
  - const scale = randomInRange(100, 150) / 100
  - const x = randomInRange(0, 100)
  .bubble(style=`--x: ${x}; --size: ${size}; --hue: ${baseHue}; --distance: ${distance}; --speed: ${speed}; --delay: ${delay}; --scale: ${scale}`)
  - b++
View Compiled
body
  align-items center
  background #111
  display flex
  justify-content center
  min-height 100vh
  overflow hidden

.bubble
  --bubble-outline 'hsl(%s, 100%, 50%)' % var(--hue)
  --bubble-spot 'hsl(%s, 100%, 75%)' % var(--hue)
  --bubble-shade 'hsl(%s, 100%, 70%)' % var(--hue)
  animation-name float
  animation-duration calc(var(--speed) * 1s)
  animation-delay calc(var(--delay) * -1s)
  animation-iteration-count infinite
  animation-timing-function ease-in-out
  background radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%), radial-gradient(15% 15% at 75% 75%, var(--bubble-spot), transparent), radial-gradient(100% 100% at 50% 25%, transparent, var(--bubble-shade) 98%)
  border 1px solid var(--bubble-outline)
  border-radius 100%
  height calc(var(--size) * 1px)
  left calc(var(--x) * 1%)
  position absolute
  top 100%
  transform translate(-50%, 0)
  width calc(var(--size) * 1px)
  will-change transform
  
@keyframes float
  0%
    opacity 1
    transform translate(-50%, 0) scale(0)
  75%
    opacity 1
  100%
    opacity 0
    transform translate(-50%, calc(var(--distance) * -1vh)) scale(var(--scale))
    
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.