<svg
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  width="200"
  height="200"
  viewBox="0 0 1000 1000"
  style="background: #111;"
  role="img"
  aria-labelledby="solarSystemTitle"
>
  <title id="solarSystemTitle">Generative Star Field Filter Effects</title>

  <!-- This element will store our graphics -->
  <g class="js-svg-wrapper"></g>
  
  <!-- This stores any global CSS styles -->
  <style>
    /* Storing values as custom properties will make them easier to change later */
    :root {
      --start-rotation: 0deg;
      --rotation-speed: 10s;
    }

    /* Set up an animation to rotate from 0 to 360 degrees */
    @keyframes orbit {
      from {
        transform: rotate(var(--start-rotation));
      }
      to {
        transform: rotate(calc(var(--start-rotation) + 360deg));
      }
    }
    .planet {
      /* Apply our animation to the planets */
      animation: orbit var(--rotation-speed) infinite linear;
      /* Within an SVG, the transform-origin is set relative to the SVG */
      /* This ensures our orbit will rotate around the center of our star */
      transform-origin: 50% 50%;
    }
  </style>
</svg>

<button class="js-refresh-button">Refresh</button>

/**
 * Code related to setting up the codepen, not the solar systems
 */

* {
  margin: 0;
}

body {
  display: flex;
  flex-direction: column;
  align-items: center;
}

svg {
  width: min(100vw, calc(100vh - 4.5em));
  height: auto;
}

button {
  margin-top: 1em;
  appearance: none;
}

:root {
  --hue: 200;
}

button {
  /* Text Colors */
  --text-saturation: 90%;
  --text-lightness: 40%;
  
  --text-saturation-hover: calc(var(--text-saturation) + 10%);
  --text-lightness-hover: calc(var(--text-lightness) - 5%); ;
  
  --text-saturation-active: var(--text-saturation-hover);
  --text-lightness-active: calc(var(--text-lightness) - 10%); ;
  
  --text-saturation-disabled: calc(var(--text-saturation) - 60%);
  --text-lightness-disabled: calc(var(--text-lightness) + 10%);
  
  /* Background Colors */
  --background-saturation: 0%;
  --background-lightness: 100%;
  
  --background-saturation-hover: calc(var(--background-saturation) + 80%);
  --background-lightness-hover: calc(var(--background-lightness) - 5%);
  
  --background-saturation-active: var(--background-saturation-hover);
  --background-lightness-active: calc(var(--background-lightness) - 10%);
  
  --background-saturation-disabled: calc(var(--background-saturation) + 30%);
  --background-lightness-disabled: calc(var(--background-lightness) - 10%);
  
  /* Border Colors */
  --border-saturation: 90%;
  --border-lightness: 60%;
  
  --border-saturation-hover: calc(var(--border-saturation) + 10%);
  --border-lightness-hover: calc(var(--border-lightness) - 10%);
  
  --border-saturation-active: var(--border-saturation-hover);
  --border-lightness-active: calc(var(--border-lightness) - 20%);
  
  --border-saturation-disabled: calc(var(--border-saturation) - 60%);
  --border-lightness-disabled: calc(var(--border-lightness) + 20%);
  
  /* Focus shadow styles */
  --shadow-saturation-focus: 100%;
  --shadow-lightness-focus: 85%;
  
  /* Color Styles */
  color: hsl(var(--hue), var(--text-saturation), var(--text-lightness));
  background-color: hsl(var(--hue), var(--background-saturation), var(--background-lightness)); 
  border:0.1em solid hsl(var(--hue), var(--border-saturation), var(--border-lightness)); 
  
  /* Misc. Styles */
  border-radius: 0.25em;
  cursor: pointer;
  display: inline-block;
  font-size: 1em;
  padding: 0.5em 1em;
  transition-property: box-shadow, background-color, border-color, color;
  transition-timing-function: ease-out;
  transition-duration: 0.2s;
}

button:hover {
  color: hsl(
    var(--hue), 
    var(--text-saturation-hover), 
    var(--text-lightness-hover)
  );
  
  background-color: hsl(
    var(--hue), 
    var(--background-saturation-hover), 
    var(--background-lightness-hover)
  );
  
  border-color: hsl(
    var(--hue), 
    var(--border-saturation-hover), 
    var(--border-lightness-hover)
  );
}

button:active {
  color: hsl(
    var(--hue), 
    var(--text-saturation-active), 
    var(--text-lightness-active)
  );
  
  background-color: hsl(
    var(--hue), 
    var(--background-saturation-active), 
    var(--background-lightness-active)
  );
  
  border-color: hsl(
    var(--hue), 
    var(--border-saturation-active), 
    var(--border-lightness-active)
  );
}

