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

Wobbling Discord Blob

  1. const SCALE = 0.25;
  2. const TWO_PI = Math.PI * 2;
  3. const HALF_PI = Math.PI / 2;
  4. const canvas = document.createElement("canvas");
  5. const c = canvas.getContext("2d");
  6.  
  7. canvas.width = window.innerWidth;
  8. canvas.height = window.innerHeight;
  9. document.body.appendChild(canvas);
  10.  
  11. class Blob {
  12.   constructor() {
  13.     this.wobbleIncrement = 0;
  14.     // use this to change the size of the blob
  15.     // use this to change the size of the blob
  16.     this.radius = 1100;
  17.     // think of this as detail level
  18.     // number of conections in the `bezierSkin`
  19.     this.segments = 14;
  20.     this.step = HALF_PI / this.segments;
  21.     this.anchors = [];
  22.     this.radii = [];
  23.     this.thetaOff = [];
  24.  
  25.     const bumpRadius = 200;
  26.     const halfBumpRadius = bumpRadius / 2;
  27.  
  28.     for (let i = 0; i < this.segments + 2; i++) {
  29.       this.anchors.push(0, 0);
  30.       this.radii.push(Math.random() * bumpRadius - halfBumpRadius);
  31.       this.thetaOff.push(Math.random() * TWO_PI);
  32.     }
  33.  
  34.     this.theta = 0;
  35.     this.thetaRamp = 0;
  36.     this.thetaRampDest = 12;
  37.     this.rampDamp = 25;
  38.   }
  39.   update() {
  40.     this.thetaRamp += (this.thetaRampDest - this.thetaRamp) / this.rampDamp;
  41.     this.theta += 0.03;
  42.  
  43.     this.anchors = [0, this.radius];
  44.     for (let i = 0; i <= this.segments + 2; i++) {
  45.       const sine = Math.sin(this.thetaOff[i] + this.theta + this.thetaRamp);
  46.       const rad = this.radius + this.radii[i] * sine;
  47.       const theta = this.step * i;
  48.       const x = rad * Math.sin(theta);
  49.       const y = rad * Math.cos(theta);
  50.       this.anchors.push(x, y);
  51.     }
  52.  
  53.     c.save();
  54.     c.translate(-10, -10);
  55.     c.scale(SCALE, SCALE);
  56.     c.fillStyle = "blue";
  57.     c.beginPath();
  58.     c.moveTo(0, 0);
  59.     bezierSkin(this.anchors, false);
  60.     c.lineTo(0, 0);
  61.     c.fill();
  62.     c.restore();
  63.   }
  64. }
  65.  
  66. const blob = new Blob();
  67.  
  68. function loop() {
  69.   c.clearRect(0, 0, canvas.width, canvas.height);
  70.   blob.update();
  71.   window.requestAnimationFrame(loop);
  72. }
  73. loop();
  74.  
  75. // array of xy coords, closed boolean
  76. function bezierSkin(bez, closed = true) {
  77.   const avg = calcAvgs(bez);
  78.   const leng = bez.length;
  79.  
  80.   if (closed) {
  81.     c.moveTo(avg[0], avg[1]);
  82.     for (let i = 2; i < leng; i += 2) {
  83.       let n = i + 1;
  84.       c.quadraticCurveTo(bez[i], bez[n], avg[i], avg[n]);
  85.     }
  86.     c.quadraticCurveTo(bez[0], bez[1], avg[0], avg[1]);
  87.   } else {
  88.     c.moveTo(bez[0], bez[1]);
  89.     c.lineTo(avg[0], avg[1]);
  90.     for (let i = 2; i < leng - 2; i += 2) {
  91.       let n = i + 1;
  92.       c.quadraticCurveTo(bez[i], bez[n], avg[i], avg[n]);
  93.     }
  94.     c.lineTo(bez[leng - 2], bez[leng - 1]);
  95.   }
  96. }
  97.  
  98. // create anchor points by averaging the control points
  99. function calcAvgs(p) {
  100.   const avg = [];
  101.   const leng = p.length;
  102.   let prev;
  103.  
  104.   for (let i = 2; i < leng; i++) {
  105.     prev = i - 2;
  106.     avg.push((p[prev] + p[i]) / 2);
  107.   }
  108.   // close
  109.   avg.push((p[0] + p[leng - 2]) / 2, (p[1] + p[leng - 1]) / 2);
  110.   return avg;
  111. }

This is a stackoverflow answer of mine. The question was asking how to create a wobbling blob like the one in the background of the discord login page. Take a look at the answer here.

User AmooHesam wrapped it up in a github repo here.

