)
}
}
)
(
}
{
)
)
(
)
(
(
{
}
)
(
)
}
)
)
{
(
(
)
)
}
)
(
}

Swrls

  1. const canvas = document.createElement("canvas");
  2. const c = canvas.getContext("2d");
  3.  
  4. canvas.width = innerWidth;
  5. canvas.height = innerHeight;
  6. document.body.append(canvas); 
  7.  
  8. const TWO_PI = 2 * Math.PI;
  9.  
  10. class Dot {
  11.   constructor(id, x, y, theta, parent) {
  12.     this.x = x;
  13.     this.y = y;
  14.     this.t = theta;
  15.     this.vx = Math.cos(this.t) * Math.random() * 2;
  16.     this.vy = Math.sin(this.t) * Math.random() * 2;
  17.     this.life = 30;
  18.     this.col = parent.col;
  19.  
  20.     if (Math.random() < 0.005) this.other = true;
  21.     if (this.other) {
  22.       this.life = 1000;
  23.       this.t = Math.random() * 7;
  24.       this.vx = Math.cos(this.t) * Math.random() * 2;
  25.       this.vy = Math.sin(this.t) * Math.random() * 2;
  26.       this.col = "black";
  27.     }
  28.     this.iter = 0;
  29.     this.parent = parent;
  30.     this.id = id;
  31.     this.alpha = 0.25;
  32.  
  33.     if (Math.random() < 0.3) this.alpha = 0.25;
  34.   }
  35.  
  36.   draw() {
  37.     this.x += this.vx;
  38.     this.y += this.vy;
  39.     this.iter++;
  40.     if (this.iter > this.life) {
  41.       delete this.parent.dots[this.id];
  42.     }
  43.     c.fillStyle = this.col;
  44.     c.globalAlpha = this.alpha;
  45.     if (!this.no) this.alpha -= 0.00125;
  46.     c.fillRect(this.x, this.y, this.parent.size, this.parent.size);
  47.   }
  48. }
  49.  
  50. class Stain {
  51.   constructor(x = innerWidth, y = innerHeight) {
  52.     this.x = x / 2;
  53.     this.y = y / 2;
  54.     this.num = 1;
  55.     this.step = TWO_PI / this.num;
  56.  
  57.     this.size = 1 + Math.random() * Math.random() * 2;
  58.     this.rad = Math.random() * 300;
  59.     this.dots = {};
  60.     this.dotId = 0;
  61.     this.col = Math.random() < 0.8 ? "white" : "black";
  62.     this.to = Math.random() * 7;
  63.     this.S = 20 + Math.random() * 50;
  64.     this.R = 100 + Math.random() * 300;
  65.     this.rr = 50 + Math.random() * 300;
  66.     this.dir = Math.random() < 0.6 ? 1 : Math.random() * 10 - 5;
  67.     this.v = 1;
  68.     this.tt = 0;
  69.     if (Math.random() * 0.25) this.v = Math.random() * 5;
  70.   }
  71.  
  72.   draw() { 
  73.     const circ = this.rad * TWO_PI;
  74.     this.rad -= this.v;
  75.  
  76.     this.step = TWO_PI / circ;
  77.  
  78.     if (this.rad > 0) {
  79.       for (let i = 1; i <= this.R; i += this.S) {
  80.         let t = (i * this.step + this.to) * this.dir;
  81.         let x = this.x + this.rad * Math.cos(t);
  82.         let y = this.y + this.rad * Math.sin(t);
  83.         this.dots[this.dotId] = new Dot(this.dotId, x, y, t, this);
  84.         this.dotId++;
  85.       }
  86.     }
  87.     for (let i in this.dots) {
  88.       if (this.dots[i]) this.dots[i].draw();
  89.     }
  90.   }
  91. }
  92.  
  93. onresize = e => {
  94.   canvas.width = innerWidth;
  95.   canvas.height = innerHeight;
  96.   c.fillStyle = "black";
  97.   c.fillRect(0, 0, innerWidth, innerHeight)
  98. }
  99.  
  100. onresize()
  101.  
  102. let ss = [new Stain()];
  103.  
  104. function loop() {
  105.   if (Math.random() < 0.05) {
  106.     ss.push(
  107.       new Stain(Math.random() * innerWidth * 2, Math.random() * innerHeight * 2)
  108.     );
  109.   }
  110.   ss.forEach((s) => s.draw());
  111.   c.globalAlpha = 0.05;
  112.   c.drawImage(canvas, 0, 3, canvas.width + 0.2, canvas.height - 6);
  113.   c.globalAlpha = 1;
  114.   requestAnimationFrame(loop);
  115. }
  116. loop();

