Simulating Cells // ICM W7

Oct 21, 2024

The Assignment

This week's focus is on using computation to explore simulation. With the ability to scale programs and have objects behave independently, consider how simulations can model the world. They can be used to practice procedures, test strategies, visualize systems (natural, biological, or social), and study situations to predict or retell events.

For this assignment, I decided to delve into natural simulations, using the opportunity to finally start reading Daniel Shiffman's The Nature of Code. Inspired by his approach, I focused on using classes and objects to model the behaviors and interactions of independent entities. The project made me question my abilities in math and revisit concepts in physics I hadn’t touched since high school. Shoutout to Dan for making the concepts super accessible, and shoutout to coffee for helping me lock in and digest the info he was putting out.

Inspiration

I remember observing cells under a microscope for the first time, I was fascinated by how they behave autonomously, yet each one operates according to a pre-coded set of instructions in its DNA. This DNA essentially governs their behavior, like a biological program. I realized that this kind of behavior could be recreated with code, after all code is just instruction. Inspired by the natural processes I observed, and after reading Dan Shiffman’s The Nature of Code(partially), I saw that simulating such autonomous behaviors is well within the realm of possibility. so I decided to embark on this project—to create a digital version of these cellular processes.

Red Blood Cells Under a Microscope

Now that I had explored some inspiration and looked into the possibility of recreating it in code, I wanted to start considering what behaviors my digital cells could have. I spent time reflecting on the process and thinking about what my cells would look like and what simple cell behavior might look like in this simulation.

Sketching Ideas:

In this phase, I explored different types of cells and sketched them on paper to see which designs resonated with me and were feasible to create. I had to keep in mind both the visual design and the coding aspect—how I would draw the cells and implement their behaviors in p5.

Sketching out cells that I could use for the simulation

While I initially started with more complex ideas, I eventually narrowed it down to one or two simpler cells. Given my understanding of movement in p5, I chose designs that I felt were most achievable within my current skill set.

Final Iteration of cell design

Defining Cell Behavior

In designing the cells for this simulation, I first needed to identify the key behaviors I wanted them to exhibit and clarify my simulation goals. I asked myself: What do I want people to see while viewing this simulation? To achieve this, I focused on a few important aspects of cell behavior and interaction.

The primary behaviors I wanted to capture included basic cell movement, how cells interact with each other and their environment, how they grow or develop, and what the dynamics of feeding and reproduction would look like. I needed to understand how to model cell movement and how they would flock and interact with one another. This led me to explore concepts from Daniel Shiffman's "Nature of Code," particularly the chapters on autonomous agents and particle systems, which provided valuable insights into simulating lifelike behaviors.

Beyond behavior, I also had to think about the visual representation of these interactions. How would different cell stages be portrayed? What would the interaction between cells during reproduction look like? I wanted to create a visual language that was both appealing and informative, allowing users to easily understand the state and actions of each cell at a glance. This involved balancing aesthetic considerations with the need for clear communication of cell states and actions.

Lastly, I had to consider the constraints and limitations within the simulation. What boundaries would the cells operate within? How would I manage population growth and resource scarcity? These questions were crucial in creating a simulation that was not only engaging but also sustainable and performant. I needed to strike a balance between realism and computational efficiency, ensuring that the simulation could run smoothly while still capturing the essence of cellular behavior.

Throughout this conceptual design process, I continually referred back to my main goal: creating an immersive, educational, and visually appealing experience that would allow users to gain insights into the fascinating world of cellular dynamics. This guiding principle helped me make decisions about which features to prioritize and how to implement them effectively.

Iterative Development Process

Based on the conceptual design process, I decided to implement the following key behaviors and features:

  1. Cell Movement:

    • Random movement for exploration

    • Directed movement towards food (seeking behavior)

    • Boundary detection and reflection off the Petri dish walls

  2. Cell Interaction:

    • Collision detection between cells and food

    • Interaction between mature (stage 5) cells for reproduction

  3. Cell Growth and Development:

    • Five distinct stages of cell development

    • Growth in size as cells consume food

    • Color changes to represent different stages

  4. Feeding Dynamics:

    • Food particles randomly spawned in the Petri dish

    • Cells consume food upon contact

    • Food consumption triggers growth and stage progression

  5. Reproduction:

    • Mature (stage 5) cells seek out other mature cells

    • Reproduction occurs after a period of interaction

    • New cells are spawned at stage 1

  6. Visual Representation:

    • Cells represented as organic, blob-like shapes

    • Color coding for different cell stages

    • Aura effect for mature (stage 5) cells

    • Simple circular representation for food particles

  7. Simulation Constraints:

    • Maximum cell population limit

    • Bounded environment (Petri dish)

    • Food scarcity (food spawn rate tied to cell population)

