JSFiddle - React, Tailwind, and code Playground
HTML
<canvas id="c" height="200" width="500"></canvas>
JavaScript
function Wave(canvas, nCircles, nLines, nSteps) {
this.canvas = canvas;
if (this.canvas) {
this.height = this.canvas.height;
this.width = this.canvas.width;
this.context = this.canvas.getContext("2d");
}
this.circles = this.generate(nCircles||4, nLines||7);
this.nSteps = nSteps||400;
this.draw();
}
Wave.prototype = {
generate: function(nCircles, nLines) {
// needs to be power of two plus one, which we get because we use 0..size inclusive
var size = 2 << nLines;
var circles = [];
// generate n circles
for (var i=0; i<nCircles; i++) {
var points = [];
// fill each circle with random walk - try to keep it inside 0..1 to avoid cropping
var min, max;
min = max = points[0] = points[size] = Math.random();
for (var j = size / 2; j >= 1; j = j / 2) {
for (var k = j; k < size; k += 2 * j) {
// crop at 0..1 - if this is terrible, maybe scale instead
points[k] = (points[k - j] + points[k + j]) / 2 + (Math.random() - 0.5)/(size/j);
if (points[k] < min) min = points[k];
if (points[k] > max) max = points[k];
}
}
for (var p=0; p<points.length; p++) {
points[p] = (points[p]-min)*(1/(max-min));
}
circles.push({phase: (Math.PI/nCircles)*i, points: points});
}
return circles;
},
draw: function() {
var xSqueeze = 0.75;
this.context.clearRect(0, 0, this.width, this.height);
this.context.strokeStyle = "black";
this.context.lineJoin = "miter";
// circle 0 is far left, last is far right. Interpolate point between circle n and n+1
for (var i=0; i<this.circles.length-1; i++) {
// find center of each circle
var ox = (i+0.5)*(this.width/this.circles.length),
oy = this.height/2,
circle = this.circles[i];
this.context.beginPath();
for (var o=0; o<circle.points.length; o++) {
var x = ox+xSqueeze*circle.points[o]*Math.min(this.height/2,...