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

WebGL Particles in Sphere

  1. (() => {
  2.   const m = new Float32Array([
  3.     0, 0, 0, 0, 
  4.     0, 0, 0, 0, 
  5.     0, 0, 0, 0, 
  6.     0, 0, 0, 0])
  7.  
  8.   const pointSize = Math.max(Math.min(30, ~~(innerWidth / 70)), 3)
  9.  
  10.   const vert = `
  11.     attribute vec3 vec;
  12.     uniform mat4 mat;
  13.     void main(void) {
  14.       gl_Position = mat * vec4(vec, 1.0);
  15.       gl_PointSize = ${pointSize}.0;
  16.     }
  17.   `
  18.  
  19.   const frag = `
  20.     void main(void) {
  21.       gl_FragColor = vec4(1., 1., 1., .25);
  22.     }
  23.   `
  24.  
  25.   document.body.style.background = '#232323'
  26.   const gl = document.body
  27.     .appendChild(document.createElement('canvas'))
  28.     .getContext('webgl', {
  29.       preserveDrawingBuffer: true,
  30.       powerPreference: 'high-performance'
  31.     })
  32.  
  33.   Object.assign(gl.canvas.style, {
  34.     position: 'absolute',
  35.     left: '50%',
  36.     top: '50%',
  37.     transform: 'translate(-50%, -50%)',
  38.     outline: '1px solid gray'
  39.   })
  40.  
  41.   with (gl) {
  42.     const NUM = 4000
  43.     const radius = 0.7
  44.     const verts = []
  45.  
  46.     for (let i = 0; i < NUM; i += 3) {
  47.       let xp = Math.random() * 2 - 1
  48.       let yp = Math.random() * 2 - 1
  49.       let zp = Math.random() * 2 - 1
  50.       let dist = Math.sqrt(xp * xp + yp * yp + zp * zp)
  51.       // normalize and scale x,y,z
  52.       verts[i] = (xp / dist) * radius
  53.       verts[i + 1] = (yp / dist) * radius
  54.       verts[i + 2] = (zp / dist) * radius
  55.  
  56.     }
  57.     const overts = verts.concat()
  58.     const leng = verts.length / 3
  59.  
  60.     bindBuffer(ARRAY_BUFFER, createBuffer())
  61.     bufferData(ARRAY_BUFFER, new Float32Array(verts), STATIC_DRAW)
  62.  
  63.     const vs = createShader(VERTEX_SHADER)
  64.     shaderSource(vs, vert)
  65.     compileShader(vs)
  66.  
  67.     const fs = createShader(FRAGMENT_SHADER)
  68.     const sp = createProgram()
  69.  
  70.     shaderSource(fs, frag)
  71.     compileShader(fs)
  72.     attachShader(sp, vs)
  73.     attachShader(sp, fs)
  74.     linkProgram(sp)
  75.     useProgram(sp)
  76.  
  77.     const vec = getAttribLocation(sp, 'vec')
  78.     vertexAttribPointer(vec, 3, FLOAT, false, 0, 0)
  79.     enableVertexAttribArray(vec)
  80.  
  81.     const matLoc = getUniformLocation(sp, 'mat')
  82.  
  83.     function rot(x, y, z) {
  84.       // https://wikimedia.org/api/rest_v1/media/math/render/svg/a8e16f4967571b7a572d1a19f3f6468512f9843e
  85.  
  86.       const sinA = Math.sin(x)
  87.       const cosA = Math.cos(x)
  88.       const sinB = Math.sin(y)
  89.       const cosB = Math.cos(y)
  90.       const sinY = Math.sin(z)
  91.       const cosY = Math.cos(z)
  92.  
  93.       m[0] = cosA * cosB
  94.       m[1] = cosA * sinB * sinY - sinA * cosY
  95.       m[2] = cosA * sinB * cosY + sinA * sinY
  96.       m[3] = 0
  97.  
  98.       m[4] = sinA * cosB
  99.       m[5] = sinA * sinB * sinY + cosA * cosY
  100.       m[6] = sinA * sinB * cosY - cosA * sinY
  101.       m[7] = 0
  102.  
  103.       m[8] = -sinB
  104.       m[9] = cosB * sinY
  105.       m[10] = cosB * cosY
  106.       m[11] = m[12] = m[13] = 0
  107.       m[15] = 1
  108.  
  109.       uniformMatrix4fv(matLoc, false, m)
  110.     }
  111.  
  112.     onresize = () => {
  113.       const { canvas } = gl
  114.       const size = Math.min(innerWidth, innerHeight) - 20
  115.       canvas.width = canvas.height = size
  116.       viewport(0, 0, size, size)
  117.     }
  118.  
  119.     onresize()
  120.  
  121.     let rx = 0,
  122.       ry = 0,
  123.       rz = 0
  124.     let t = 0
  125.  
  126.     function loop() {
  127.       rx += 0.01
  128.       ry += 0.01
  129.       rot(rx, ry, rz)
  130.  
  131.       disable(DEPTH_TEST)
  132.       enable(BLEND)
  133.       blendFunc(SRC_ALPHA, ONE_MINUS_SRC_ALPHA)
  134.  
  135.       clearColor(0, 0, 0, 1)
  136.  
  137.       clear(COLOR_BUFFER_BIT)
  138.  
  139.       for (let i = 0; i < verts.length; i += 3) {
  140.         let tof = i * 0.001 + t;
  141.         verts[i] = Math.sin(tof) * overts[i]
  142.         verts[i + 1] = Math.sin(tof) * overts[i + 1]
  143.         verts[i + 2] = Math.sin(tof) * overts[i + 2]
  144.       }
  145.  
  146.       t += 0.01
  147.  
  148.       bufferSubData(ARRAY_BUFFER, 0, new Float32Array(verts))
  149.  
  150.       drawArrays(POINTS, 0, leng)
  151.       window.requestAnimationFrame(loop)
  152.     }
  153.     loop()
  154.   }
  155. })()

