Lab 4: Conway’s Game of You

Jared Mantell
9 min readJust now

--

DEMO: https://youtu.be/igh0gfN7NRLk

Hey ✌️ I’ve been reading up on cybernetics, and recently had the opportunity to turn some latent thoughts into a funky project. :)

“We are not stuff that abides, but patterns that perpetuate themselves.”
— Norbert Wiener, Mathematician & Founder of Cybernetics

We often overlook our dual role as both participants and observers.

At its core, my project aims to communicate a mutual understanding between atoms and bits. As technology develops at an exponential pace, it continues to bring a storm of changes to our cultures and economies across the world.

In Conway’s Game of You, the user uses a living plant to control — and initiate — Conway’s Game of Life. As you gently squeeze the living, breathing plant, you’re triggering a the birth of a digital ecosystem.

By creating a medium that physically connects the user to a digital simulation of life, I’d like for “Conway’s Game of You” to bring a tiny, but hopefully visceral feeling of our function in an infinite, interconnected machine.

How I did the thing:

Technical Requirements and Setup

For this project, I built upon my previous lab experience with LEDs and potentiometers, integrating it with Conway’s Game of Life and a novel plant-based interface. Here’s what I used:

  • Arduino Uno
  • Breadboard
  • Jumper wires
  • 2 Potentiometers
  • 1 Force Sensitive Resistor (FSR)
  • 2 LEDs (1 green, 1 blue)
  • 2 220Ω resistors
  • A small potted plant

I started by wiring up the LEDs to digital pins (green to pin 11, blue to pin 10), each with 220Ω resistors. potentiometers control brightness and blink rate, and the FSR is attached to the plant pot for interaction.

Here’s a picture of my initial breadboard setup:

From LEDs to Conway: The Idea Evolution

My idea started with a simple LED control system from the previous lab. I was excited by how analog inputs (potentiometers) could control digital outputs (LEDs) in real-time. This got me thinking: what if we could use organic, living inputs to control digital simulations of life?
This was going to be difficult, but Conway’s Game of Life is frequently discussed online, as it’s a pretty common piece of project code. I had plenty of resources to help me recreate it from scratch.

Deep Dive: The Game of Life Code

Conway’s Game of Life, the heart of this project, is a cellular automaton where cells live or die based on simple rules. Let’s break down the key components of our implementation:

Grid Initialization

We start by setting up our grid:

int[][] grid;
int cols, rows;
int resolution = 5;

void setup() {
// ... other setup code ...
cols = width / resolution;
rows = height / resolution;
grid = new int[cols][rows];
prevGrid = new int[cols][rows];
initializeGridWithCenterSeed();
}

void initializeGridWithCenterSeed() {
// Initialize the grid with a small seed in the center
int centerX = cols / 2;
int centerY = rows / 2;
grid[centerX][centerY] = 1;
grid[centerX-1][centerY] = 1;
grid[centerX+1][centerY] = 1;
grid[centerX][centerY-1] = 1;
grid[centerX][centerY+1] = 1;
}

This section sets up the grid for our Game of Life.
The resolution variable determines the size of each cell.

void draw() {
background(0);
if (arduino.available() > 0) {
String data = arduino.readStringUntil('\n');
if (data != null) {
data = trim(data);
float[] values = float(split(data, ','));
if (values.length == 3) {
brightnessValue = map(values[0], 0, 1023, 20, 100); // Map brightness to 20-100 range
speedValue = values[1];
fsrValue = map(values[2], 1023, 0, 0, 1); // Reverse mapping for FSR
fsrValue = constrain(fsrValue, 0, 1); // Ensure fsrValue stays within 0-1

The draw() function renders the current state of the grid and updates it for the next generation. This function also colors cells differently based on whether they’ve just come to life, adding visual dynamics to the simulation.

Updating the Grid

The core of the Game of Life logic:

void updateGrid() {
arrayCopy(grid, prevGrid); // Store the current state before updating
int[][] next = new int[cols][rows];
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
int state = grid[i][j];
int neighbors = countNeighbors(grid, i, j);
if (state == 0 && neighbors == 3) {
next[i][j] = 1;
} else if (state == 1 && (neighbors == 2 || neighbors == 3)) {
next[i][j] = 1;
} else {
next[i][j] = 0;
}

// Spontaneous life based on FSR pressure
if (fsrValue > 0 && !initialSetup && next[i][j] == 0 && random(1) < fsrValue * 0.01) { // More pressure = more chance of spontaneous life
next[i][j] = 1;
}
}
}
grid = next;
}

But where things get interesting is how I’ve tied the plant (via the FSR) into the rules. While the basic game logic remains intact, I introduced a new mechanic: spontaneous life, triggered by the amount of pressure applied to the plant pot. Here’s how it works:

if (fsrValue > 0 && !initialSetup && next[i][j] == 0 && random(1) < fsrValue * 0.01) {
next[i][j] = 1;
}

The fsrValue — a reading from the force sensor — directly controls the probability of life randomly appearing in empty cells. The more pressure you apply, the higher the chances.

Implementing the Arduino Code

I also had to create LED indicators for the values of the potentiometers. Writing the Arduino code was pretty easy, as I’d done most of this in a previous lab. Here’s the full Arduino code:

const int LED_PIN1 = 11;
const int LED_PIN2 = 10;
const int BRIGHTNESS_POT_PIN = A1;
const int SPEED_POT_PIN = A2;
const int FSR_PIN = A3;

void setup() {
pinMode(LED_PIN1, OUTPUT);
pinMode(LED_PIN2, OUTPUT);
Serial.begin(57600);
}

void loop() {
int brightnessValue = analogRead(BRIGHTNESS_POT_PIN);
int speedValue = analogRead(SPEED_POT_PIN);
int fsrValue = analogRead(FSR_PIN);

int brightness = map(brightnessValue, 0, 1023, 0, 255);
int blinkDelay = map(speedValue, 0, 1023, 1000, 50); // Faster blink for higher speed

// Set LED brightness
analogWrite(LED_PIN1, brightness);

// Blink LED2 based on speed
digitalWrite(LED_PIN2, HIGH);
delay(50); // Short on-time
digitalWrite(LED_PIN2, LOW);
delay(blinkDelay - 50); // Remaining time off

// Send values to Processing
Serial.print(brightnessValue);
Serial.print(",");
Serial.print(speedValue);
Serial.print(",");
Serial.println(fsrValue);
}

This code reads the values from the potentiometers and FSR, controls the LEDs accordingly, and sends the sensor values to Processing via serial communication.

Counting Neighbors

A crucial function for applying the rules:

int countNeighbors(int[][] grid, int x, int y) {
int sum = 0;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
if (i == 0 && j == 0) continue;
int col = (x + i + cols) % cols;
int row = (y + j + rows) % rows;
sum += grid[col][row];
}
}
return sum;
}

