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

Random Walk to Target

  1. const canvas = document.createElement('canvas');
  2. const c = canvas.getContext('2d');
  3.  
  4. let targetX, targetY, startX, startY;
  5. const rectSize = 20;
  6. const maxStep = 10;
  7.  
  8. const matchStep = maxStep / 2;
  9. const halfRectSize = rectSize / 2;
  10. let matchTime = 0;
  11. const resetTime = 20;
  12.  
  13. function randX() {
  14.   return innerWidth * 0.8 * Math.random() + innerWidth * 0.1;
  15. }
  16. function randY() {
  17.   return innerHeight * 0.8 * Math.random() + innerHeight * 0.1;
  18. }
  19.  
  20. function resize() {
  21.   canvas.width = innerWidth;
  22.   canvas.height = innerHeight;
  23.   reset();
  24. }
  25. window.addEventListener('resize', resize);
  26. resize();
  27.  
  28. document.body.appendChild(canvas);
  29. document.body.style.margin = 0;
  30.  
  31. function reset() {
  32.   matchTime = 0;
  33.   targetX = randX();
  34.   targetY = randY();
  35.   startX = randX();
  36.   startY = randY();
  37.   c.fillStyle = '#ccc';
  38.   c.fillRect(0, 0, innerWidth, innerHeight);
  39.  
  40.   c.fillStyle = '#c79500';
  41.   c.fillRect(
  42.     targetX - halfRectSize,
  43.     targetY - halfRectSize,
  44.     rectSize,
  45.     rectSize
  46.   );
  47.  
  48.   c.fillStyle = '#4e82c7';
  49.   c.fillRect(startX - halfRectSize, startY - halfRectSize, rectSize, rectSize);
  50. }
  51.  
  52. function loop() {
  53.   c.strokeStyle = 'black';
  54.   c.beginPath();
  55.   c.moveTo(startX, startY);
  56.   if (startX < targetX) {
  57.     startX += Math.random() * maxStep;
  58.   } else if (startX > targetX) {
  59.     startX -= Math.random() * maxStep;
  60.   }
  61.   if (startY < targetY) {
  62.     startY += Math.random() * maxStep;
  63.   } else if (startY > targetY) {
  64.     startY -= Math.random() * maxStep;
  65.   }
  66.  
  67.   c.lineTo(startX, startY);
  68.   c.stroke();
  69.  
  70.   if (
  71.     Math.abs(startX - targetX) < matchStep &&
  72.     Math.abs(startY - targetY) < matchStep
  73.   ) {
  74.     matchTime++;
  75.     if (matchTime > resetTime) {
  76.       reset();
  77.     }
  78.   }
  79.  
  80.   window.requestAnimationFrame(loop);
  81. }
  82. loop();

Randomly walk to a target.

2x mod 1 Map

  1. function twoXmodMap(start = 0.2) {
  2.   const seed = () => Math.random() * Math.random() * start
  3.   let xn = seed()
  4.   let xn1
  5.   let iter = 0
  6.   return () => {
  7.     iter++
  8.     if (iter > 50) {
  9.       xn = seed()
  10.       iter = 0
  11.     }
  12.  
  13.     // this is the key part:
  14.     xn1 = (2 * xn) % 1
  15.     xn = xn1
  16.  
  17.     return xn1
  18.   }
  19. }
  20.  
  21. const el = document.body.appendChild(document.createElement('div'))
  22. el.innerHTML = `
  23.   <svg>
  24.     <path d="M 0 0 L 1 1" fill="none" stroke="red" />
  25.   </svg>
  26.   <style>
  27.     svg, body, html {
  28.       width: 100%;
  29.       height: 100%;
  30.       overflow: visible;
  31.     }
  32.   </style>
  33. `
  34.  
  35. const path = el.querySelector('path')
  36. let pathStr = ''
  37.  
  38. const map = twoXmodMap()
  39.  
  40. let x = 0
  41. let yo = 0
  42. let y;
  43. function loop() {
  44.   y = map()
  45.   x += 2
  46.  
  47.   if (pathStr == '' || x > 300) {
  48.     x = 10
  49.     yo += 50
  50.     pathStr += `M 10 ${yo}`
  51.   } else {
  52.     pathStr += ` L ${x} ${yo + y * 30} `
  53.   }
  54.  
  55.   path.setAttribute('d', pathStr)
  56.   window.requestAnimationFrame(loop)
  57. }
  58. loop()

Drawing the 2x mod 1 map.

// javascript // math // svg

