Quantised Fluid Sim

by soulwire

HTML

<canvas id="canvas"></canvas>

CSS

body {
      margin: 0;
      background: black;
      overflow: hidden;
    }
    canvas {
      display: block;
      image-rendering: pixelated;
    }

JavaScript

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

let width = window.innerWidth;
let height = window.innerHeight;
canvas.width = width;
canvas.height = height;

const cellSize = 16;
const cols = Math.floor(width / cellSize);
const rows = Math.floor(height / cellSize);

let fluid = [];
let velocity = [];
for (let y = 0; y < rows; y++) {
  fluid[y] = [];
  velocity[y] = [];
  for (let x = 0; x < cols; x++) {
    fluid[y][x] = 0;
    velocity[y][x] = 0;
  }
}

let mouse = { x: 0, y: 0, down: false };
canvas.addEventListener('mousemove', (e) => {
  mouse.x = Math.floor(e.clientX / cellSize);
  mouse.y = Math.floor(e.clientY / cellSize);
});
canvas.addEventListener('mousedown', () => mouse.down = true);
canvas.addEventListener('mouseup', () => mouse.down = false);

function updateFluid() {
  // Simple diffusion + damping
  const diffusion = 0.2;
  const damping = 0.98;

  for (let y = 1; y < rows - 1; y++) {
    for (let x = 1; x < cols - 1; x++) {
      let avg = (
        fluid[y][x] +
        fluid[y - 1][x] +
        fluid[y + 1][x] +
        fluid[y][x - 1] +
        fluid[y][x + 1]
      ) / 5;

      velocity[y][x] += (avg - fluid[y][x]) * diffusion;
      velocity[y][x] *= damping;
    }
  }

  for (let y = 0; y < rows; y++) {
    for (let x = 0; x < cols; x++) {
      fluid[y][x] += velocity[y][x];
      fluid[y][x] *= 0.99; // fade out
    }
  }

  if (mouse.down) {
    for (let dy = -1; dy <= 1; dy++) {
      for (let dx = -1; dx <= 1; dx++) {
        const x = mouse.x + dx;
        const y = mouse.y + dy;
        if (x >= 0 && x < cols && y >= 0 && y < rows) {
          fluid[y][x] = 1;
        }
      }
    }
  }
}

function densityToSymbol(v) {
  if (v < 0.05) return ' ';
  if (v < 0.15) return '.';
  if (v < 0.3) return '/';
  if (v < 0.45) return '\\';
  if (v < 0.6) return '+';
  if (v < 0.8) return 'x';
  return '-';
}

function...