xxxxxxxxxx
126
/**
* Made by Sébastien Raynaud - Twitter @seb_raynaud
*
* Inspired by The Coding Train (Daniel Shiffman): https://youtu.be/Ggxt06qSAe4
* Reference: https://mathworld.wolfram.com/ElementaryCellularAutomaton.html
*
* Update the first group of constants as you wish and click the play icon on the top left corner
*/
const WIDTH = 1200;
const HEIGHT = 1200;
const RULE_BASE_10 = 73;
const RESOLUTION = 5;
const FRAME_RATE = 20;
const BACKGROUND_COLOR = [6, 6, 12]; // [red, green, blue]
const HUE_OFFSET = 10 // from 1 to 360
const HUE_SHIFT_RATIO = 0.1 // how fast the hue of the color changes on x/y axes
const SHOULD_SAVE_AS_GIF = false;
const GIF_END_OFFSET_SECONDS = 3; // Gif continues to record frames for this duration after the generation of the full screen has ended
const WIDTH_CELLS = Math.floor(WIDTH / RESOLUTION);
const OFFSET_RATIO = 3;
const OFFSET_CELLS = Math.ceil(WIDTH_CELLS * OFFSET_RATIO);
const TOTAL_CELLS = WIDTH_CELLS + OFFSET_CELLS * 2;
const RULE_SET = getRuleSetFromBase10({ ruleBase10: RULE_BASE_10 });
let rows = [];
let firstRow = Array.from(Array(TOTAL_CELLS)).map((_, i) => {
if (i === Math.floor(TOTAL_CELLS / 2)) {
return 1;
}
return 0;
});
rows.push(firstRow);
function setup() {
createCanvas(WIDTH, HEIGHT);
frameRate(FRAME_RATE);
colorMode(HSB);
if (SHOULD_SAVE_AS_GIF) {
const durationSeconds = saveGif(
`cellular_automata_${RULE_BASE_10.toString(10).padStart(3, "0")}`,
Math.ceil(HEIGHT / RESOLUTION / FRAME_RATE) + GIF_END_OFFSET_SECONDS
);
}
}
function draw() {
const maximumRows = Math.floor(HEIGHT / RESOLUTION);
if (rows.length > maximumRows) {
return;
}
render();
const newRow = getNextRow();
if (!newRow) {
return;
}
rows.push(newRow);
}
function getRuleSetFromBase10({ ruleBase10 }) {
const ruleArrayBase2 = ruleBase10
.toString(2)
.padStart(8, "0")
.split("")
.map((item) => +item);
const ruleSetEntries = ruleArrayBase2.map((result, index) => {
const parents = (8 - 1 - index).toString(2).padStart(3, "0");
return [parents, result];
});
return Object.fromEntries(ruleSetEntries);
}
function render() {
background(BACKGROUND_COLOR);
strokeWeight(1);
const isEvenNumberOfCellsPerRow = WIDTH_CELLS % 2 === 0;
if (isEvenNumberOfCellsPerRow) {
translate(-Math.floor(RESOLUTION / 2), 0);
}
rows.forEach((row, i) => {
row.forEach((cell, actualJIndex) => {
if (!cell) {
return;
}
if (
actualJIndex < OFFSET_CELLS ||
actualJIndex > WIDTH_CELLS + OFFSET_CELLS
) {
return;
}
const j = actualJIndex - OFFSET_CELLS;
const currentFillColor = [
((HUE_OFFSET + (i * 1.5 + j * 0.2) * HUE_SHIFT_RATIO) % 360) + 1,
90 - i * 0.05 - j * 0.08,
90 + i * 0.02 + j * 0.01,
];
fill(currentFillColor);
stroke(currentFillColor);
square(j * RESOLUTION, i * RESOLUTION, RESOLUTION);
});
});
}
function getNextRow() {
const previousRow = rows[rows.length - 1];
if (!previousRow) {
return;
}
return Array.from(Array(TOTAL_CELLS)).map((_, i) => {
const left = previousRow[i - 1] || 0;
const middle = previousRow[i];
const right = previousRow[i + 1] || 0;
const parents = `${left}${middle}${right}`;
return RULE_SET[parents];
});
}