#fps
#entry-point
View Compiled
$background: #000;
$font-color: #fff;

body {
  background: $background;
}

#fps {
  position: absolute;
  color: $font-color;
  top: 5px;
  left: 5px;
}

#entry-point {
  position: relative;
  width: 500px;
  height: 500px;
  left: 0;
  right: 0;
  margin: 0 auto 0 auto;
  top: 50vh;
  transform: translateY(-50%);
  
  svg {
    width: 500px;
    height: 500px;
    background: $background;
  }
}
View Compiled
const Circle = ({ attr: { r, fill, stroke, strokeWidth }, position }) => (
  <circle
    r={r}
    fill={fill === undefined ? 'none' : fill}
    stroke={stroke === undefined ? 'none' : stroke}
    strokeWidth={2}
    transform={`translate(${position[0]}, ${position[1]})`}
  />
);

const Path = ({ attr: { path, fill, stroke, strokeWidth }, position, phi }) => (
  <path
    d={path}
    fill={fill === undefined ? 'none' : fill}
    stroke={stroke === undefined ? 'none' : stroke}
    strokeWidth={2}
    transform={`rotate(${phi}, ${position[0]}, ${position[1]}) translate(${position[0]}, ${position[1]})`}
  />
);

const Teardrop = ({ position, phi, a, b, m }) => {
  let path = 'M 0 0'
  for (let i = 0; i <= 360; i++) {
    const rad = i * Math.PI / 180;
    const x = a * Math.cos(rad);
    const y = b * Math.sin(rad) * Math.pow(Math.sin(rad/2), m);
    path += ` L ${x} ${y}`;
  }
  return (
    <Path
      attr={{ path, stroke: mandalaStroke, fill: mandalaFill }}
      position={position}
      phi={phi}
    />
  );
}

const Group = ({ position, phi, children }) => (
  <g
    transform={`rotate(${phi}, ${position[0]}, ${position[1]}) translate(${position[0]}, ${position[1]})`}
  >
    {children}
  </g>
);

const mandalaFill = '#382D7B';
const mandalaStroke = '#9370DB';