Playing around with animating points…

Responsive Radial Gradient Animation

  1. function rect(x1, y1, x2, y2, col, blur=.1) {
  2.   const dx = x1 - x2;
  3.   const dy = y1 - y2;
  4.   const dist = Math.sqrt(dx * dx + dy * dy);
  5.   return `radial-gradient(circle at ${x1}% ${y1}%, ${col} 0, ${col} ${dist}%, transparent ${dist +
  6.     blur}%)`;
  7. }
  8.  
  9. const NUM = 8; 
  10.  
  11. let rects = [];
  12. const colors = ['rgba(0, 0, 0, 0.2)', 'white', 'black'];
  13.  
  14. let x = 0;
  15. let y = 0;
  16. let t = 8;
  17. function loop() { 
  18.   rects = [];
  19.  
  20.   for (let i = 0; i < NUM; i++) { 
  21.     x = 50 + 30 * Math.sin(t + i / 2);
  22.     y = 50 + 30 * Math.cos(t * 1.5 * i / 10);
  23.     rects.push(rect(x, y, x + 5, y + 5, 'rgba(255, 255, 255, 1)', 1));
  24.     rects.push(rect(x, y, x + 5, y + 5, 'rgba(0, 0, 0, 0.4)', 
  25.       8 + 6 * Math.cos(y / 10)));
  26.   }
  27.   t += .04;
  28.   document.body.style.backgroundImage = rects.join(', ');
  29.   window.requestAnimationFrame(loop);
  30. }
  31. loop()
  32.  
  33. document.body.innerHTML += `
  34.   <style>
  35.     body, html {
  36.       height: 100%;
  37.       background: #ccc;
  38.       margin: 0;
  39.       background-repeat: no-repeat;
  40.       width: 100%;
  41.     }
  42.   </style>
  43. `;

Animated variation on yesterdays post – many animating circles with no divs or canvas, just radial-gradients…

// animation // css // dom // graphics // hacks // javascript // math

