<!DOCTYPE html>
<html>

  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg.js"></script>
  </head>

  <body>
    <svg id="svg" width="600px" height="600px"></svg>
    <script src="script.js"></script>
  </body>
  
</html>
var s = Snap("#svg");

var padding = 10;   // left/top padding
var r = 280;        // radius of big circle
var rotation = 0;   // current rotation (radiants)
var step = 100;
var animSpeed = 20;

// big circle
var bigCircle = s.circle(padding + r, padding + r, r);
bigCircle.attr({ 
  fill: 'white', 
  stroke: 'black', 
  strokeWidth: 3 
});

// bounding circle for smooth animation
var smallCircleBounding = s.circle(padding + r, padding + r/2, r/2 + 8);
smallCircleBounding.attr({ fill: 'none', stroke: 'none' });

// small, rotating circle with radius = r/2
var smallCircle = s.circle(padding + r, padding + r/2, r/2);
smallCircle.attr({ 
  fill: 'white', 
  stroke: 'gray', 
  strokeWidth: 1, 
});

// dot rotating with small circle
var dot = s.circle(padding + r, padding, 4);
dot.attr({ 
  fill: 'red' 
});

// group rotating circles
var group = s.group(smallCircleBounding, smallCircle, dot);

// line on which the dot moves
var line = s.line(padding + r, padding, padding + r, padding + 2 * r);
line.attr({ 
  stroke: 'lightgray', 
  strokeWidth: 1,
  'stroke-dasharray': '5,5'
});

// timer function 
var timerFunc = function() {
  // increment rotation
  rotation += Math.PI / step;
  if (rotation >= 2 * Math.PI) {
    rotation -= 2 * Math.PI;
  }
  
  // calculate translation of small circle
  var x = Math.cos(Math.PI / 2 - rotation) * r/2;
  var y = r/2 - Math.sin(Math.PI / 2 - rotation) * r/2;

  // transform circle and dot
  group.animate(
    { transform: 'T' + Math.round(x) +',' + Math.round(y) + 'R' + (-1) * rotation / Math.PI / 2 * 360 }, 
    animSpeed,
    timerFunc);
};

// schedule first animation step
timerFunc();