<div class="spinner"></div>
//Edit these
$small: 9; //Number of small dots
$smallsize:10%;
$big:3; // Number of big dots
$bigsize:15%;

//Not these
$radius:50%-$bigsize;
$smalldots: ();
$smalldotpositions: ();
$smalldotsizes: ();
$as:360deg/$small;
$bigdots: ();
$bigdotpositions:();
$bigdotsizes: ();
$ab:360deg/$big;

:root {
	--dotcolor:50, 49, 51;
	--dot: radial-gradient(rgb(var(--dotcolor)) 67%, rgba(var(--dotcolor), 0) 68%);
}
/*
	For small and big construct:
	• List of radial-gradients/var(--dot) [all the same, can be different for different colours] => background-image
	• List of positions, using trig to go around a radius => background-position
	• List of sizes [all the same, can be different for different sizes] => background-size
	Also background-repeat: no-repeat; and background-color:white; are vital
*/
@for $i from 0 to $small {
	$smalldots: $smalldots, var(--dot);
	$smalldotpositions: $smalldotpositions, (50% + (($radius+$smallsize/2)*sin($as*$i))) (50% + (($radius+$smallsize/2)*cos($as*$i)));
	$smalldotsizes: $smalldotsizes, $smallsize $smallsize;
}
@for $i from 0 to $big {
	$bigdots: $bigdots, var(--dot);
	$bigdotpositions: $bigdotpositions, (50% + (($radius+$bigsize/2)*sin($ab*$i))) (50% + (($radius+$bigsize/2)*cos($ab*$i)));
	$bigdotsizes: $bigdotsizes, $bigsize $bigsize;
}

body {
	display:grid;
	place-items:center;
	min-height:100vh;
	overflow-x:hidden;
	.spinner {
		position: relative;
		width:50vmin;
		height:50vmin;
		filter: blur(1vmin) contrast(12); //Blur so the dots overlap, then use contrast to make the overlapping blurred parts solid.
		&:before, &:after {
			display:block;
			content: '';
			position: absolute;
			width:100%;
			height:100%;
			transform-origin:50% 50%;
			background-color:white;
			background-repeat:no-repeat;
			animation:spin linear infinite;
			mix-blend-mode:multiply; //:after has a white background, to make it show through use multiply so the black becomes visible
		}
		&:before {
			background-image:$smalldots;
			background-position:$smalldotpositions;
			background-size:$smalldotsizes;
			animation-duration:10s;
		}
		&:after {
			background-image:$bigdots;
			background-position:$bigdotpositions;
			background-size:$bigdotsizes;
			animation-duration:5s;
		}
	}
}

@keyframes spin {
	0% {
		transform:rotate(0deg);
	}
	100% {
		transform:rotate(360deg);
	}
}
View Compiled

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css
  2. https://i.koya.io/trig.scss

External JavaScript

  1. https://codepen.io/z-/pen/a8e37caf2a04602ea5815e5acedab458.js