The "Color Theory" project is an interactive color-matching game designed to blend digital and physical interactivity, foster teamwork, and enhance participants' understanding of RGB color blending. Two teams compete in three levels, adjusting RGB values to match a target color displayed on the screen. The project evolved through research on color theory and interactivity, as well as insights from midterm project exploring how interaction with project can improve engagement and collaboration. This foundation shaped our concept, prioritizing a balance between fun, competition, and education.
At User Testing Session understanding how users would interact with the project was key to several design decisions:
We designed physical slider pots for level adjustments, ensuring that users could the digital outcomes on the screen.
Observations suggested users enjoyed collaboration, so we structured the gameplay to require teamwork for problem-solving.
Immediate visual feedback on color matching ensured participants could easily track their progress and make strategic adjustments.
User Testing Session and Feedback
Users' feedback highlighted several areas for improvement:
Some users were unclear about the game’s timer mechanics.
The physical wheels were difficult to turn, causing frustration.
Limited space hindered group participation.
Based on user feedback, we made several adjustments:
A timer was added to the hardest level, helping users track remaining time and increasing the challenge.
We redesigned the wheels for smoother turning, enhancing physical interactivity and user comfort.
Added a visual representation of slider movements on the screen, allowing for clearer feedback on which level they chose.
Adjusted the design to accommodate up to six players per game box so all participants could engage freely.
Labeled teams as "Team 1" and "Team 2," on the screen, making gameplay more organized.
The iterative process of design, testing, and refinement resulted in a more engaging and accessible project. The "Color Theory" game successfully achieves its goal of educating participants about color blending while fostering collaboration and excitement through an interactive, team-based experience.
1x Breadboard
1x Arduino Mega
1x USB cable
6x Potentiometers
1x 10kOhm Resistor
1х Slider Pot
6x Wheels
1x Arcade button
1x Breadboard
1x LED
2x LED Neopixels
A handful of M/M and F/M jumper cables
1x Hot glue gun
3x Acrylic Sheet
Power Supply (USB Cable)
Superglue
Soldering tools
1x Rubber material
Step 1: Testing Potentiometers
1.1 Potentiometer Setup:
Attach each potentiometer to the Arduino to regulate the Red, Green, and Blue values for the respective teams.
Connections:
Link one outer terminal of the potentiometer to the Arduino's 5V pin.
Attach the other outer terminal to GND.
Connect the center terminal to one of the analog input pins on the Arduino (A0-A5).
Secure the potentiometer wires by soldering them.
1.2 Slide Potentiometer (Difficulty Adjustment):
Wire the slide potentiometer similarly to the standard ones, using female-to-male jumper wires, and connect it to a suitable input pin (A6).
1.3 Arcade Button (Start/Stop/Reset):
LED Connections:
Attach the red terminal of the LED on the button to the 5V pin on the Arduino.
Connect the black terminal to GND via a small resistor.
Button Connections:
Link one terminal of the button to a digital input pin on the Arduino and to GND through a 10k resistor.
Attach the other terminal to the 5V pin.
1.4 Arduino Setup:
Once all components are connected, link the Arduino to your computer via a USB cable for programming.
Step 2: Code Uploading
2.1 Arduino Code:
Upload the necessary code to the Arduino using the Arduino IDE.
2.2 Processing Code:
Open the .pde file in Processing and configure it to match your Arduino board’s COM port.
Step 3: Final Assembly
3.1 Box Fabrication:
Design and cut the box using a laser cutter.
3.2 Box Assembly:
Assemble the box using super glue.
Mount the potentiometers on the inner surface of the top panel and affix the wheels externally.
Secure the slide potentiometer and arcade button in their designated spots.
3.3 Wiring and Integration:
Connect the Arduino to all components: potentiometers, slide potentiometer, button, and power source. Verify that all wiring is correctly and firmly connected.
3.4 Testing the System:
Perform a full functionality test by interacting with the potentiometers and arcade button.
Arduino:
#include <FastLED.h>
#define NUM_LEDS 120 // Total number of LEDs on your strip
#define DATA_PIN 3 // Pin connected to the LED strip
CRGB leds[NUM_LEDS];
// int pot1 = 0; // analog pin used to connect the potentiometer
int val1;
// int pot2 = 1; // analog pin used to connect the potentiometer
int val2;
// int pot3 = 2; // analog pin used to connect the potentiometer
int val3;
int val4;
int val5;
int val6;
int button ;
#define PIN_SLIDE_POT_A A6 // Assign analog pin A0
int slide = A6;
int segments;
void setup() {
Serial.begin(9600);
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); // Initialize LED strip
FastLED.setBrightness(50); // Set brightness
}
void loop() {
// Clear all LEDs
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Black;
}
// to send values to Processing assign the values you want to send
// this is an example:
val1 = analogRead(A0);
val2 = analogRead(A1);
val3 = analogRead(A2);
val4 = analogRead(A3);
val5 = analogRead(A4);
val6 = analogRead(A5);
button = digitalRead(7);
pinMode(PIN_SLIDE_POT_A, INPUT); // Optional for analog input, but okay to leave it
slide = analogRead(PIN_SLIDE_POT_A); // Read the analog value from pin A0
if (slide < 341){
segments = 0;
}else if (slide < 682){
segments = 1;
}else{
segments = 2;
}
// Light up the LEDs based on the section
if (segments == 0) { // First third
for (int i = 0; i < NUM_LEDS / 3; i++) {
leds[i] = CRGB::Green; // Change color to your preference
}
} else if (segments == 1) { // Two-thirds
for (int i = 0; i < (NUM_LEDS * 2) / 3; i++) {
leds[i] = CRGB::Blue; // Change color to your preference
}
} else if (segments == 2) { // Full strip
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Red; // Change color to your preference
}
}
FastLED.show(); // Update the LED strip
delay(100); // Small delay for stability
// send the values keeping this format
Serial.print(val1);
Serial.print(","); // put comma between sensor values
Serial.print(val2);
Serial.print(","); // put comma between sensor values
Serial.print(val3);
Serial.print(","); // put comma between sensor values
Serial.print(val4);
Serial.print(","); // put comma between sensor values
Serial.print(val5);
Serial.print(","); // put comma between sensor values
Serial.print(val6);
Serial.print(","); // put comma between sensor values
Serial.print(button);
Serial.print(","); // put comma between sensor values
Serial.print(segments);
Serial.println(); // add linefeed after sending the last sensor value
delay(500);
}
Processing:
import processing.serial.*;
import processing.sound.*;
PImage photo;
PImage photo2;
PImage photo3;
PImage photo4;
SoundFile sound;
SoundFile sound1;
SoundFile sound2;
Serial serialPort;
int NUM_OF_VALUES_FROM_ARDUINO = 8;
int arduino_values[] = new int[NUM_OF_VALUES_FROM_ARDUINO];
int screen = 0; // 0 = intro screen, 1 = game screen, 2 = end screen
int difficulty = 0;
int tolerance = 20;
int timer = 60 * 1000; // 60 seconds timer in milliseconds
int startTime;
boolean gameEnded = false;
boolean team1Won = false;
boolean team2Won = false;
int remainingTime;
float r;
float g;
float b;
// Wave variables
float waveSpeed = 5; // Speed of wave movement
float waveHeight = 5; // Amplitude of the wave
float waveFrequency = 0.1; // Frequency of the waves
float offset = 0; // Horizontal offset for the moving wave
float verticalOffset = 0; // Offset for vertical wave movement
float verticalWaveSpeed = 4; // Speed for vertical wave movement
float verticalWaveThickness = 20; // Thickness of the vertical wave
void setup() {
size(1260, 700);
photo = loadImage("intro.png");
photo2 = loadImage("team1.png");
photo3 = loadImage("team2.png");
photo4 = loadImage("lose.png");
textSize(24);
printArray(Serial.list()); // Print available serial ports for debugging
serialPort = new Serial(this, "/dev/cu.usbmodem101", 9600);
sound = new SoundFile(this,"game.mp3"); // Game intro sound
sound1 = new SoundFile(this, "win.mp3"); // Win sound
sound2 = new SoundFile(this, "lose.mp3"); // Lose sound
r = random(255);
g = random(255);
b = random(255);
}
void draw() {
getSerialData(); // Read serial data
if (screen == 0) {
introScreen();
} else if (screen == 1) {
gameScreen(); // Show game screen
} else if (screen == 2) {
endScreen(); // Show end screen
}
}
void introScreen() {
photo.resize(1260, 700);
image(photo, 0, 0);
// Play game intro sound on the intro screen
if (!sound.isPlaying()) { // Ensure it only plays once
sound.play();
}
fill(#F50000);
float level = map(arduino_values[7], 0,2,430,640);
rect(level,430,20,70,5);
//430 to 640
if (arduino_values[6] == 1 && arduino_values[7] == 0) {
difficulty = 1;
screen = 1;
} else if (arduino_values[6] == 1 && arduino_values[7] == 1) {
difficulty = 2;
screen = 1;
} else if (arduino_values[6] == 1 && arduino_values[7] == 2) {
difficulty = 3;
screen = 1;
}
delay(200);
}
void gameScreen() {
if (startTime == 0) {
startTime = millis(); // Initialize start time when entering game screen
}
background(200);
fill(0);
textAlign(LEFT, TOP);
text("Difficulty: " + (difficulty == 1 ? "Easy" : difficulty == 2 ? "Medium" : "Hard"), 10, 10);
// Adjust tolerance based on difficulty
if (difficulty == 1) tolerance = 25;
else if (difficulty == 2) tolerance = 15;
else if (difficulty == 3) {
tolerance = 15;
int elapsedTime = millis() - startTime;
remainingTime = timer - elapsedTime;
if (remainingTime <= 0) {
remainingTime = 0;
gameEnded = true;
screen = 2;
}
}
// Team RGB values from Arduino
float r1 = map(arduino_values[0], 0, 1023, 0, 255);
float g1 = map(arduino_values[1], 0, 1023, 0, 255);
float b1 = map(arduino_values[2], 0, 1023, 0, 255);
float r2 = map(arduino_values[3], 0, 1023, 0, 255);
float g2 = map(arduino_values[4], 0, 1023, 0, 255);
float b2 = map(arduino_values[5], 0, 1023, 0, 255);
// Draw vertical waves falling into horizontal waves
drawVerticalWaves(147, height / 2, width / 16, height / 4, r1, 0, 0); // Left side vertical waves falling into horizontal waves
drawVerticalWaves(305, height / 2, width / 16, height / 4, 0, g1, 0); // Right side vertical waves falling into horizontal waves
drawVerticalWaves(462, height / 2, width / 16, height / 4, 0, 0, b1); // Left side vertical waves falling into horizontal waves
drawVerticalWaves(777, height / 2, width / 16, height / 4, r2, 0, 0); // Right side vertical waves falling into horizontal waves
drawVerticalWaves(934, height / 2, width / 16, height / 4, 0, g2, 0); // Left side vertical waves falling into horizontal waves
drawVerticalWaves(1091, height / 2, width / 16, height / 4, 0, 0, b2); // Right side vertical waves falling into horizontal waves
// Draw horizontal waves based on team colors
drawHorizontalWaves(0, height * 2 / 3, width / 2, height / 3, r1, g1, b1); // Left bottom square waves
drawHorizontalWaves(width / 2, height * 2 / 3, width / 2, height / 3, r2, g2, b2); // Right bottom square waves
fill(r, g, b);
noStroke();
rect(0, 0, width, height / 2);
textSize(24);
fill(0);
text("Difficulty:" + difficulty, 10, height - 20);
text("Team 1", 500 , height -20);
text("Team 2", 700 , height -20);
if (difficulty == 3) {
rect(width*50/100,height/18, 100,60);
fill(#F50000);
stroke(100);
textFont(createFont("Arial",60));
text( max(remainingTime / 1000, 0) + "s", width /2 , height / 16);}
if (screen == 1 &&arduino_values[6] == 1||screen == 2 &&arduino_values[6] == 1) {
resetGame();
delay(150);}
if (!gameEnded) {
// Check win condition for team 1
if (abs(r1 - r) <= tolerance && abs(g1 - g) <= tolerance && abs(b1 - b) <= tolerance) {
gameEnded = true;
team1Won = true;
}
// Check win condition for team 2
if (abs(r2 - r) <= tolerance && abs(g2 - g) <= tolerance && abs(b2 - b) <= tolerance) {
gameEnded = true;
team2Won = true;
}
// Display the instruction
fill(0, 0, 0);
textSize(32);
text("Adjust colors to match!", 600, 300);
} else {
// Transition to end screen
screen = 2;
}
}
void drawHorizontalWaves(float xPos, float yPos, float wWidth, float wHeight, float cR, float cG, float cB) {
color waterColor = color(cR, cG, cB);
for (float x = xPos; x < xPos + wWidth; x++) {
float y = yPos + waveHeight * sin(waveFrequency * (x + offset));
stroke(waterColor);
line(x, y, x, yPos + wHeight);
}
stroke(waterColor);
noFill();
beginShape();
for (float x = xPos; x < xPos + wWidth; x++) {
float y = yPos + waveHeight * sin(waveFrequency * (x + offset));
vertex(x, y);
}
endShape();
offset -= waveSpeed;
if (offset < -wWidth) {
offset = 0;
}
}
void drawVerticalWaves(float xPos, float yPos, float vWidth, float vHeight, float cR, float cG, float cB) {
color waterColor = color(cR, cG, cB);
stroke(waterColor);
strokeWeight(verticalWaveThickness);
noFill();
beginShape();
for (float y = yPos; y < yPos + vHeight; y++) {
float x = xPos + waveHeight * sin(waveFrequency * (y + verticalOffset));
vertex(x, y);
}
endShape();
verticalOffset -= verticalWaveSpeed;
if (verticalOffset < -vHeight) {
verticalOffset = 0;
}
}
void endScreen() {
background(0);
textAlign(CENTER, CENTER);
textSize(48);
// Determine winner or timeout result
if (team1Won) {
sound.stop();
photo2.resize(1260, 700);
image(photo2, 0, 0);
if (!sound1.isPlaying()) { // Play win sound if it's not already playing
sound1.play();
sound1.stop();
}
} else if (team2Won) {
sound.stop();
photo3.resize(1260, 700);
image(photo3, 0, 0);
//fill(color(0, 255, 255));
//text("Team2 Won!", width / 2, height / 3);
if (!sound1.isPlaying()) { // Play win sound if it's not already playing
sound1.play();
sound1.stop();
}
} else {
sound.stop();
photo4.resize(1260, 700);
image(photo4, 0, 0);
//fill(color(255, 255, 50));
//text("Maybe stay on EASY", width / 2, height / 3);
if (!sound2.isPlaying()) { // Play lose sound if it's not already playing
sound2.play();
}
}
// Reset Instruction
// Check for reset button press from Arduino
if (arduino_values[6] == 1) {
resetGame();
}
}
void resetGame() {
screen = 0;
startTime = 0;
gameEnded = false;
team1Won = false;
team2Won = false;
difficulty = 0;
tolerance = 20;
r = random(255);
g = random(255);
b = random(255);
delay(500);
}
void getSerialData() {
while (serialPort.available() > 0) {
String in = serialPort.readStringUntil(10); // 10 = '\n' Linefeed in ASCII
if (in != null) {
print("From Arduino: " + in);
String[] serialInArray = split(trim(in), ",");
if (serialInArray.length == NUM_OF_VALUES_FROM_ARDUINO) {
for (int i = 0; i < serialInArray.length; i++) {
arduino_values[i] = int(serialInArray[i]);
}
}
}
}
}
Coding Challenges:
Connecting the arcade button with the integrated LED to synchronize properly with the game actions was tricky. The LED would light up at unexpected moments, requiring changing to the debounce logic in the code.
While integrating the Arduino with Processing, there were occasional failures in getting stable connection due to mismatched COM port settings.
Mapping the potentiometer values to consistent RGB outputs was a challenge, as slight variations in readings caused fluctuations in color display. Also, creating waves was hard, to make them as smooth sinusoidal waves we tried several times with different dimensions.
Fabrication Challenges:
Fixing the potentiometers inside the box was challenging because their bases didn’t align properly with the holes. Extra adhesive and rubber material were needed to secure them in place without interfering with the turning mechanism. We've tried with different sizes and materials to make sure the wheels were attached to the box firmly.
The potentiometers and wires connections became loose during assembly, requiring us to secure them with stretching rubber and soldering.
Excess glue accidentally seeped into moving parts of the potentiometers and button, temporarily immobilizing them. Cleaning and reassembling these components took considerable effort.
After assembling everything, we realized we forgot to add a hole to the side of the box for the USB cable. Starting from the beginning was not the option as we superglued the wheels, so we improvized, used the sharp cutter to cut the little hole.
These challenges provided valuable lessons in both the design and assembly process, improving our problem-solving and improvizing skills. We understood that we needed more beforehand planning rather than assembling everything immediately.
This project was a great learning experience for both coding and building hardware. We successfully created a system where potentiometers control colors, and a button lets users interact with the game. Although we faced challenges like wiring issues, coding errors, and difficulties assembling the box, solving these problems helped us improve our technical and teamwork skills. In the end, we built a functional and fun project that works as planned, and we’re proud of what we achieved.