// animation // canvas // javascript // math // paths // ui

Flower of Life Canvas

  1. ((
  2.   d = document,
  3.   b = d.body,
  4.   rad = (innerWidth * 1.9) / 6,
  5.   theta = 0,
  6.   thetaSpeed = 0.03,
  7.   cx = innerWidth / 4,
  8.   cy = innerHeight / 4,
  9.   ox = 0,
  10.   oy = 0,
  11.   offTheta,
  12.   x, y, ang, step, blur,
  13.   _
  14. ) => {
  15.   Object.assign(b.style, {
  16.     background: 'black',
  17.     margin: 0
  18.   }) 
  19.  
  20.   blur = Object.assign(d.createElement`canvas`, {
  21.     width: innerWidth * 2,
  22.     height: innerHeight * 2
  23.   }).getContext`2d`
  24.  
  25.   with (Math) {
  26.     with (b.appendChild(
  27.       Object.assign(d.createElement`canvas`, {
  28.         width: innerWidth * 2,
  29.         height: innerHeight * 2
  30.       })
  31.     ).getContext`2d`) {
  32.       Object.assign(canvas.style, {
  33.         width: '100%',
  34.         height: '100%'
  35.       })
  36.  
  37.       onresize = () => {
  38.         blur.canvas.width = canvas.width = innerWidth * 2
  39.         blur.canvas.height = canvas.height = innerHeight * 2
  40.         rad = (innerWidth * 2.5) / 6
  41.         cx = innerWidth
  42.         cy = innerHeight
  43.         fillStyle = '#000'
  44.         fillRect(0, 0, canvas.width, canvas.height)
  45.       }
  46.       onresize()
  47.  
  48.       step = (PI * 2) / 6
  49.  
  50.       _ = t => {
  51.         ang = ~~(t / 500) % 7
  52.  
  53.         globalAlpha = 0.23
  54.         fillStyle = '#fff'
  55.  
  56.         if (ang > 0) {
  57.           offTheta = step * ang
  58.           ox = rad * cos(offTheta)
  59.           oy = rad * sin(offTheta)
  60.         } else {
  61.           ox = 0
  62.           oy = 0
  63.         }
  64.  
  65.         for (i = 0; i < 20; i++) {
  66.           x = ox + cx + rad * cos(theta)
  67.           y = oy + cy + rad * sin(theta)
  68.           theta += thetaSpeed
  69.  
  70.           fillRect(x, y, 4, 4)
  71.         }
  72.  
  73.         blur.drawImage(canvas, 0, 0)
  74.  
  75.         globalAlpha = 0.05
  76.         drawImage(blur.canvas, 0, 2)
  77.  
  78.         requestAnimationFrame(_)
  79.       }
  80.       _()
  81.     }
  82.   }
  83. })()

Speed coded animated flower of life on canvas

Wiggly Line on Canvas 2

  1. const canvas = document.body.appendChild(
  2.   document.createElement('canvas')
  3. );
  4. const c = canvas.getContext('2d');
  5. document.body.style.margin = 0;
  6.  
  7. function resize() {
  8.   canvas.width = innerWidth * 2;
  9.   canvas.height = innerHeight * 2;
  10.   canvas.style.width = innerWidth + 'px';
  11.   canvas.style.height = innerHeight + 'px';
  12. }
  13. addEventListener('resize', resize);
  14. resize();
  15.  
  16. const PAD = 50;
  17. const RAD = 2;
  18. const SPEED = 200;
  19. const TWO_PI = Math.PI * 2;
  20.  
  21. let mode = 'draw';
  22.  
  23. let t = Math.random() * TWO_PI, 
  24.     x = canvas.width / 2, 
  25.     y = canvas.height / 2,
  26.     vx = 0, vy = 0, ta = 0;
  27.  
  28. let solid = false;
  29. let dotMod = 3;
  30. function loop() {
  31.   if (Math.random() < .01) solid = !solid;
  32.   if (Math.random() < .01) dotMod = [2, 3, 6][Math.floor(Math.random() * 3)]
  33.  
  34.   for (var i = 0; i < SPEED; i++) {
  35.     t = Math.sin(ta) * TWO_PI;
  36.     vx = RAD * Math.cos(t);
  37.     vy = RAD * Math.sin(t);
  38.     ta += Math.random() * 0.1 - 0.05;
  39.     x += vx;
  40.     y += vy;
  41.  
  42.     if (Math.random() < 0.005) {
  43.       mode = 'no draw';
  44.     } else if (Math.random() < 0.005) {
  45.       mode = 'draw';
  46.     }
  47.  
  48.     if (mode === 'draw' && (solid || i % dotMod === 0)) {
  49.       c.fillStyle = 'black';
  50.       c.fillRect(x, y, 2, 2);
  51.     }
  52.  
  53.     if (x < -PAD) {
  54.       x = canvas.width + PAD;
  55.     } else if (x > canvas.width + PAD) {
  56.       x = -PAD;
  57.     }
  58.     if (y < -PAD) {
  59.       y = canvas.height + PAD;
  60.     } else if (y > canvas.height + PAD) {
  61.       y = -PAD;
  62.     }
  63.   }
  64.  
  65.   requestAnimationFrame(loop);
  66. }
  67. loop();