These chosen behaviors and features formed the foundation for the iterative development process that followed.

Iterative Code Development

Basic Setup and Single Cell Movement

In this first stage of development, the goal was to set up the basic structure of the simulation and implement simple cell movement. I started by creating a canvas and a single cell that moves randomly across the screen.

This stage set the foundation for more complex behaviors that I added in later iterations. It demonstrates the use of object-oriented programming to create a simple, moving entity in our simulation.

Create Cell class
  Initialize with position and random velocity
  
  Update method:
    Add velocity to position
    Wrap around edges if out of bounds
  
  Display method:
    Draw circle at current position

Setup:
  Create canvas
  Create single cell instance

Draw loop:
  Clear background
  Update cell position
  Display cell
Final Output:
Multiple Cells and Food

In this stage the code now manage multiple cells and food particles using arrays, introducing the concept of particle systems. I've implemented a simple food spawning mechanism, demonstrating how to add new objects to the simulation over time. The Cell class has been improved with new methods for eating food and seeking targets, introducing more complex motion behaviors. I've added a new Food class to represent food particles in our simulation.

Some key ideas introduced here include: Array manipulation for managing multiple objects, Basic physics simulation with acceleration and velocity, Probability-based object spawning and Simple collision detection for "eating" food.

Pseudo code:

cells = empty array
food = empty array
INITIAL_CELLS = 5
MAX_FOOD = 50

function setup:
    create canvas
    for INITIAL_CELLS times:
        create new Cell at random position
        add Cell to cells array

function draw:
    clear background
    
    if random chance and food count < MAX_FOOD:
        create new Food at random position
        add Food to food array
    
    for each cell in cells (backwards):
        update cell
        display cell
    
    for each food in food:
        display food

class Cell:
    constructor(x, y):
        initialize position, velocity, acceleration
        set radius, max speed, max force
    
    function update:
        apply physics (acceleration affects velocity, velocity affects position)
        limit speed
        check edges
        eat food
    
    function eat:
        for each food (backwards):
            if cell is touching food:
                remove food from array
    
    function seek(target):
        calculate desired velocity towards target
        calculate steering force
        apply force to acceleration
    
    function edges:
        wrap around screen edges
    
    function display:
        draw cell on screen

class Food:
    constructor(x, y):
        set position
        set radius
    
    function display:
        draw food on screen
Final Output:
Cell Eating Behavior

In this stage, i'm taking our cell simulation to the next level by implementing a crucial biological process: feeding. I'll introduce a mechanism for cells to consume food, grow, and progress through different life stages. This adds a new layer of complexity to our simulation, bringing us closer to modeling real cellular behavior.

Building on concepts from Daniel Shiffman's "The Nature of Code," particularly Chapters 2 (Forces) and 6 (Autonomous Agents), I'll enhance our cells with the ability to seek out and consume food.

Pseudo code:

cells = empty array
food = empty array
INITIAL_CELLS = 8
MAX_CELLS = 200
FOOD_COLORS = ["#577455", "#8C3F4D", "#6B8E9B", "#EE8838", "#F4F2EF", "#6B8E9B"]

function setup:
    create canvas
    create PetriDish
    for INITIAL_CELLS times:
        create new Cell at random position within PetriDish
        add Cell to cells array

function draw:
    clear background
    translate canvas to center
    display PetriDish
    
    if random chance and food count < cells count - 1:
        create new Food at random position within PetriDish
        add Food to food array
    
    for each food in food:
        display food
    
    for each cell in cells (backwards):
        update cell
        display cell
        if cell is reproducing and cells count < MAX_CELLS:
            create new Cell near parent cell
            reset parent cell to stage one

class Cell:
    constructor(x, y):
        initialize position, velocity, acceleration
        set radius, max speed, max force
        initialize foodEaten, stage, color
    
    function update:
        if not interacting with another cell:
            apply physics (acceleration affects velocity, velocity affects position)
            limit speed
            check edges
            eat food
            revert stage if necessary
            if stage < 5:
                seek food
            else:
                seek stage 5 cell or move randomly
        else:
            interact with other cell
    
    function eat:
        for each food (backwards):
            if cell is touching food:
                remove food from array
                increase foodEaten
                grow cell
                update stage and color
    
    function seekFood:
        find closest food
        seek food position
    
    function seekStage5:
        find closest stage 5 cell
        if close enough:
            start interaction
    
    function interact:
        if interaction time > threshold:
            set reproducing flag
    
    function display:
        draw cell on screen with color based on stage
        if stage 5, draw aura

class Food:
    constructor(x, y):
        set position
        set color randomly from FOOD_COLORS
        set size
    
    function display:
        draw food on screen

class PetriDish:
    constructor(radius):
        set radius
    
    function display:
        draw petri dish outline
Final Output:
Cell Reproduction

In this stage, we'll implement a more sophisticated reproduction mechanism for our cells. This will involve mature cells seeking each other out, interacting, and potentially creating new cells. We'll also ensure that the parent cells revert to their initial stage after reproduction.

Pseudo code:

// Global variables
cells = empty array
food = empty array
INITIAL_CELLS = 8
MAX_CELLS = 200
FOOD_COLORS = ["#577455", "#8C3F4D", "#6B8E9B", "#EE8838", "#F4F2EF", "#6B8E9B"]
petriDish = null

function setup:
    create canvas (600x600)
    create PetriDish with radius 85% of half the canvas width
    for INITIAL_CELLS times:
        create new Cell at random position within PetriDish
        add Cell to cells array

function draw:
    clear background
    translate canvas to center
    display PetriDish
    
    if random chance (10%) and food count < cells count - 1:
        create new Food at random position within PetriDish
        add Food to food array
    
    for each food in food:
        display food
    
    for each cell in cells (backwards):
        update cell
        display cell
        if cell is reproducing and cells count < MAX_CELLS:
            create new Cell near parent cell
            reset parent cell and its partner to stage one
            clear interaction states

class PetriDish:
    constructor(radius):
        set radius
    
    function display:
        draw petri dish outline

class Cell:
    constructor(x, y):
        initialize position, velocity, acceleration vectors
        set radius, max speed, max force
        initialize foodEaten, stage, color
        set interactingWith to null
        set interactionTime to 0
        set reproducing to false
    
    function update:
        if not interacting with another cell:
            apply physics (update velocity and position)
            check and handle edges
            eat food
            revert stage if necessary
            if stage < 5:
                seek food
            else:
                seek stage 5 cell or move randomly
        else:
            interact with other cell
    
    function display:
        draw cell shape
        draw inner circle
        display stage number
        if stage 5, draw aura
    
    function edges:
        keep cell within petri dish bounds
    
    function eat:
        check for collision with food
        if collided:
            remove food
            increase foodEaten
            grow cell
            update stage and color
    
    function seekFood:
        find closest food
        seek food position
    
    function seekStage5:
        find closest stage 5 cell
        if close enough:
            start interaction
    
    function seek:
        calculate and apply steering force towards target
    
    function interact:
        simulate circular motion with partner cell
        increment interaction time
        if interaction time > threshold:
            set reproducing flag for both cells
    
    function revertStage:
        if stage 5 and no partner found for a while:
            revert to stage 4
    
    function revertToStageOne:
        reset stage, foodEaten, color, and size to initial values
    
    function getColorForStage:
        return color based on current stage

class Food:
    constructor(x, y):
        set position
        set color randomly from FOOD_COLORS
        set size
    
    function display:
        draw food particle
Final Output:
Final Refinements and Balancing

In this stage, I'll focus on optimizing the simulation and add some final touches to enhance the visual representation and overall performance.

cells = empty array
food = empty array
INITIAL_CELLS = 8
MAX_CELLS = 200
FOOD_COLORS = ["#577455", "#8C3F4D", "#6B8E9B", "#EE8838", "#F4F2EF", "#6B8E9B"]
petriDish = null

function setup:
    create canvas (600x600)
    create PetriDish with radius 85% of half the canvas width
    for INITIAL_CELLS times:
        create new Cell at random position within PetriDish
        add Cell to cells array

