<svg id="mySVG" width="340" height="340">
<!--   <circle cx="50%" cy="50%" r="135" fill="none" stroke="#666" stroke-width="1" /> -->
</svg>
body {
  background: #f2f2f2;
}

svg {
  overflow: visible !important;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: auto;
  // outline: 1px dotted #666;
  animation: rotate 20s linear infinite;
}

path {
  stroke: #0D0D0D;
  stroke-width: 3px;
  fill: none;
}

@keyframes rotate {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(-360deg);
  }
}
View Compiled
////////////////////////////////////////////
//   You can try modifying these params   //
////////////////////////////////////////////

var cx = 170;          // center x
var cy = 170;          // center y
var r = 135;           // radius
var wn = 15;           // number of waves
var wa = 10;            // number of active waves
var wh = 15;           // max height of a wave
var duration = 2;      // duration of one cycle in seconds
var curviness = 1.5;   // kurvatura :)

////////////////////////////////////////////



function segmentToString(x, y) {
  return [x.b.toFixed(2), y.b.toFixed(2), x.c.toFixed(2), y.c.toFixed(2), x.d.toFixed(2), y.d.toFixed(2)].join(",");
}

function arrayRotate(arr, count) {
  count -= arr.length * Math.floor(count / arr.length)
  arr.push.apply(arr, arr.splice(0, count))
  return arr
}

function getCurve(phase, inverse) {
  inverse = inverse ? -1 : 1;

  var step = 1 / Math.ceil(wa/2);
  var steps = new Array(wn).fill(0);
  
  for (let i = 0, diff = 0; i < Math.ceil(wa/2); i++) {
    diff += step;
    steps[i] = Sine.easeInOut.getRatio(diff) * wh;
  }

  for (let i = wa - 1, diff = 0; i > Math.ceil(wa/2) - 1; i--) {
    diff += step;
    steps[i] = Sine.easeInOut.getRatio(diff) * wh;
  }
  
  var steps = arrayRotate(steps, phase);
  
  var values = [];
  steps.forEach((step, i) => {
    var a = cx + (r + (i%2 ? -step : step ) * inverse) * Math.cos(2 * Math.PI * (i*2) / (wn*2));
    var b = cy + (r + (i%2 ? -step : step ) * inverse) * Math.sin(2 * Math.PI * (i*2) / (wn*2));   
    var c = cx + r * Math.cos(2 * Math.PI * (i*2 + 1) / (wn*2));
    var d = cy + r * Math.sin(2 * Math.PI * (i*2 + 1) / (wn*2));

    values.push({x: a, y: b}, {x: c, y: d});
  });
  values.push(values[0]);
  
  var data = BezierPlugin.bezierThrough(values, curviness);
  var d = "M" + data.x[0].a + "," + data.y[0].a + " C" + segmentToString(data.x[0], data.y[0]);
  for (var i = 1; i < data.x.length; i++) {
    d += "," + segmentToString(data.x[i], data.y[i]);
  }
  
  return d;  
}



var svgNS = "http://www.w3.org/2000/svg";
var path1 = document.createElementNS(svgNS, "path");
var path2 = document.createElementNS(svgNS, "path");
TweenMax.set(path1, {
  attr: {
    "d": getCurve(0),
    // "stroke": "#f00",
    "id": "svg-preloader-wave-1"
  }
});
TweenMax.set(path2, {
  attr: {
    "d": getCurve(0, true),
    // "stroke": "#fff",
    "id": "svg-preloader-wave-2"
  }
});
document.getElementById("mySVG").appendChild(path1);
document.getElementById("mySVG").appendChild(path2);



var phase = 0;
var timeline = new TimelineMax({
  repeat: -1
});
for (var i = 0; i < wn; i++) {
  phase--;
  
  timeline.to("#svg-preloader-wave-1", (duration / wn), {
    attr: {d: getCurve(phase)},
    ease: Power0.easeNone
  });

  timeline.to("#svg-preloader-wave-2", (duration / wn), {
    attr: {d: getCurve(phase, true)},
    ease: Power0.easeNone
  }, "-=" + (duration / wn));
}
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.2/TweenMax.min.js