This is a variation on a post from awhile back. I was posting it over on dev.to and realized I wanted it to look a bit different.

Elasticity With Trails

  1. let pointX = pointY = 0;
  2.  
  3. document.addEventListener('touchmove', 
  4.   e => e.preventDefault(), { passive: false });
  5.  
  6. document.addEventListener('mousemove', e => {
  7.   pointX = e.clientX;
  8.   pointY = e.clientY;
  9. });
  10.  
  11. document.addEventListener('touchmove', e => {
  12.   pointX = e.touches[0].clientX;
  13.   pointY = e.touches[0].clientY;
  14. });
  15.  
  16. let el = document.body.appendChild(
  17.   document.createElement`div`
  18. );
  19.  
  20. const size = 20;
  21. const halfSize = size / 2;
  22.  
  23. Object.assign(el.style, {
  24.   position: 'absolute',
  25.   width: `${size}px`,
  26.   height: `${size}px`,
  27.   background: 'red',
  28.   borderRadius: `${size}px`,
  29.   left: 0, top: 0
  30. });
  31.  
  32. let x = vx = y = vy = 0;
  33. const FADE_TIME = 800;
  34. const plotDot = (x, y) => {
  35.   const dot = document.body.appendChild(el.cloneNode());
  36.   const time = 
  37.   dot.style.transform += ' scale(.25)';
  38.   dot.style.transition = `opacity ${FADE_TIME}ms ease-out`;
  39.   window.requestAnimationFrame(() => {
  40.     dot.style.opacity = 0;
  41.     setTimeout(() => dot.parentNode.removeChild(dot), FADE_TIME);
  42.   })
  43. }
  44.  
  45. let ticks = 0;
  46. const loop = () => { 
  47.   vx = ((pointX - x) * .08 + vx) * .95;
  48.   vy = ((pointY - y) * .08 + vy) * .95;
  49.   x += vx;
  50.   y += vy;
  51.  
  52.   if (ticks++ % 2 === 0 && 
  53.     Math.abs(pointX - x) > 1 && 
  54.     Math.abs(pointY - y) > 1) {
  55.       plotDot();
  56.     }
  57.   el.style.transform = `translate(${x - halfSize}px, ${y - halfSize}px)`;
  58.   requestAnimationFrame(loop);
  59. }
  60. loop();
  61.  
  62. const info = document.body.appendChild(
  63.   document.createElement`div`
  64. );
  65. info.innerHTML = 'move mouse or finger left/right/up/down';

This is a variation on yesterdays post. This has elasticity on both axis and draws a trail of dots…

Elasticity

  1. let pointX = pointY = 0;
  2.  
  3. document.addEventListener('mousemove', e => {
  4.   pointX = e.clientX;
  5.   pointY = e.clientY;
  6. });
  7.  
  8. document.addEventListener('touchmove', e => {
  9.   pointX = e.touches[0].clientX
  10.   pointY = e.touches[0].clientY
  11. });
  12.  
  13. let el = document.body.appendChild(
  14.   document.createElement`div`
  15. );
  16.  
  17. const size = 20;
  18. const halfSize = size / 2;
  19.  
  20. Object.assign(el.style, {
  21.   position: 'absolute',
  22.   width: `${size}px`,
  23.   height: `${size}px`,
  24.   background: 'red',
  25.   left: 0, top: 0
  26. })
  27.  
  28. let x = vx = 0;
  29. const loop = () => { 
  30.   vx = ((pointX - x) * .2 + vx) * .79;
  31.   x += vx;
  32.   el.style.transform = `translate(${x - halfSize}px, 50px)`;
  33.   requestAnimationFrame(loop);
  34. }
  35. loop();
  36.  
  37. let info = document.body.appendChild(
  38.   document.createElement`div`
  39. );
  40. info.innerHTML = 'move mouse or finger left/right';

Basic interactive elasticity with mouse or touch

snippet.zone /// {s/z}