const Mandala = ({ theta }) => (
  <Group
    position={[500, 500]}
    phi={theta / 50}
  >
    <Circle
      phi={0}
      position={[0, 0]}
      attr={{ r: 500, fill: mandalaFill, stroke: mandalaStroke }}
    />
    <Circle
      phi={0}
      position={[0, 0]}
      attr={{ r: 480, fill: mandalaFill, stroke: mandalaStroke }}
    />
    {Array.apply(null, Array(24)).map(() => {}).map((elem, i) => (
      <Teardrop
        phi={15 * i + 180}
        position={
          [400 * Math.cos(Math.PI * i / 12), 400 * Math.sin(Math.PI * i / 12)]
        }
        m={5}
        a={80}
        b={40 + 10 * Math.cos(theta / 600)}
      />
    ))}
    {Array.apply(null, Array(6)).map(() => {}).map((elem, i) => (
      <Group
        phi={0}
        position={
          [467.5 * Math.cos(Math.PI * i / 3), 467.5 * Math.sin(Math.PI * i / 3)]
        }
      >
        <Circle
          phi={0}
          position={[0, 0]}
          attr={{ r: 23 + 5 * Math.cos(theta / 300), fill: mandalaFill, stroke: mandalaStroke }}
        />
        <Circle
          phi={0}
          position={[0, 0]}
          attr={{ r: 10 + 7 * Math.cos(theta / 300), fill: mandalaFill, stroke: mandalaStroke }}
        />
      </Group>
    ))}
    <Circle
      phi={0}
      position={[0, 0]}
      attr={{ r: 435, fill: mandalaFill, stroke: mandalaStroke }}
    />
    {Array.apply(null, Array(48)).map(() => {}).map((elem, i) => (
      <Group
        phi={0}
        position={[0, 0]}
      >
        <Path
          position={[0, 0]}
          phi={0}
          attr={{
            path: `M 0 0 L ${435 * Math.cos(Math.PI * i / 24)} ${435 * Math.sin(Math.PI * i / 24)}`,
            stroke: mandalaStroke
          }}
        />
        <Circle
          position={
            [417 * Math.cos(Math.PI * (i + 0.5) / 24), 417 * Math.sin(Math.PI * (i + 0.5) /24)]
          }
          phi={0}
          attr={{ r: 5 + 3 * Math.cos(Math.PI * i / 9 + theta / 1e4), fill: mandalaFill, stroke: mandalaStroke }}
        />
      </Group>
    ))}
    <Circle
      phi={0}
      position={[0, 0]}
      attr={{ r: 400, fill: mandalaFill, stroke: mandalaStroke }}
    />
    {Array.apply(null, Array(6)).map(() => {}).map((elem, i) => (
      <Group
        position={
          [280 * Math.cos(Math.PI * (i + 0.5) / 3), 280 * Math.sin(Math.PI * (i + 0.5) / 3)]
        }
        phi={-theta / 50}
      >
        <Circle
          phi={0}
          position={[0, 0]}
          attr={{ r: 115, fill: mandalaFill, stroke: mandalaStroke }}
        />
        <Circle
          phi={0}
          position={[0, 0]}
          attr={{ r: 100, fill: mandalaFill, stroke: mandalaStroke }}
        />
        {Array.apply(null, Array(12)).map(() => {}).map((elem, i) => (
          <Teardrop
            position={
              [30 * Math.cos(Math.PI * i / 6), 30 * Math.sin(Math.PI * i / 6)]
            }
            phi={30 * i}
            m={2}
            a={60}
            b={80}
          />
        ))}
        <Circle
          phi={0}
          position={[0, 0]}
          attr={{ r: 60 + 5 * Math.cos(theta / 300), fill: mandalaFill, stroke: mandalaStroke }}
        />
        <Circle
          phi={0}
          position={[0, 0]}
          attr={{ r: 40, fill: mandalaFill, stroke: mandalaStroke }}
        />
      </Group>
    ))}
    {Array.apply(null, Array(6)).map(() => {}).map((elem, i) => (
      <Group
        phi={60 * i}
        position={
          [280 * Math.cos(Math.PI * i / 3), 280 * Math.sin(Math.PI * i / 3)]
        }
      >
        <Teardrop
          position={[0, 0]}
          phi={0}
          a={200}
          b={240}
          m={2}
        />
        <Teardrop
          position={[0, 0]}
          phi={0}
          a={160}
          b={200}
          m={2}
        />
        <Circle
          attr={{ r: 80, fill: mandalaFill, stroke: mandalaStroke }}
          position={[80, 0]}
          phi={0}
        />
        <Circle
          attr={{ r: 70, fill: mandalaFill, stroke: mandalaStroke }}
          position={[80, 0]}
          phi={0}
        />
        <Group
          phi={theta / 25}
          position={[80, 0]}
        >
          {Array.apply(null, Array(6)).map(() => {}).map((elem, j) => (
            <Teardrop
              position={
                [-20 * Math.cos(Math.PI * (j + 0.5) / 3), -20 * Math.sin(Math.PI * (j + 0.5) / 3)]
              }
              phi={60 * j + 30}
              m={5}
              a={30}
              b={30}
            />
          ))}
          {Array.apply(null, Array(6)).map(() => {}).map((elem, j) => (
            <Teardrop
              position={
                [50 * Math.cos(Math.PI * j / 3), 50 * Math.sin(Math.PI * j / 3)]
              }
              phi={60 * j}
              m={5}
              a={35}
              b={35}
            />
          ))}
          {Array.apply(null, Array(6)).map(() => {}).map((elem, j) => (
            <Circle
              position={
                [70 * Math.cos(Math.PI * j / 3), 70 * Math.sin(Math.PI * j / 3)]
              }
              phi={0}
              attr={{ r: 10, fill: mandalaFill, stroke: mandalaStroke }}
            />
          ))}
          <Circle
            phi={0}
            position={[0, 0]}
            attr={{ r: 30, fill: mandalaFill, stroke: mandalaStroke }}
          />
          <Circle
            phi={0}
            position={[0, 0]}
            attr={{ r: 18 + 2 * Math.cos(theta / 300), fill: mandalaFill, stroke: mandalaStroke }}
          />
        </Group>
      </Group>
    ))}
    <Circle
      attr={{ r: 290, stroke: mandalaStroke, fill: mandalaFill }}
      position={[0, 0]}
      phi={0}
    />
    {Array.apply(null, Array(24)).map(() => {}).map((elem, i) => (
      <Circle
        attr={{ r: 30, stroke: mandalaStroke, fill: mandalaFill }}
        position={
          [260 * Math.cos(Math.PI * i /12), 260 * Math.sin(Math.PI * i / 12)]
        }
      />
    ))}
    <Circle
      attr={{ r: 260, stroke: mandalaStroke, fill: mandalaFill }}
      position={[0, 0]}
      phi={0}
    />
    {Array.apply(null, Array(6)).map(() => {}).map((elem, i) => (
      <Group
        phi={-theta / 50}
        position={
          [150 * Math.cos(Math.PI * i / 3), 150 * Math.sin(Math.PI * i / 3)]
        }
      >
        <Circle
          attr={{ r: 100, stroke: mandalaStroke, fill: mandalaFill }}
          position={[0, 0]}
          phi={0}
        />
        {Array.apply(null, Array(24)).map(() => {}).map((elem, j) => {
          const path = `M 0 0 L ${100 * Math.cos(Math.PI * j / 12)} ${100 * Math.sin(Math.PI * j / 12)}`;
          return (
            <Path
              attr={{ path, stroke: mandalaStroke }}
              position={[0, 0]}
              phi={0}
            />
          );
        })}
      </Group>
    ))}
    {Array.apply(null, Array(6)).map(() => {}).map((elem, i) => (
      <Teardrop 
        position={
          [220 * Math.cos(Math.PI * (i + 0.5) / 3), 220 * Math.sin(Math.PI * (i + 0.5) / 3)]
        }
        phi={60 * i + 30}
        a={80}
        b={200}
        m={5}
       />
    ))}
    {Array.apply(null, Array(6)).map(() => {}).map((elem, i) => (
      <Group
        phi={theta / 25}
        position={
          [190 * Math.cos(Math.PI * (i + 0.5) / 3), 190 * Math.sin(Math.PI * (i + 0.5) / 3)]
        }
      >
        <Circle
          attr={{ r: 45 + 5 * Math.cos(theta / 300), stroke: mandalaStroke, fill: mandalaFill }}
          position={[0, 0]}
          phi={0}
        />
        <Circle
          attr={{ r: 28, stroke: mandalaStroke, fill: mandalaFill }}
          position={[0, 0]}
          phi={0}
        />
        {Array.apply(null, Array(3)).map(() => {}).map((elem, i) => (
          <Circle
            attr={{ r: 8, stroke: mandalaStroke, fill: mandalaFill }}
            position={
              [12 * Math.cos(2 * Math.PI * i / 3), 12 * Math.sin(2 * Math.PI * i /3)]
            }
          />
        ))}
      </Group>
    ))}
    {Array.apply(null, Array(6)).map(() => {}).map((elem, i) => (
      <Teardrop 
        position={
          [200 * Math.cos(Math.PI * i / 3), 200 * Math.sin(Math.PI * i / 3)]
        }
        phi={60 * i}
        a={120}
        b={180}
        m={6}
       />
    ))}
    {Array.apply(null, Array(6)).map(() => {}).map((elem, i) => (
      <Teardrop 
        position={
          [180 * Math.cos(Math.PI * i / 3), 180 * Math.sin(Math.PI * i / 3)]
        }
        phi={60 * i}
        a={60}
        b={90}
        m={6}
       />
    ))}
    {Array.apply(null, Array(6)).map(() => {}).map((elem, i) => (
      <Circle
        attr={{ r: 15, stroke: mandalaStroke, fill: mandalaFill }}
        position={
          [280 * Math.cos(Math.PI * i / 3), 280 * Math.sin(Math.PI * i / 3)]
        }
      />
    ))}
    {Array.apply(null, Array(6)).map(() => {}).map((elem, i) => (
      <Circle
        attr={{ r: 8 + 2 * Math.cos(theta / 300), stroke: mandalaStroke, fill: mandalaFill }}
        position={
          [310 * Math.cos(Math.PI * i / 3), 310 * Math.sin(Math.PI * i / 3)]
        }
      />
    ))}
    {Array.apply(null, Array(12)).map(() => {}).map((elem, i) => (
      <Circle
        attr={{ r: 30, stroke: mandalaStroke, fill: mandalaFill }}
        position={
          [120 * Math.cos(Math.PI * i / 6), 120 * Math.sin(Math.PI * i / 6)]
        }
      />
    ))}
    {Array.apply(null, Array(12)).map(() => {}).map((elem, i) => (
      <Circle
        attr={{ r: 20 + 2 * -Math.cos(theta / 600), stroke: mandalaStroke, fill: mandalaFill }}
        position={
          [120 * Math.cos(Math.PI * i / 6), 120 * Math.sin(Math.PI * i / 6)]
        }
      />
    ))}
    <Circle
      attr={{ r: 120, stroke: mandalaStroke, fill: mandalaFill }}
      position={[0, 0]}
    />
    <Circle
      attr={{ r: 100 + 5 * Math.cos(theta / 600), stroke: mandalaStroke, fill: mandalaFill }}
      position={[0, 0]}
    />
    <Group
      position={[0, 0]}
      phi={-theta / 50}
    >
      {Array.apply(null, Array(6)).map(() => {}).map((elem, i) => (
        <Teardrop 
          position={
            [70 * Math.cos(Math.PI * (i + 0.5) / 3), 70 * Math.sin(Math.PI * (i + 0.5) / 3)]
          }
          phi={60 * i + 30}
          a={30}
          b={60}
          m={5}
         />
      ))}
      {Array.apply(null, Array(6)).map(() => {}).map((elem, i) => (
        <Circle
          position={
            [80 * Math.cos(Math.PI * i / 3), 80 * Math.sin(Math.PI * i / 3)]
          }
          phi={60 * i}
          attr={{ r: 7, fill: mandalaFill, stroke: mandalaStroke }}
         />
      ))}
    </Group>
    {Array.apply(null, Array(6)).map(() => {}).map((elem, i) => (
      <Teardrop 
        position={
          [80 * Math.cos(Math.PI * i / 3), 80 * Math.sin(Math.PI * i / 3)]
        }
        phi={60 * i}
        a={40}
        b={70}
        m={5}
       />
    ))}
    <Circle
      attr={{ r: 50, stroke: mandalaStroke, fill: mandalaFill }}
      position={[0, 0]}
    />
    {Array.apply(null, Array(12)).map(() => {}).map((elem, i) => (
      <Path
        attr={{ path: 'M 0 0 L 35 35', stroke: mandalaStroke, fill: mandalaFill }}
        position={[0, 0]}
        phi={i * 30}
      />
    ))}
    <Circle
      attr={{ r: 32 + 2 * Math.cos(theta / 400), stroke: mandalaStroke, fill: mandalaFill }}
      position={[0, 0]}
    />
    <Circle
      attr={{ r: 20, stroke: mandalaStroke, fill: mandalaFill }}
      position={[0, 0]}
    />
  </Group>
);

const App = ({ theta, children }) => (
  <svg
    viewBox="0 0 1000 1000"
  >
    <Circle
      attr={{ r: 499, stroke: '#333' }}
      position={[500, 500]}
    />
    <Mandala theta={theta} />
  </svg>
);

const beginning = Date.now();
let then = Date.now();

function renderApp() {
  const now = Date.now() - beginning;
  ReactDOM.render(
    (<App theta={now} />),
    document.getElementById('entry-point')
  );
  ReactDOM.render(
    (<div>{`${Math.round(1e4 / (now - then)) / 10} fps`}</div>),
    document.getElementById('fps')
  );
  then = now;
  window.requestAnimationFrame(renderApp);
}

renderApp();
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.min.js
  4. https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.6/react-redux.min.js