<div id="container"></div>
body {
	margin: 0;
	background-color: #202020;
}

#container {
	display: flex;
	justify-content: center;
	align-items: center;
	height: 100vh;
}
import { SvJs, Gen } from 'https://cdn.jsdelivr.net/npm/svjs@latest/dist/svjs.min.js';

// Parent SVG.
const svg = new SvJs().addTo(document.getElementById('container'));

// Viewport and viewBox (1:1 aspect ratio).
const svgSize = Math.min(window.innerWidth, window.innerHeight);
svg.set({ width: svgSize, height: svgSize, viewBox: '0 0 1000 1000' });

// Background.
svg.create('rect').set({
	x: 0, y: 0, width: 1000, height: 1000, fill: '#181818'
});

// Randomise some variables.
let numCircles = 35;//Gen.random(20, 35);
let baseRadius = Gen.random(5, 25, true);
let hue = Gen.random(0, 360);

let animations = [];
let isPaused = false;

// Arrange and animate the circles.
for (let i = 0; i < numCircles; i += 1) {

  // Create the circle, but don't set the position yet.
  let circle = svg.create('circle').set({
    r: baseRadius, cx: 500, cy: 500,
    fill: 'none',
    stroke: `hsl(${hue} 80% 80% / 0.75)`,
    transform_origin: '500 500'
  });

  // Calculate the current angle. 
  let angle = Math.PI * 2 / numCircles * i;

  // Get the sine and cosine of the angle.
  let sin = Math.sin(angle);
  let cos = Math.cos(angle);

  // Map the sine and cosine to the desired range.
  let cx = Gen.map(sin, -1, 1, 150, 850, false);
  let cy = Gen.map(cos, -1, 1, 150, 850, false);

  // Set the initial and target radii.
  let r1 = baseRadius * 2 + (i * 10);
  let r2 = baseRadius / (i + 10);

  // Move from (500, 500) to (cx, cy), reduce the radius, rotate.
  animations.push(circle.element.animate({
    cx: [500, cx, 500],
    cy: [500, cy, 500],
    r: [r1, r2, r1],
    transform: ['rotate(0deg)', 'rotate(360deg)']
  }, {
    duration: 10000,
    iterations: Infinity,
    easing: ['ease-in-out']
  }));

  // Increment the hue.
  hue = (hue % 360) + (180 / numCircles);
}

// Save the root svg as a downloadable file.
document.addEventListener('keydown', (event) => {
  let key = event.key.toLowerCase();
  if (key === 's') svg.save();
});


document.addEventListener('keydown', (event) => {
  let key = event.key.toLowerCase();
  if (key === 'p') {
    animations.forEach((animation) => {
      if (isPaused) {
        animation.pause();
      } else {
        animation.play();
      }
    }); 
    isPaused = !isPaused; 
  }
});

Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.