This function applies the classic Game of Life rules:

  • Birth: A dead cell with exactly three live neighbors becomes alive.
  • Survival: A live cell with two or three live neighbors stays alive.
  • Death: In all other cases, the cell dies or remains dead.

Integrating Arduino and FSR

Connecting the FSR to the plant and integrating it with the Arduino was more challenging than I anticipated. The FSR readings were noisy, and slight vibrations were triggering unwanted responses. To address this, I had to implement a threshold:

      if (values.length == 3) {
brightnessValue = map(values[0], 0, 1023, 20, 100); // Map brightness to 20-100 range
speedValue = values[1];
fsrValue = map(values[2], 1023, 0, 0, 1); // Reverse mapping for FSR
fsrValue = constrain(fsrValue, 0, 1); // Ensure fsrValue stays within 0-1

// Apply threshold
if (fsrValue < 0.35) {
fsrValue = 0;

This threshold helps filter out unintended inputs from slight touches or vibrations, ensuring that the Game of Life simulation only responds to deliberate interactions with the plant.

Integrating the Physical Plant

The next step was to integrate the physical plant into the system. I attached the FSR to the plant’s leaves, so that squeezing the plant would trigger the Game of Life simulation. The potentiometers were used to control the LEDs just like in the previous lab: one for the green LED’s brightness and one for the blue LED’s blink rate.

void introduceNewLife() {
if (fsrValue > 0) {
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
if (grid[i][j] == 0 && random(1) < fsrValue * 0.1) {
grid[i][j] = 1;
}
}
}
}
}

Implementation Details and Challenges

As I went deeper into the project, several aspects of the implementation evolved from my initial concept. These changes were necessary to overcome challenges and improve the overall functionality of the system. Let me walk you through the key differences and explain the reasons behind them:

  1. Initialization Function: Initially, I planned to use an initGrid() function to set up the Game of Life grid. However, in the final implementation, I opted for a more specific initializeGridWithCenterSeed() function. This change allowed me to start the simulation with a predetermined pattern at the center of the grid, providing a more consistent and visually interesting starting point.
  2. LED Control: My original plan was to control the LEDs directly from Processing using the Arduino library. However, I realized that offloading this task to the Arduino itself would result in more responsive LED behavior and simplify the Processing sketch. In the final version, the Arduino handles all LED control based on potentiometer inputs, while Processing focuses on the Game of Life simulation and FSR input processing.
  3. Arduino-Processing Communication: I initially described using the Arduino library for Processing to directly read analog inputs. The final implementation uses the Serial library in Processing to receive comma-separated sensor values from the Arduino.
  4. Grid Update Logic: While my initial description glossed over the grid update logic, it’s actually a crucial part of the Conway’s Game of Life implementation. The updateGrid() function in the final code applies the rules of the Game of Life and also introduces new life based on the FSR input:
void updateGrid() {
// ... [Game of Life rules implementation]

// Spontaneous life based on FSR pressure
if (fsrValue > 0 && !initialSetup && next[i][j] == 0 && random(1) < fsrValue * 0.01) {
next[i][j] = 1;
}
}

The Final Experience

The result was a unique, interactive installation where squeezing a plant pot controls a digital simulation of life, while two potentiometers allow fine-tuning of lighting and rate of speed. It creates a bridge between the organic and digital worlds, making us reflect on our role in both.

Here’s a picture of the final setup, including the plant:

“In a fractal conception, I am the branch and the tree.”
- Nassim Nicholas Taleb, Author, Statistician & Risk Analyst

And here’s a snapshot of my friends interacting with an early version of the project. They were able to quickly figure out what each piece of the hardware was used for, after some trial and error — of course.

Chris & Basim trying their hardest not to break the mangled ball of wires

Reflections and Future Work

This project was a fun exploration of the intersection between physical computing and digital simulations. In future iterations, I’d like to explore using multiple plants or different types of organic interfaces. Perhaps the health or growth of the plant could influence the Game of Life parameters over time, creating a longer-term interaction between the organic and digital realms.

Jared Mantell,
Professor Kimiko Ryokai, Tangible User Interfaces

--

--

Jared Mantell

I am a student in INFO 262! :) BGA Concurrent Enrollment; 3rd year student Interested in XR & Human Perception jaredmantell.com