function draw:
    clear background
    translate canvas to center
    display PetriDish
    
    if random chance (10%) and food count < cells count - 1:
        create new Food at random position within PetriDish
        add Food to food array
    
    for each food in food:
        display food
    
    for each cell in cells (backwards):
        update cell
        display cell
        if cell is reproducing and cells count < MAX_CELLS:
            create new Cell near parent cell
            reset parent cell and its partner to stage one
            clear interaction states

class PetriDish:
    constructor(radius):
        set radius
    
    function display:
        draw petri dish outline

class Cell:
    constructor(x, y):
        initialize position, velocity, acceleration vectors
        set radius, max speed, max force
        initialize foodEaten, stage, color
        set interactingWith to null
        set interactionTime to 0
        set reproducing to false
    
    function update:
        if not interacting with another cell:
            apply physics (update velocity and position)
            check and handle edges
            eat food
            revert stage if necessary
            if stage < 5:
                seek food
            else:
                seek stage 5 cell or move randomly
        else:
            interact with other cell
    
    function display:
        draw cell body (teardrop shape)
        draw inner circle
        display stage number
        if stage 5, draw pulsating aura
    
    function edges:
        keep cell within petri dish bounds
    
    function eat:
        check for collision with food
        if collided:
            remove food
            increase foodEaten
            grow cell
            update stage and color
    
    function seekFood:
        find closest food
        seek food position
    
    function seekStage5:
        find closest stage 5 cell
        if close enough:
            start interaction
    
    function seek:
        calculate and apply steering force towards target
    
    function interact:
        simulate circular motion with partner cell
        increment interaction time
        if interaction time > threshold:
            set reproducing flag for both cells
    
    function revertStage:
        if stage 5 and no partner found for a while:
            revert to stage 4
    
    function revertToStageOne:
        reset stage, foodEaten, color, and size to initial values
    
    function getColorForStage:
        return color based on current stage

class Food:
    constructor(x, y):
        set position
        set color randomly from FOOD_COLORS
        set size
    
    function display:
        draw food particle
Final Output: Link to Code

Learnings and Reflections

This project help me discover the fundamental relationship between computational thinking and natural systems. Through implementing cell behaviors like movement, feeding, and reproduction, I understood how complex biological processes can be broken down into programmable instructions. Creating digital simulations like this one demonstrated how nature's complexity often emerges from relatively simple rules and interactions – much like how our simulation's sophisticated behaviors emerged from basic physics principles and mathematical models.

The importance of physics and mathematics also became very evident on this project for implementing cell movements and interactions. Concepts like velocity, acceleration, and force vectors – which I hadn't engaged with since high school – proved essential for creating realistic cell behaviors. Working with Daniel Shiffman's "The Nature of Code" reinforced how mathematical models can bridge the gap between natural phenomena and digital simulation. This project is proof that even basic physics principles, when applied systematically through code, can generate surprisingly life like behaviors.


The most intriguing aspect was the exploration of autonomous agents and emergence. By giving each cell its own decision-making capabilities through programmed behaviors, the simulation demonstrated how individual entities following simple rules can create complex, system-level patterns.

Using EEG waves as input for controlling cell behaviour
Controlling Cell behaviour with our mind

I've been experimenting with the Muse 2 headset and it's been quite exciting to work with the date for interactive simulations. Rather than working directly with raw brainwave data (alpha, beta, delta, theta, and gamma), I've been working on translating these patterns into intuitive mental states that users can consciously control. This abstraction allows us to map states like calmness or focus directly to cell behaviors in our simulation.

The practical implementation can look something like this : in the cell simulation, one cell follows the standard rules while another responds to the user's mental state. When the user focuses, their cell behaves predictably; when their attention wanders, so does their cell. This creates an intimate connection between mental state and digital behavior, opening up possibilities for games where thought becomes a form of input. Future developments could include machine learning models trained on neural data patterns, enabling more sophisticated interactions between thought and simulation.


References
  1. Nik's Blog
  2. Claude by Anthropic
  3. Ellen for being such a great teacher
  4. https://p5js.org/reference
  5. Shiffman, D. (2012). The Nature of Code: Simulating Natural Systems with Processing. Self-published. Retrieved from https://natureofcode.com/
  6. Shiffman, D. (n.d.). The Coding Train. https://thecodingtrain.com/ (For p5.js tutorials and inspiration)
  7. Cursor AI

©2019-2025 SURYA NARREDDI.

©2019-2025 SURYA NARREDDI.