Find a String Naive Hill Climbing

  1. const target = 'snippetzone'.split``;
  2. const leng = target.length;
  3. const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split``;
  4.  
  5. function randomString() {
  6.   let str = [];
  7.   for (let i = 0; i < leng; i++) {
  8.     str.push(randomChar());
  9.   }
  10.   return str;
  11. }
  12.  
  13. function randomChar() {
  14.   return alphabet[Math.floor(Math.random() * alphabet.length)];
  15. }
  16.  
  17. let iterations = 0;
  18. const search = randomString();
  19. const indices = [];
  20. for (var i = 0; i < leng; i++) indices.push(i);
  21. let found = false;
  22.  
  23. function loop() {
  24.   for (let i = 0; i < 10; i++) {
  25.     if (indices.length > 0) {
  26.       let ii = Math.floor(Math.random() * indices.length);
  27.       let index = indices[ii];
  28.       search[index] = randomChar();
  29.       if (search[index] == target[index]) {
  30.         indices.splice(ii, 1);
  31.       }
  32.       console.log(search.join(','));
  33.       iterations++;
  34.     } else {
  35.       console.log('found after', iterations, 'iterations');
  36.       found = true;
  37.       break;
  38.     }
  39.   }
  40.  
  41.   if (!found) {
  42.     requestAnimationFrame(loop);
  43.   }
  44. }
  45. loop();

This is a port of an old snippet of mine. It naively and randomly finds a target string. In this case the string “snippetzone”.

It’s fun to see it randomly perform better/worse…

e,f,k,w,q,s,o,h,n,r,f e,f,k,w,q,s,o,h,n,r,u k,f,k,w,q,s,o,h,n,r,u k,f,k,w,q,o,o,h,n,r,u k,f,k,w,q,o,o,v,n,r,u k,f,k,w,q,v,o,v,n,r,u ... s,e,i,p,p,e,t,z,o,n,e s,v,i,p,p,e,t,z,o,n,e s,h,i,p,p,e,t,z,o,n,e s,d,i,p,p,e,t,z,o,n,e s,s,i,p,p,e,t,z,o,n,e s,e,i,p,p,e,t,z,o,n,e s,g,i,p,p,e,t,z,o,n,e s,q,i,p,p,e,t,z,o,n,e s,m,i,p,p,e,t,z,o,n,e s,w,i,p,p,e,t,z,o,n,e s,g,i,p,p,e,t,z,o,n,e s,x,i,p,p,e,t,z,o,n,e s,t,i,p,p,e,t,z,o,n,e s,o,i,p,p,e,t,z,o,n,e s,e,i,p,p,e,t,z,o,n,e s,k,i,p,p,e,t,z,o,n,e s,p,i,p,p,e,t,z,o,n,e s,b,i,p,p,e,t,z,o,n,e s,n,i,p,p,e,t,z,o,n,e found after 176 iterations

Fast Sine and Cosine Approximation

  1. let t = 0, cos, sin, x, y;
  2.  
  3. const PI = Math.PI;
  4. const TWO_PI = PI * 2;
  5. const HALF_PI = PI / 2;
  6. const tA = 4 / PI;
  7. const tB = 4 / PI ** 2;
  8.  
  9. const canvas = document.body.appendChild(document.createElement('canvas'));
  10. const c = canvas.getContext('2d');
  11. canvas.width = canvas.height = 300;
  12.  
  13. function loop() {
  14.   // low precision sine/cosine
  15.   // always wrap input angle to -PI..PI
  16.   t += 0.1;
  17.   if (t < -PI) {
  18.     t += TWO_PI;
  19.   } else if (t > PI) {
  20.     t -= TWO_PI;
  21.   }
  22.  
  23.   // compute sine
  24.   if (t < 0) {
  25.     sin = tA * t + tB * t * t;
  26.   } else {
  27.     sin = tA * t - tB * t * t;
  28.   }
  29.  
  30.   // compute cosine: sin(t + PI/2) = cos(t)
  31.   t += HALF_PI;
  32.   if (t > PI) {
  33.     t -= TWO_PI;
  34.   }
  35.  
  36.   if (t < 0) {
  37.     cos = tA * t + tB * t * t;
  38.   } else {
  39.     cos = tA * t - tB * t * t;
  40.   }
  41.  
  42.   t -= HALF_PI; // move the shape
  43.  
  44.   x = 110 + 100 * cos;
  45.   y = 110 + 100 * sin;
  46.  
  47.   c.fillStyle = 'rgba(100, 100, 20, .5)';
  48.   c.fillRect(x, y, 10, 10);
  49.   window.requestAnimationFrame(loop);
  50. }
  51. loop();

This is an old trick for fast sine/cosine approximation. I learned about it from Michael Baczynski’s blog which now seems to be down. Here is a wayback link for the original article and another one to a more in-depth discussion about it:
original post
original thread

It’s unlikely that the speed gain (if there even is one here) makes sense in javascript. This is really more for fun… a quick search will turn up all kinds of cool info about fast sine/cosine stuff.

isPowerOfTwo

  1. function isPowerOfTwo(value) {
  2.   return (value & (value - 1)) === 0 && value !== 0;
  3. }
  4.  
  5. for (let i = 0; i < 66; i++) {
  6.   console.log(i, isPowerOfTwo(i));
  7. }

I’ve had the pleasure of using THREE.js quite a bit over the years. The MathUtils file has some great utility functions. This version of isPowerOfTwo comes straight from there. I may post a few more from there in the future…

snippet.zone ~ 2021-24 /// {s/z}