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);
}