Untitled fiddle

by Tan

HTML

<div id="container" class="container">
<div id="grad1" class="grad1"></div>
<div id="grad2" class="grad2"></div>
<div id="grad3" class="grad3"></div>
<div id="grad4" class="grad4"></div>
</div>

CSS

.container {max-width:900px;height:500px;margin:auto;position:relative;overflow:hidden;border:1px solid #bbbbbb;}

.grad1, .grad2, .grad3, .grad4 {position:absolute;width:50%;height:50%;overflow:visible;border:1px solid #bbbbbb;}
.grad1 {top:-10%;left:-20%;
background: radial-gradient(50% 50% at center center, rgba(10, 149, 231, .12) 0, rgba(10, 149, 231, 0) 100%);	}
.grad2 {top:20%;left:10%;
background: radial-gradient(50% 50% at center center, rgba(227,173,23,0.2) 0, rgba(227,173,23,0) 100%);}
.grad3 {top:50%;left:35%;
background: radial-gradient(50% 50% at center center, rgba(136, 85, 218, 0.1) 0, rgba(136, 85, 218,0) 100%);}
.grad4 {top:-15%;right:-15%;
background: radial-gradient(50% 50% at center center, rgba(57, 168, 63, 0.1) 0, rgba(57, 168, 63,0) 100%);}

JavaScript

(function() {
  const container = document.getElementById('container');
  const elements = ['grad1', 'grad2', 'grad3', 'grad4'].map(id => document.getElementById(id));

  // Give each element its own independent motion parameters
  const params = elements.map(() => ({
    // random starting phase so they don't move in sync
    angle1: Math.random() * Math.PI * 2,
    angle2: Math.random() * Math.PI * 2,
    // random speed for each axis
    speed1: 0.0003 + Math.random() * 0.0004,
    speed2: 0.0002 + Math.random() * 0.0005,
    // how far it wanders, in % of container size
    range1: 10 + Math.random() * 15,
    range2: 10 + Math.random() * 15,
  }));

  // Capture each element's base top/left (from CSS) as the center point
  const bases = elements.map(el => {
    const style = getComputedStyle(el);
    return {
      top: parseFloat(style.top) || 0,
      left: parseFloat(style.left) || 0,
      right: style.right !== 'auto' ? parseFloat(style.right) : null,
    };
  });

  function animate(time) {
    elements.forEach((el, i) => {
      const p = params[i];
      const b = bases[i];

      const offsetX = Math.sin(time * p.speed1 + p.angle1) * p.range1;
      const offsetY = Math.cos(time * p.speed2 + p.angle2) * p.range2;

      el.style.transform = `translate(${offsetX}%, ${offsetY}%)`;
    });

    requestAnimationFrame(animate);
  }

  requestAnimationFrame(animate);
})();