title: SVG - examples | cable with physics (gravity)
body: <h2>Does anybody know a good example for cable-like (or basically any string or rope) behaviour?</h2>
  <p>I am currently writing a semi-modular synthesizer. I am using Raphael.js for the cables. But they seem too stiff. I would like to add some physics, so when I drag one end and the second one is not connected, the other end would hang down and move in a slightly curved way. I guess what I'm looking for is gravity.</p>
  <h3>Right now the cables behave like this:</h3>
  <h2><a href="http://jsfiddle.net/pixelass/4fk2F/" rel="nofollow noreferrer">jsFiddle</a></h2>
  <hr />
  <h3>With this code:</h3>
  <pre><code>window.onload = function() {
      var r = Raphael(&quot;cables&quot;, 1420, 795),
          discattr = {
              fill: &quot;#aaa&quot;,
              stroke: &quot;#ddd&quot;
          };
  
  
  
      function curve(x, y, ax, ay, bx, by, zx, zy, color) {
          var movrattr = {
              fill: color || Raphael.getColor(),
              stroke: &quot;none&quot;
          };
          var path = [
              [&quot;M&quot;, x, y],
              [&quot;C&quot;, ax, ay, bx, by, zx, zy]
          ],
              path2 = [
                  [&quot;M&quot;, x, y],
                  [&quot;L&quot;, ax, ay],
                  [&quot;M&quot;, bx, by],
                  [&quot;L&quot;, zx, zy]
              ],
              curve = r.path(path).attr({
                  stroke: color || Raphael.getColor(),
                  &quot;stroke-width&quot;: 4
              }).shadow(),
              controls = r.set(
                  r.path(path2).attr({
                      stroke: &quot;none&quot;
                  }),
                  r.circle(x, y, 7).attr(discattr).emboss(1).light(-100,-100,30,'#aaaaaa','diffuse'),
                  r.circle(ax, ay, 0).attr({
                      stroke: &quot;none&quot;
                  }),
                  r.circle(bx, by, 0).attr({
                      stroke: &quot;none&quot;
                  }),
                  r.circle(zx, zy, 7).attr(discattr).emboss(1).light(-100,-100,30,'#aaaaaa','diffuse')
              ),
              mover = r.set(
  
                  r.circle(zx, zy + 9, 7).attr(movrattr)
              );
  
          mover[0].move = function(x, y) {
              var X = this.attr(&quot;cx&quot;) + x,
                  Y = this.attr(&quot;cy&quot;) + y;
              this.attr({
                  cx: X,
                  cy: Y
              });
  
              // path[1][5] = X;
              // path[1][6] = Y;
              // path2[3][2] = X;
              // path2[3][3] = Y;
              controls[1].update(x, y);
              controls[4].update(x, y, true);
  
          };
          mover[0].update = function(x, y, dragging) {
              if (!dragging) return false;
              var X = this.attr(&quot;cx&quot;) + x,
                  Y = this.attr(&quot;cy&quot;) + y;
              this.attr({
                  cx: X,
                  cy: Y
              });
  
  
  
          };
          controls[1].update = function(x, y) {
              var X = this.attr(&quot;cx&quot;) + x,
                  Y = this.attr(&quot;cy&quot;) + y;
              this.attr({
                  cx: X,
                  cy: Y
              });
              path[0][3] = X;
              path[0][4] = Y;
              path2[0][4] = X;
              path2[0][5] = Y;
  
  
  
              controls[2].update(x, y);
          };
          controls[2].update = function(x, y) {
              var X = this.attr(&quot;cx&quot;) + x,
                  Y = this.attr(&quot;cy&quot;) + y;
              this.attr({
                  cx: X,
                  cy: Y
              });
              path[1][5] = X;
              path[1][6] = Y;
              path2[1][6] = X;
              path2[1][7] = Y;
              curve.attr({
                  path: path
              });
              controls[0].attr({
                  path: path2
              });
  
          };
          controls[3].update = function(x, y) {
              var X = this.attr(&quot;cx&quot;) + x,
                  Y = this.attr(&quot;cy&quot;) + y;
              this.attr({
                  cx: X,
                  cy: Y
              });
              path[1][3] = X;
              path[1][4] = Y;
              path2[2][7] = X;
              path2[2][8] = Y;
  
              curve.attr({
                  path: path
              });
              controls[0].attr({
                  path: path2
              });
  
          };
  
          controls[4].update = function(x, y, dragging) {
              var X = this.attr(&quot;cx&quot;) + x,
                  Y = this.attr(&quot;cy&quot;) + y;
              this.attr({
                  cx: X,
                  cy: Y
              });
  
              path[1][5] = X;
              path[1][6] = Y;
              path2[3][8] = X;
              path2[3][9] = Y;
              controls[3].update(x, y);
              console.log(dragging)
              if (dragging) return false;
  
              mover[0].update(x, y, 'true');
          };
          controls.drag(move, up);
          mover.drag(drag, up);
      }
  
      function move(dx, dy) {
          this.update(dx - (this.dx || 0), dy - (this.dy || 0));
          this.dx = dx;
          this.dy = dy;
  
      }
  
      function drag(dx, dy) {
          this.move(dx - (this.dx || 0), dy - (this.dy || 0));
          this.dx = dx;
          this.dy = dy;
  
      }
  
  
  
      function up() {
          this.dx = this.dy = 0;
      }
  var newCable = function(){
          curve(20, 20, 40, 270, 100, 270, 120, 20, &quot;hsb(&quot; + Math.random() + &quot;, .75, .75)&quot;);
  
  };
  
  
  
  
    
       $('#newCable').on('click', newCable);
   
  
  };
  </code></pre>
  <h2>EDIT</h2>
  <hr />
  <h3>Here's another fiddle with a shaking cable (just drag the top gray circle)</h3>
  <h2><a href="http://jsfiddle.net/pixelass/m5ZN2/1/" rel="nofollow noreferrer">jsFiddle</a></h2>
  <hr />
  <pre><code>var r = Raphael(&quot;holder&quot;, window.innerWidth, window.innerHeight)
  
      function findReflected(coord, about) {
          var diff = about - coord;
          return about + diff;
      }
  
      function curve(x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cy3, x3, y3, color) { //zx --x1
          var cx2b = findReflected(cx2, x2),
              cy2b = findReflected(cy2, y2),
              path = [
                  [&quot;M&quot;, x1, y1],
                  [&quot;C&quot;, cx1, cy1, cx2, cy2, x2, y2, &quot;S&quot;, cx3, cy3, x3, y3]
              ],
              path2 = [
                  [&quot;M&quot;, x1, y1],
                  [&quot;L&quot;, cx1, cy1],
                  [&quot;M&quot;, cx2, cy2],
                  [&quot;L&quot;, cx2b, cy2b],
                  [&quot;M&quot;, x3, y3],
                  ['L', cx3, cy3]
              ],
              curve = r.path(path).attr({
                  stroke: color || Raphael.getColor(),
                      &quot;stroke-width&quot;: 4,
                      &quot;stroke-linecap&quot;: &quot;round&quot;
              }),
              controls = r.set(
              r.path(path2).attr({
                  stroke: &quot;none&quot;
              }),
              r.circle(x1, y1, 7).attr({
                  fill: &quot;#ccc&quot;,
                  stroke: &quot;none&quot;
              }),
              r.circle(cx1, cy1, 5).attr({
                  fill: &quot;transparent&quot;,
                  stroke: &quot;none&quot;
              }),
              r.circle(cx2, cy2, 5).attr({
                  fill: &quot;transparent&quot;,
                  stroke: &quot;none&quot;
              }),
              r.circle(x2, y2, 5).attr({
                  fill: &quot;transparent&quot;,
                  stroke: &quot;none&quot;
              }),
              r.circle(cx2b, cy2b, 5).attr({
                  fill: &quot;transparent&quot;,
                  stroke: &quot;none&quot;
              }),
              r.circle(cx3, cy3, 5).attr({
                  fill: &quot;transparent&quot;,
                  stroke: &quot;none&quot;
              }),
              r.circle(x3, y3, 7).attr({
                  fill: &quot;#ccc&quot;,
                  stroke: &quot;none&quot;
              })
  
              );
          controls[1].update = function (x, y) {
              var X = this.attr(&quot;cx&quot;) + x,
                  Y = this.attr(&quot;cy&quot;) + y;
              this.attr({
                  cx: X,
                  cy: Y
              });
              path[0][1] = X;
              path[0][2] = Y;
              path2[0][1] = X;
              path2[0][2] = Y;
              controls[2].update(x, y);
          };
          controls[2].update = function (x, y) {
              var X = this.attr(&quot;cx&quot;) + x,
                  Y = this.attr(&quot;cy&quot;) + y;
              this.attr({
                  cx: X,
                  cy: Y
              });
              path[1][1] = X;
              path[1][2] = Y;
              path2[1][1] = X;
              path2[1][2] = Y;
              curve.attr({
                  path: path
              });
              controls[0].attr({
                  path: path2
              });
          };
          controls[3].update = function (x, y, doReflect) {
              var X = this.attr(&quot;cx&quot;) + x,
                  Y = this.attr(&quot;cy&quot;) + y;
              this.attr({
                  cx: X,
                  cy: Y
              });
              path[1][3] = X;
              path[1][4] = Y;
              path2[2][1] = X;
              path2[2][2] = Y;
              curve.attr({
                  path: path
              });
              controls[0].attr({
                  path: path2
              });
              if (typeof doReflect === undefined || doReflect == null) controls[5].update(-x, -y, false);
          };
          controls[4].update = function (x, y) {
              var X = this.attr(&quot;cx&quot;) + x,
                  Y = this.attr(&quot;cy&quot;) + y;
              this.attr({
                  cx: X,
                  cy: Y
              });
              path[1][5] = X;
              path[1][6] = Y;
              controls[3].update(x, y, false);
              controls[5].update(x, y, false);
          };
          controls[5].update = function (x, y, doReflect) {
              var X = this.attr(&quot;cx&quot;) + x,
                  Y = this.attr(&quot;cy&quot;) + y;
              this.attr({
                  cx: X,
                  cy: Y
              });
              path2[3][1] = X;
              path2[3][2] = Y;
              controls[0].attr({
                  path: path2
              });
              if (typeof doReflect === undefined || doReflect == null) controls[3].update(-x, -y, false);
          };
          controls[6].update = function (x, y) {
              var X = this.attr(&quot;cx&quot;) + x,
                  Y = this.attr(&quot;cy&quot;) + y;
              this.attr({
                  cx: X,
                  cy: Y
              });
              path[1][8] = X;
              path[1][9] = Y;
              path2[5][1] = X;
              path2[5][2] = Y;
              curve.attr({
                  path: path
              });
              controls[0].attr({
                  path: path2
              });
          };
          controls[7].update = function (x, y) {
              var X = this.attr(&quot;cx&quot;) + x,
                  Y = this.attr(&quot;cy&quot;) + y;
              this.attr({
                  cx: X,
                  cy: Y
              });
              path[1][10] = X;
              path[1][11] = Y;
              path2[4][1] = X;
              path2[4][2] = Y;
              controls[1].update(x, y);
              controls[4].update(x, y);
              controls[6].update(x, y);
          };
  
          controls.drag(move, up);
          var count = 0;
          var shake;
  
  
          $('circle:eq(6)').on('mousedown', function () {
              shake = setInterval(function () {
                  count++;
                  if (count &lt;= 4) {
                      controls[4].update(-1, 0);
                      controls[1].update(2, 0);
                  } else if (count &lt;= 12) {
                      controls[4].update(1, 0);
                      controls[1].update(-2, 0);
  
                  } else if (count &lt;= 16) {
                      controls[4].update(-1, 0);
                      controls[1].update(2, 0);
  
                  } else {
                      count = 0;
  
                  }
  
              }, 30);
          }).on('mouseup', function () {
              clearInterval(shake);
          });
  
      }
  
      function move(dx, dy) {
          this.update(dx - (this.dx || 0), dy - (this.dy || 0));
          this.dx = dx;
          this.dy = dy;
      }
  
      function up() {
          this.dx = this.dy = 0;
      }
  curve(100, 350, 100, 300, 100, 300, 100, 200, 100, 100, 100, 50, &quot;hsb(0.2, 0.7, 0.8)&quot;);
  </code></pre>
  <h3>Here's a screenshot of what I'm currently working on.</h3>
  <p><img src="https://i.stack.imgur.com/Ig2OY.png" alt="Roland System 100 in javascript html5 and css3" /></p>