full page available here

it’s a trivially simple algorithm but still cool to watch

const ASCII_CHARS = ["-", "/", "|", "\\"];
const RED_TONES  = ["#440000", "#990011", "#ff2233", "#ff6677"];
const GRAY_TONES = ["#1f1f1f", "#444444", "#888888", "#cccccc"];
 
function drawCell(gridX, gridY, time) {
  // Compute field angle at this grid coordinate
  const angle = Math.sin(gridX * params.scale + time) + 
                Math.cos(gridY * params.scale + time);
                
  // Quantize 0..2PI angle into 4 directional index buckets
  const charIdx = Math.floor(Math.abs(angle * 2) % ASCII_CHARS.length);
  const char = ASCII_CHARS[charIdx];
 
  // Determine color weighting (Grays vs Reds)
  const intensity = (Math.sin(angle * 3) + 1) / 2;
  const isRed = intensity > (1.0 - params.redRatio);
  
  const palette = isRed ? RED_TONES : GRAY_TONES;
  const colorIdx = Math.floor(intensity * (palette.length - 1));
 
  ctx.fillStyle = palette[colorIdx];
  ctx.fillText(char, gridX * charWidth, gridY * charHeight);
}