input#playPause(type='checkbox', name='playPause')

- var algi = 180
.scene
  - for (var c = 0; c < 2; c++) {
  .circle
    - for (var s = 0; s < algi; ++s) {
    .segment
      .facet
    - }
  - }

label(for='playPause')
  - for (var i = 0; i < 7; ++i) {
  span
  - }
View Compiled
$algi: 180; // the number of particles
$floops: 9; // the number of star's arms

/* You can try to change the above - BUT:

1) $floops works with some integers better than others, e.g. 7, 8, 11, 13 won't work.

2) make sure the value of $algi matches the value of - var algi in Pug (currently at 180.

4) Going for more than that might be tough on your CPU */

body {
  margin: 0;
  background-color: #000;
  height: 100vh;
  overflow: hidden;
  display: grid;
  font-size: .8vmin;
}

body *,
body *::before {
  position: absolute;
}

.scene {
  width: 100em;
  aspect-ratio: 1;
  perspective: 50em;
  place-self: center;
  pointer-events: none;
}

body *,
body *::before {
  transform-style: preserve-3d;
}

.circle {
  inset: 0;
  --animDur: 18s;
}

.circle:nth-child(1) {
  --dir: 1;
}

.circle:nth-child(2) {
  --dir: -1;
  animation: roll calc(var(--animDur)*#{$floops/2}) linear infinite;
}


.circle, .segment, label {
  display: grid;
  justify-items: center;
}

.segment {
  --segH: 35em;
  height: var(--segH);
  bottom: 50em;
  rotate: calc(var(--dir)*#{360/$algi}deg*var(--segTurn));
}

.facet {
  height: calc(50em - var(--segH));
  bottom: var(--segH);
  animation: spin var(--animDur) linear infinite;
}

.segment,.facet {
  transform-origin: 50% 100%;
}

.facet::before {
  content: '';
  display: block;
  width: 4.5em;
  aspect-ratio: 1;
  border-radius: 50%;
  background-image: radial-gradient(at 30% 30%, #fff, hsl(calc(60deg*(var(--hue) - 1)) 100% 85%) 10%, hsl(calc(60deg*var(--hue)) 100% 25%) 50%, hsl(calc(60deg*(var(--hue) + .5)) 100% 10%) 66%, hsl(calc(60deg*(var(--hue) + .5)) 100% 5%) 75%);
  box-shadow: inset -1px -1px hsl(calc(60deg*(var(--hue) + 1)) 100% 50%);
  animation: counterSpin var(--animDur) linear infinite, darkness calc(var(--animDur)/2) ease-in-out infinite alternate;
  animation-delay: 0s, calc(-1*var(--animDur)/#{$algi/$floops}*var(--faceTurn) - var(--animDur)*(.5 - var(--dir)/6));
}


/* PLAY/PAUSE TOGGLE */

label {
  --labelW: 30;
  width: calc(var(--labelW)*1em);
  --angle: 360deg/7;
  --pushZ: calc(1/cos(var(--angle)*2 - 90deg)*var(--labelW)/2);
  --spanW: calc(sin(var(--angle)/2)*var(--pushZ)*2);
  aspect-ratio: 2;
  place-self: center;
  rotate: x 30deg;
  transition: all .5s ease-in-out;
}

label:hover {
  scale: 1.15;
}

span {
  --angle: 360deg/7;
  --pushZ: calc(1/cos(var(--angle)*2 - 90deg)*var(--labelW)/2);
  --spanW: calc(sin(var(--angle)/2)*var(--pushZ)*2);
  width: calc(var(--spanW)*1em);
  height: calc(var(--labelW)*.5em);
  display: grid;
  place-items: center;
  transform: rotateY(calc(-30deg*var(--spanStep))) translateZ(calc(var(--pushZ)*-1em));
}

span::before {
  font-size: 15em;
  line-height: 0;
  color: hsl(calc(var(--spanStep)*var(--angle)) 100% 50%);
  backface-visibility: hidden;
  background-image: linear-gradient(90deg, red, yellow);
}

input {
  display: none;
}

#playPause:not(:checked) ~ .scene :is(.circle, .facet),
#playPause:not(:checked) ~ .scene .facet::before {
  animation-play-state: paused;
}

#playPause:not(:checked) ~ label {
  rotate: y 150deg;
}

#playPause:checked ~ label {
  rotate: y 30deg;
}


/* ANIMATION KEYFRAMES */

@keyframes roll {
  0% {
    rotate: calc(#{360/($floops*2)}deg);
  }
  100% {
    rotate: calc(#{360/($floops*2)}deg - 1turn);
  }
}

@keyframes spin {
  0% {
    rotate: x calc(var(--dir)*var(--faceTurn)*#{360/($algi/$floops)}deg);
  }
  100% {
    rotate: x calc( var(--dir)*(var(--faceTurn)*#{360/($algi/$floops)}deg + 1turn) );
  }
}

@keyframes counterSpin {
  0% {
    transform: rotateX(calc(var(--dir)*-1*var(--faceTurn)*#{360/($algi/$floops)}deg));
  }
  100% {
    transform: rotateX(calc( var(--dir)*(-1*var(--faceTurn)*#{360/($algi/$floops)}deg - 1turn)) );
  }
}

@keyframes darkness {
  100% {
    filter: brightness(.15) saturate(2);
  }
}


/* LOOPS / ITERATING */

@for $i from 1 through $algi
  {
    .segment:nth-child(#{$i}) {
      --segTurn: #{$i};
  }
}

@for $i from 1 through $algi/$floops {
  .segment:nth-child(#{$algi/$floops}n+#{$i}) .facet
  {
      --faceTurn: #{$i};
  }
}

@for $i from 1 through 6 {
  .segment:nth-child(6n+#{$i}) .facet
  {
    --hue: #{$i};
  }
}

$letters: S T O P L A Y;

@each $letter in $letters {
  $index: index($letters, $letter);

  span:nth-child(#{$index}) {
    --spanStep: #{$index - 1};
  }

  span:nth-child(#{$index})::before {
  content: "#{$letter}";
  }
}
View Compiled
/* For a step-by-step guide of how to make a 2D version of this, with comments on each line of code, in a tutorial on my blog at:

https://dev.to/mackfitz/particles-spiral-patterns-in-css-part-ii-4bn8

https://mackfitz.hashnode.dev/particles-spiral-patterns-in-css-part-ii */

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.