Letter Particles With Keyboard

  1. const Key = {
  2.   LEFT: 37,
  3.   UP: 38,
  4.   RIGHT: 39,
  5.   DOWN: 40,
  6.   disabled: false
  7. };
  8.  
  9. const keyDown = {};
  10.  
  11. function setupKeys() {
  12.   const alph = 'abcdefghijklmnopqrstuvwxyz'.split('');
  13.   const keys = {};
  14.   const aKey = 65;
  15.   alph.forEach((letter, i) => {
  16.     const idx = aKey + i;
  17.     keyDown[idx] = false;
  18.     Key[letter.toUpperCase()] = idx;
  19.   });
  20.  
  21.   keyDown.allLetters = () => {
  22.     const lettersDown = alph.filter(
  23.       letter => keyDown[Key[letter.toUpperCase()]]
  24.     );
  25.     return lettersDown;
  26.   };
  27.  
  28.   document.addEventListener('keydown', e => {
  29.     e.preventDefault();
  30.     if (!Key.disabled) {
  31.       keyDown[e.which] = true;
  32.     }
  33.   });
  34.  
  35.   document.addEventListener('keyup', e => {
  36.     e.preventDefault();
  37.     keyDown[e.which] = false;
  38.   });
  39. }
  40.  
  41. setupKeys();
  42.  
  43. // later:
  44.  
  45. const dots = {};
  46. let id = 0;
  47. function dot(letter = '') {
  48.   const el = document.createElement('div');
  49.   el.innerHTML = letter;
  50.   const size = 20;
  51.   const scale = 1 + Math.random() * 2;
  52.   const vel = (2 - scale / 2) / 4;
  53.   let x = innerWidth / 2 - size;
  54.   let y = innerHeight / 2 - size;
  55.   let vx = Math.random() * 6 - 3;
  56.   let vy = Math.random() * 6 - 3;
  57.   let life = 0;
  58.   let maxLife = Math.floor(50 + Math.random() * 30);
  59.   Object.assign(el.style, {
  60.     position: 'absolute',
  61.     left: 0,
  62.     top: 0,
  63.     transform: `translate3d(${x}px, ${y}px)`,
  64.     borderRadius: '500px',
  65.     background: 'red',
  66.     width: `${size}px`,
  67.     fontFamily: 'sans-serif',
  68.     textAlign: 'center',
  69.     color: 'white'
  70.   });
  71.  
  72.   id++;
  73.  
  74.   let props = {
  75.     el,
  76.     id,
  77.     right() {
  78.       vx += vel;
  79.     },
  80.     left() {
  81.       vx -= vel;
  82.     },
  83.     down() {
  84.       vy += vel;
  85.     },
  86.     up() {
  87.       vy -= vel;
  88.     },
  89.     run() {
  90.       life++;
  91.       if (life === maxLife) {
  92.         document.body.removeChild(dots[props.id].el);
  93.         delete dots[props.id];
  94.       }
  95.       x += vx;
  96.       y += vy;
  97.       vx *= 0.999;
  98.       vy *= 0.999;
  99.       el.style.transform = `translate(${x}px, ${y}px) scale(${scale}) `;
  100.     }
  101.   };
  102.   dots[id] = props;
  103.   document.body.appendChild(el);
  104. }
  105.  
  106. function loop() {
  107.   keyDown.allLetters().forEach(letter => {
  108.     dot(letter);
  109.   });
  110.  
  111.   for (let i in dots) {
  112.     dots[i].run();
  113.   }
  114.  
  115.   if (keyDown[Key.LEFT]) {
  116.     for (let i in dots) {
  117.       dots[i].left();
  118.     }
  119.   }
  120.  
  121.   if (keyDown[Key.DOWN]) {
  122.     for (let i in dots) {
  123.       dots[i].down();
  124.     }
  125.   }
  126.  
  127.   if (keyDown[Key.RIGHT]) {
  128.     for (let i in dots) {
  129.       dots[i].right();
  130.     }
  131.   }
  132.  
  133.   if (keyDown[Key.UP]) {
  134.     for (let i in dots) {
  135.       dots[i].up();
  136.     }
  137.   }
  138.  
  139.   requestAnimationFrame(loop);
  140. }
  141. loop();
  142.  
  143. const info = document.createElement('div')
  144. info.innerHTML = 'click here, then type letters and use arrow keys'
  145. document.body.appendChild(info)