Speed coding for some recent youtube shorts…

Polar Forking Tweak

  1. const FOUR_PI = 6 * Math.PI;
  2. const { cos, sin } = Math;
  3.  
  4. const canvas = document.body.appendChild(
  5.   document.createElement('canvas')
  6. );
  7. const c = canvas.getContext('2d');
  8.  
  9. function resize() {
  10.   canvas.width = window.innerWidth;
  11.   canvas.height = window.innerHeight;
  12. }
  13.  
  14. let inc = 0;
  15. function draw() { 
  16.   c.fillStyle = 'rgba(0, 0, 0, .3)'
  17.   c.fillRect(0, 0, canvas.width, canvas.height)
  18.   c.fillStyle = 'white';
  19.  
  20.   const halfWidth = window.innerWidth / 2;
  21.   const halfHeight = window.innerHeight / 2;
  22.   let theta = 0,
  23.     a = 20 * Math.min(window.innerWidth, window.innerHeight) * 0.005,
  24.     x,
  25.     y;
  26.  
  27.   c.save();
  28.   c.translate(halfWidth, halfHeight)
  29.  
  30.   let b = 5 * cos(inc);
  31.   inc += .02;
  32.  
  33.   for (let i = 0; theta < FOUR_PI; i++) {
  34.     let rad = a * (b + 10 * sin(theta / 3));
  35.     // randomly speed-coded and tweaked... leaving as is :D
  36.     x = rad * cos(theta + b / 10) * cos(b / 10 +theta * 2) * cos(theta * 2);
  37.     y = rad * sin(theta * 2) * cos(theta + b / 3) * cos(theta * 2);
  38.     c.fillRect(x,y, 2, 2);
  39.     theta += 0.04;
  40.   }
  41.   c.restore();
  42.  
  43.   requestAnimationFrame(draw)
  44. }
  45.  
  46. resize();
  47. addEventListener('resize', resize);
  48.  
  49. draw();

Just randomly futzing with sin/cos…

Animation Along Path SVG

  1. const el = document.body.appendChild(
  2.   document.createElement('div'));
  3.  
  4. el.innerHTML = `
  5.   <svg width="100%" height="100%" viewBox="0 0 550 496">
  6.     <path 
  7.       d="M 20 10 C 100 100 300 00 300 100 200 200 150 300 20 10" 
  8.       stroke="black" fill='none' vector-effect="non-scaling-stroke"/>
  9.     <circle cx="20" cy="10" r="6" fill="red" />
  10.   </svg>
  11.   <style>
  12.     svg, div, body, html {
  13.       overflow: visible; 
  14.       height: 100%; 
  15.       width: 100%;
  16.       margin: 0; padding: 0;
  17.     }
  18.   </style>
  19.  `;
  20.  
  21. const circle = el.querySelector('circle');
  22. const path = el.querySelector('path');
  23. const length = path.getTotalLength();
  24. let pos = 0;
  25.  
  26. function loop() {
  27.   pos += 3;
  28.   if (pos > length) {
  29.     pos = 0;
  30.   }
  31.   const point = path.getPointAtLength(pos);
  32.   circle.cx.baseVal.value = point.x;
  33.   circle.cy.baseVal.value = point.y;
  34.   requestAnimationFrame(loop);
  35. }
  36. loop();

SVG has an easy way to animate something along an arbitrary path… getPointAtLength

Catmull Rom to Bezier SVG

  1. // from: 
  2. //   https://github.com/DmitryBaranovskiy/raphael/blob/b5fdbdc9850827eb49622cd0bc81b26025f8128a/dev/raphael.core.js#L1019
  3. // and: 
  4. //   http://schepers.cc/getting-to-the-point
  5.  
  6. function catmullRom2bezier(crp, close) {
  7.   const d = [];
  8.   for (let i = 0, iLen = crp.length; iLen - 2 * !close > i; i += 2) {
  9.     const p = [
  10.       { x: crp[i - 2], y: crp[i - 1] },
  11.       { x: crp[i], y: crp[i + 1] },
  12.       { x: crp[i + 2], y: crp[i + 3] },
  13.       { x: crp[i + 4], y: crp[i + 5] }
  14.     ];
  15.     if (close) {
  16.       if (!i) {
  17.         p[0] = { x: crp[iLen - 2], y: crp[iLen - 1] };
  18.       } else if (iLen - 4 == i) {
  19.         p[3] = { x: crp[0], y: crp[1] };
  20.       } else if (iLen - 2 == i) {
  21.         p[2] = { x: crp[0], y: crp[1] };
  22.         p[3] = { x: crp[2], y: crp[3] };
  23.       }
  24.     } else {
  25.       if (iLen - 4 == i) {
  26.         p[3] = p[2];
  27.       } else if (!i) {
  28.         p[0] = { x: crp[i], y: crp[i + 1] };
  29.       }
  30.     }
  31.     d.push([
  32.       'C',
  33.       (-p[0].x + 6 * p[1].x + p[2].x) / 6,
  34.       (-p[0].y + 6 * p[1].y + p[2].y) / 6,
  35.       (p[1].x + 6 * p[2].x - p[3].x) / 6,
  36.       (p[1].y + 6 * p[2].y - p[3].y) / 6,
  37.       p[2].x,
  38.       p[2].y
  39.     ]);
  40.   }
  41.  
  42.   // really for demo purposes:
  43.  
  44.   // draw a tiny rect if less than 2 points
  45.   console.log(d.length);
  46.   if (d.length < 2) {
  47.     const x = crp[0];
  48.     const y = crp[1];
  49.     return `M ${x} ${y} h 1 v 1 h -1 v-1`;
  50.  
  51.     // draw a line if 2 points
  52.   } else if (d.length === 2) {
  53.     return `M ${crp[0]} ${crp[1]} L ` + crp.flat().join` `;
  54.   }
  55.   // draw curves if 3 or more (raphael only ever did this, other numbers of points
  56.   // were considered invalid)
  57.   return  `M ${crp[0]} ${crp[1]}` + d.flat().join` `;
  58. }
  59.  
  60. // try it out:
  61.  
  62. const pathCommands = catmullRom2bezier([
  63.   100, 100, 200, 100, 200, 300, 170, 150, 120, 180, 120, 120
  64.   ], true)
  65.  
  66. const svg = `
  67.   click 3 or more times anywhere<br>
  68.   <button>clear</button>
  69.   <svg>
  70.     <path d="M 100 100 ${pathCommands}" stroke="blue" fill="none" />
  71.   </svg>
  72.   <style>
  73.   svg {
  74.     position:absolute;
  75.     left:0;top:0;
  76.     overflow:visible;
  77.     pointer-events:none;
  78.   }
  79.   </style>
  80. `;
  81.  
  82. const div = document.body.appendChild(
  83.   document.createElement('div')
  84. );
  85. div.innerHTML = svg;
  86. const path = div.querySelector('path')
  87.  
  88. let pnts = [];
  89.  
  90. function isButton(e) {
  91.   if (e.target.tagName === 'BUTTON') {
  92.     pnts = []
  93.     path.setAttribute('d', 'M 0 0L0 0');
  94.     return true;
  95.   }
  96. }
  97.  
  98. document.addEventListener('click', e => {
  99.   if (isButton(e)) return;
  100.   pnts.push(e.clientX, e.clientY);
  101.   path.setAttribute('d', catmullRom2bezier(pnts, true))
  102. });
  103.  
  104. document.addEventListener('touchend', e => {
  105.   if (isButton(e)) return;
  106.   pnts.push(e.touchs[0].clientX, e.touchs[0].e.clientY);
  107.   path.setAttribute('d', catmullRom2bezier(pnts, true))
  108. });

Raphaël was my first deep dive into SVG. Back then, only VML was supported in older IE versions, so using Raphaël was key for tons of commercial work I did (Raphaël generated VML as well as SVG). This snippet is the Catmull Rom code from Raphaël. I’m pretty sure I didn’t know what a Catmull Rom spline was until I found out that using `R` in a path wasn’t supported natively in SVG… and was actually some magic thing that existed in Raphaël alone.

// curves // javascript // math // svg
snippet.zone ~ 2021-24 /// {s/z}