button:focus {
  outline: none;
  box-shadow: 0 0 0 0.25em hsl(
    var(--hue), 
    var(--shadow-saturation-focus),
    var(--shadow-lightness-focus)
  );
}

body {
  font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, Ubuntu, roboto, noto, segoe ui, arial, sans-serif; 
}
// Define a couple variables about our SVG grid
const width = 1000;
const height = 1000;

function draw() {
  let starSize = randomInt(70, 120);
  let markup = drawStarField();
  
  document.querySelector(".js-svg-wrapper").innerHTML = markup;
}

function drawStarField() {
  const id = `starField`;
  const seed = randomInt(0, 100);
  
  return `
      ${filters({id, seed})}
      
      ${rectangles({id, isMain: true})}
      ${rectangles({id, isMain: false})}
  `;
};

const availHeight = height * 9/10;
const startHeight = height * 1/10;

function filters({id, seed}) {
  const y = height/2 - 150;
  
  return `
    <defs>
      <radialGradient id="${id}-gradient-1">
        <stop offset="0%" stop-color="hsla(${random(190, 230)}, 100%, 20%, 1)" />
        <stop offset="100%" stop-color="hsla(${random(290, 360)}, 100%, 20%, 1)" />
      </radialGradient>

      <radialGradient id="${id}-gradient-2">
        <stop offset="0%" stop-color="hsla(${random(190, 230)}, 100%, 20%, 1)" />
        <stop offset="100%" stop-color="hsla(${random(290, 360)}, 100%, 20%, 1)" />
      </radialGradient>
      <filter id="${id}-bg">
        <feTurbulence type="fractalNoise" baseFrequency="0.01" seed="${seed}"/> 
        <feGaussianBlur stdDeviation="10"/>  
        <feComposite operator="in" in2="SourceGraphic"/>     
      </filter>
      <filter id="${id}-stars">
        <feTurbulence baseFrequency="0.2" seed="${seed}"/>
        <feColorMatrix values="0 0 0 9 -4
                              0 0 0 9 -4
                              0 0 0 9 -4
                              0 0 0 0 0.5"/>
        <feComposite operator="in" in2="SourceGraphic"/>
      </filter>
      <clipPath id="${id}-clip-main">
        <rect width="300" height="300" x="${550}" y="${y}" filter="url(#${id}-bg)" />
      </clipPath>
      <clipPath id="${id}-clip-1">
        <rect width="300" height="200" x="${150}" y="${startHeight}" filter="url(#${id}-bg)"/>
      </clipPath>
      <clipPath id="${id}-clip-2">
        <rect width="300" height="200" x="${150}" y="${availHeight / 5 + startHeight}" filter="url(#${id}-bg)" />
      </clipPath>
    </defs>
  `;
}

function rectangles({id, isMain}) {
  const x = isMain ? 550 : 150;
  const y = height/2 - 150;
  
  return `
    <rect x="0" y="0" width="100%" height="100%" fill="url(#${id}-gradient-2)" opacity="0.75" clip-path="url(#${id}-clip-${isMain ? 'main' : '1'})"/>
    <rect x="0" y="0" width="100%" height="100%" fill="url(#${id}-gradient-1)" clip-path="url(#${id}-clip-${isMain ? 'main' : '2'})"/>
    <rect x="${x}" y="${isMain ? y : availHeight / 5 * 2 + startHeight}" width="300" height="${isMain ? 300 : 200}" filter="url(#${id}-bg)" opacity="0.3"/>
    <rect x="${x}" y="${isMain ? y : availHeight / 5 * 3 + startHeight}" width="300" height="${isMain ? 300 : 200}" filter="url(#${id}-stars)"/>
   `;
}

/**
 * Initialize our art piece
 */
draw();

document.querySelector(".js-refresh-button").addEventListener("click", draw);

//
// Randomization Functions
//

// Return a number between two values.
function random(min, max) {
  const difference = max - min;
  return min + difference * Math.random();
}

// Returns a random integer between two values
function randomInt(min, max) {
  return Math.round(random(min, max));
}

// Returns true or false. By default the chance is 50/50 but you can pass in
// a custom probability between 0 and 1. (Higher values are more likely to
// return true.)
function randomBool(probability = 0.5) {
  return Math.random() > probability;
}

// Returns a random item from an array
function randomItemInArray(array) {
  return array[randomInt(0, array.length - 1)];
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.