Created
April 27, 2026 03:24
-
-
Save schollz/7eee1c9ed8061ff31bc4205fe09bff99 to your computer and use it in GitHub Desktop.
reaction diffusion
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 16x16 display, higher-res internal sim (p5.js) | |
| const VIEW_W = 16; | |
| const VIEW_H = 16; | |
| const SCALE = 32; | |
| // internal resolution (must be multiple of 16) | |
| const SIM_W = 64; | |
| const SIM_H = 64; | |
| const BLOCK = SIM_W / VIEW_W; // 4 | |
| let grid, nextGrid; | |
| let dA = 1.0; | |
| let dB = 0.5; | |
| let baseFeed = 0.036; | |
| let baseKill = 0.064; | |
| let t = 0; | |
| function setup() { | |
| createCanvas(VIEW_W * SCALE, VIEW_H * SCALE); | |
| noStroke(); | |
| grid = makeGrid(SIM_W, SIM_H); | |
| nextGrid = makeGrid(SIM_W, SIM_H); | |
| for (let y = 0; y < SIM_H; y++) { | |
| for (let x = 0; x < SIM_W; x++) { | |
| grid[y][x].a = 1; | |
| grid[y][x].b = random(0, 0.2); | |
| } | |
| } | |
| } | |
| function draw() { | |
| background(255); | |
| t += 0.01; | |
| let feed = baseFeed + 0.004 * sin(t * 0.6); | |
| let kill = baseKill + 0.004 * cos(t * 0.4); | |
| for (let i = 0; i < 8; i++) { | |
| step(feed, kill); | |
| } | |
| injectNoise(); | |
| renderDownsampled(); | |
| } | |
| function makeGrid(w, h) { | |
| let g = []; | |
| for (let y = 0; y < h; y++) { | |
| g[y] = []; | |
| for (let x = 0; x < w; x++) { | |
| g[y][x] = { a: 1, b: 0 }; | |
| } | |
| } | |
| return g; | |
| } | |
| function step(feed, kill) { | |
| for (let y = 0; y < SIM_H; y++) { | |
| for (let x = 0; x < SIM_W; x++) { | |
| let a = grid[y][x].a; | |
| let b = grid[y][x].b; | |
| let lapA = laplace(x, y, "a"); | |
| let lapB = laplace(x, y, "b"); | |
| let reaction = a * b * b; | |
| let newA = a + (dA * lapA - reaction + feed * (1 - a)) * 0.9; | |
| let newB = b + (dB * lapB + reaction - (kill + feed) * b) * 0.9; | |
| nextGrid[y][x].a = constrain(newA, 0, 1); | |
| nextGrid[y][x].b = constrain(newB, 0, 1); | |
| } | |
| } | |
| let tmp = grid; | |
| grid = nextGrid; | |
| nextGrid = tmp; | |
| } | |
| function laplace(x, y, chem) { | |
| let sum = 0; | |
| sum += sample(x, y, chem) * -1; | |
| sum += sample(x - 1, y, chem) * 0.2; | |
| sum += sample(x + 1, y, chem) * 0.2; | |
| sum += sample(x, y - 1, chem) * 0.2; | |
| sum += sample(x, y + 1, chem) * 0.2; | |
| sum += sample(x - 1, y - 1, chem) * 0.05; | |
| sum += sample(x + 1, y - 1, chem) * 0.05; | |
| sum += sample(x - 1, y + 1, chem) * 0.05; | |
| sum += sample(x + 1, y + 1, chem) * 0.05; | |
| return sum; | |
| } | |
| function sample(x, y, chem) { | |
| x = (x + SIM_W) % SIM_W; | |
| y = (y + SIM_H) % SIM_H; | |
| return grid[y][x][chem]; | |
| } | |
| // downsample 64x64 → 16x16 | |
| function renderDownsampled() { | |
| for (let gy = 0; gy < VIEW_H; gy++) { | |
| for (let gx = 0; gx < VIEW_W; gx++) { | |
| let sum = 0; | |
| for (let dy = 0; dy < BLOCK; dy++) { | |
| for (let dx = 0; dx < BLOCK; dx++) { | |
| let x = gx * BLOCK + dx; | |
| let y = gy * BLOCK + dy; | |
| sum += grid[y][x].a - grid[y][x].b; | |
| } | |
| } | |
| let avg = sum / (BLOCK * BLOCK); | |
| avg = constrain(avg, 0, 1); | |
| // 16 grayscale levels | |
| let level = floor(avg * 15); | |
| let shade = level * (255 / 15); | |
| fill(shade); | |
| rect(gx * SCALE, gy * SCALE, SCALE, SCALE); | |
| } | |
| } | |
| } | |
| function injectNoise() { | |
| for (let i = 0; i < 8; i++) { | |
| let x = floor(random(SIM_W)); | |
| let y = floor(random(SIM_H)); | |
| grid[y][x].b += random(0.01, 0.03); | |
| grid[y][x].b = constrain(grid[y][x].b, 0, 1); | |
| } | |
| } | |
| // click = precise injection | |
| function mousePressed() { | |
| let gx = floor(mouseX / SCALE); | |
| let gy = floor(mouseY / SCALE); | |
| for (let dy = 0; dy < BLOCK; dy++) { | |
| for (let dx = 0; dx < BLOCK; dx++) { | |
| let x = gx * BLOCK + dx; | |
| let y = gy * BLOCK + dy; | |
| grid[y][x].b = 1; | |
| } | |
| } | |
| } | |
| // drag = smear | |
| function mouseDragged() { | |
| let gx = floor(mouseX / SCALE); | |
| let gy = floor(mouseY / SCALE); | |
| for (let dy = -1; dy <= 1; dy++) { | |
| for (let dx = -1; dx <= 1; dx++) { | |
| let cx = (gx + dx + VIEW_W) % VIEW_W; | |
| let cy = (gy + dy + VIEW_H) % VIEW_H; | |
| for (let sy = 0; sy < BLOCK; sy++) { | |
| for (let sx = 0; sx < BLOCK; sx++) { | |
| let x = cx * BLOCK + sx; | |
| let y = cy * BLOCK + sy; | |
| grid[y][x].b += 0.05; | |
| grid[y][x].b = constrain(grid[y][x].b, 0, 1); | |
| } | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment