Search code examples
algorithmcanvaspathhtml5-canvas

Randomly Generating Curved/Wavy Paths


I have a massive image of a map that is much larger than the viewport and centered in the viewport, which can be explored by the user by dragging the screen. In order to create a parallax effect, I used a massive image of clouds in the foreground. As the user explores the map via dragging, both the background and foreground move in a parallax fashion. So far, so good.

However, what I really want to do is give the image of clouds a "default" movement that would be randomly generated on each page load, so that the clouds would always be moving, even if the user is not dragging. I know this can be done by animating the foreground along a path, but I am not exactly sure how to go about this.

How can I randomly generate irregularly curved or wavy paths on each page load?

Does anybody know of any algorithms that can do this?


Solution

  • I also use a copy of the previous answers to realize a simplified version of what I hinted at in the comments.

    Use a random walk on the unit circle, that is on the angle, to determine a velocity vector that slowly but randomly changes and move forward using cubic Bezier patches.

    var c = document.getElementById("c");
    var ctx = c.getContext("2d");
    var cw = c.width = 600;
    var ch = c.height = 400;
    var cx = cw / 4, cy = ch / 2;
    
    var angVel = v.value;
    var tension = t.value;
    ctx.lineWidth = 4;
    
    var npts = 60;
    var dw = Array();
    var xs = Array();
    var ys = Array();
    var vxs = Array();
    var vys = Array();
    
    function Randomize() {
        for (var i = 0; i < npts; i++) {
            dw[i] = (2*Math.random()-1);
        }
    }
    
    function ComputePath() {
        xs[0]=cx; ys[0]=cy; 
        var angle = 0;
        for (var i = 0; i < npts; i++) {
            vxs[i]=10*Math.cos(2*Math.PI*angle);
            vys[i]=10*Math.sin(2*Math.PI*angle);
            angle = angle + dw[i]*angVel;
        }
        for (var i = 1; i < npts; i++) {
            xs[i] = xs[i-1]+3*(vxs[i-1]+vxs[i])/2; 
            ys[i] = ys[i-1]+3*(vys[i-1]+vys[i])/2;
        }
    }
    
    function Draw() {
      ctx.clearRect(0, 0, cw, ch);
      ctx.beginPath();
      ctx.moveTo(xs[0],ys[0]); 
      for (var i = 1; i < npts; i++) {
        var cp1x = xs[i-1]+tension*vxs[i-1];
        var cp1y = ys[i-1]+tension*vys[i-1];
        var cp2x = xs[i]-tension*vxs[i];
        var cp2y = ys[i]-tension*vys[i]
        ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, xs[i], ys[i]); 
      }
      ctx.stroke();
    }
    Randomize();
    ComputePath();
    Draw();
    
    r.addEventListener("click",()=>{
      Randomize();
      ComputePath();
      Draw();
    })
    
    v.addEventListener("input",()=>{
      angVel = v.value;
      vlabel.innerHTML = ""+angVel;
      ComputePath();
      Draw();
    })
    
    t.addEventListener("input",()=>{
      tension = t.value;
      tlabel.innerHTML = ""+tension;
      Draw();
    })
    canvas{border:1px solid}
    <canvas id = 'c'></canvas>
    <table>
      <tr><td>angular velocity:</td><td> <input type="range" id="v" min ="0" max = "0.5" step = "0.01" value="0.2" /></td><td id="vlabel"></td></tr>
      <tr><td>tension</td><td> <input type="range" id="t" min ="0" max = "1" step = "0.1" value="0.8" /></td><td id="tlabel"></td></tr>
      <tr><td>remix</td><td> <button id="r"> + </button></td><td></td></tr>
    </table>