Cellular Automata is a fun passion project I did because I wanted practice using the d3.js library. Data visualization has always been an interest of mine and so I wanted to work on a project that would help further expand on this skill.
The concept of Cellular Automata revolves around the idea that complex systems in life, organic or inorganic, can be modeled through microinteractions between smaller individual pieces. Depending on the system being modeled, there exists rules which govern the interactions between these individual pieces of the system that, in the end, affect the system on a macro level.
In this project, I model 3 different systems: traffic, forest fires, and urban growth.
Frontend
This project was done entirely with vanilla JavaScript, HTML5, CSS3, and the d3.js library.
The animations are all created via script that uses a mix of object-oriented & procedural programming along with the d3.js library. For an example, the code below describes the function that is ran in between every frame of the animation for Conway's Game of Life shown above. This function will update to form the next "state" of the animation.
const tick = (items) => {
const current = {};
items.forEach(item => {
current[`${item[0]}_${item[1]}`] = 0;
});
const nextGen = [];
const birthCands = {};
items.forEach(item => {
let neighbors = 0;
[-1, 0, 1].forEach(i => {
[-1, 0, 1].forEach(j => {
let xInterval = item[0] + i;
let yInterval = item[1] + j;
// Code to implement a wrap-around border
if (xInterval > rows-1) {
xInterval = 0;
} else if (xInterval < 0) {
xInterval = rows-1;
}
if (yInterval > cols-1) {
yInterval = 0;
} else if (yInterval < 0) {
yInterval = cols-1;
}
const key = `${xInterval}_${yInterval}`;
if (current.hasOwnProperty(key)) {
neighbors += 1;
} else {
if (!birthCands.hasOwnProperty(key)) {
birthCands[key] = 0;
}
birthCands[key] += 1;
}
});
});
// Removes self from neighbors count
neighbors -= 1;
// If 2 or 3 neighbors, the cell kept around for the next generation
if ([2, 3].includes(neighbors)) {
nextGen.push(item);
}
});
// Potential new cells generated from equilibrium
const birth = Object.entries(birthCands)
.filter(e => e[1] === 3)
.map(e => e[0].split("_").map(e => parseInt(e)));
return nextGen.concat(birth);
};
Below are the animations for the 3 complex systems I've modeled in this project!
- Traffic Model
- Forest Fire Model
- Urban Growth Model