A demo showing how to use the keyboard snippet from yesterday. Also added a function to obtain an array of all currently pressed letter keys

// animation // javascript // keys // math // random // tricks // ui

Animate to Target

  1. function box(x, y, col = 'red', cursor){
  2.   const box = document.body.appendChild(
  3.     document.createElement`div`
  4.   )
  5.   box.classList.add(col + '-box')
  6.   Object.assign(box.style, {
  7.     position: 'absolute', 
  8.     left: `${x}%`,
  9.     top: `${y}%`,
  10.     width: '30px',
  11.     height: '30px',
  12.     background: col,
  13.     cursor: cursor || 'pointer',
  14.     color: 'white'
  15.   })
  16.   return box
  17. }
  18.  
  19. const NUM = 10;
  20. for (let i = 0; i < NUM; i++) 
  21.   box(
  22.     Math.random() * 100, 
  23.     Math.random() * 100)
  24.  
  25.  
  26. let destX = destY = x = y = 0;
  27. const blue = box(destX, destY, 'blue', 'default')
  28. const info = box(0, 30, 'gray')
  29. info.innerHTML = 'click the red boxes'
  30. Object.assign(info.style, {
  31.   width: '100%', 
  32.   padding: '.5em',
  33.   fontFamily: 'sans-serif'
  34. })
  35.  
  36. document.addEventListener('click', e => {
  37.   const curr = e.target
  38.   if (curr.classList.contains('red-box')) {
  39.     destX = curr.offsetLeft
  40.     destY = curr.offsetTop
  41.     curr.style.background = 'black'
  42.     setTimeout(() => curr.style.background = 'red', 700)
  43.     if (info.parentNode != null) { 
  44.       info.parentNode.removeChild(info);
  45.     }
  46.   }
  47. })
  48.  
  49. function loop() {
  50.   x += (destX - x) / 12
  51.   y += (destY - y) / 12
  52.   blue.style.transform = `translate3d(${x}px, ${y}px, 0)`
  53.   requestAnimationFrame(loop)
  54. }
  55. loop()

Click a red box, watch the blue box animate…

Oscillating Canvas Wave

  1. const c = document.body
  2.   .appendChild(document.createElement('canvas'))
  3.   .getContext('2d');
  4.  
  5. Object.assign(document.body.style, {
  6.   margin: 0,
  7.   height: '100%'
  8. });
  9.  
  10. Object.assign(c.canvas.style, {
  11.   position: 'absolute',
  12.   left: 0,
  13.   top: 0,
  14.   width: '100%',
  15.   height: '100%'
  16. });
  17.  
  18. let t = 0;
  19.  
  20. function resize() {
  21.   c.canvas.width = innerWidth * 2;
  22.   c.canvas.height = innerHeight * 2;
  23.   draw();
  24. }
  25. window.addEventListener('resize', resize);
  26. resize();
  27.  
  28. function draw() {
  29.   const {
  30.     canvas: { width, height }
  31.   } = c;
  32.  
  33.   c.fillStyle = 'rgba(155, 155, 155, .4)';
  34.   c.fillRect(0, 0, width, height);
  35.   c.fillStyle = '#000';
  36.  
  37.   let x = innerWidth;
  38.   let y = 0;
  39.  
  40.   t += 0.05;
  41.   for (let i = 0; i < innerHeight; i++) {
  42.     x += 2 * Math.cos(t + i * 0.1 * Math.cos(i / 200));
  43.     y += 2;
  44.     c.fillRect(x, y, 100, 1);
  45.   }
  46. }
  47.  
  48. function loop() {
  49.   draw();
  50.   window.requestAnimationFrame(loop);
  51. }
  52. loop();

Speed coded oscillating wave on canvas… Looks better in fullscreen.

snippet.zone /// {s/z}