27 April 2026

Team 11 - SRB2Kart Final Controller

 

Sonic Robo Blast 2 Kart Controller

The final controller we built is for the game Sonic Robo Blast 2 Kart. It is designed as a car radio. The idea is built on the irony that the user is ‘playing with the radio’ while driving. It is a way of symbolizing the havoc and chaos in karting games like this.

The left knob (which is typically associated with volume control on a car radio) is used for the speed controls. As the ‘volume’ ramps up, so does the kart velocity. The potentiometer inputs for it are mapped to a range of 1 to 100. The lowest part of this range is used for reverse, the mostly low section is used for idle (no gas), the section above this uses a PMW system to allow for speed ramping, and the highest section is equivalent to holding the accelerate button.

The right button (which is typically the tune button) is used to control the steering, as the player is making an ‘adjustment’ to their kart. It is mapped to a range of -100 to 100 and also uses a form of PMW to make it so gradual turning is possible in the intermediate zones, with maximum turn at the farther ends on each side.

Both knobs have notches in the 3d design to provide feedback on the current position and allow for the user to normalize their frame of reference while controlling.

The final input device is the slider in the center. The slider is an indicator of the radio's channel, and in this case, it is an indicator of the kart's special state. The input is mapped to create 3 zones; The left is used for drifting, the middle is neutral, and the right activates/uses items.

One question we have about our controller design is: Which input device is the weakest, and if you had to replace it with another type of input device, what would it be?

 

(Final Controller)


(Schematic)

#include <Keyboard.h>
#include <Adafruit_CircuitPlayground.h>
#include <Adafruit_Circuit_Playground.h>


float stopwatch = 0;

bool isWPressed = false;
bool isSpacePressed = false;

int speed;
int steer;
int slider;

void setup() {
  // put your setup code here, to run once:
  CircuitPlayground.begin();
  Serial.begin(9600);
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  pinMode(A3, INPUT);
  delay(1000);
}

void loop() {

  ReadSensors();

  HandleSpeed();

  HandleSteering();

  HandleSlider();
 
}

void ReadSensors() {
  //  Read Left Knob (Speed): Map the 0-1023 range to a 1 to 100 range
  speed = map(analogRead(A1), 0, 1023, 1, 100);
  //  Read Right Knob (Steering): Map the 0-1023 range to a -100 to 100
  steer = map(analogRead(A2), 0, 1023, -100, 100);
  //  Read Slider: Map the 0-1023 range to a 1-30 range
  slider = map(analogRead(A3), 0, 1023, 1, 30);
}


void HandleSpeed() {

  if (speed <= 10) {
    // 1 Reverse if dial is at the very bottom
    Keyboard.release('a');
    Keyboard.press('d');

  } else if (speed >=11 && speed <= 30) {
    // 2 No movement (gas off, idling)
    Keyboard.release('d');
    Keyboard.release('a');

  } else if (speed >= 85) {
    // 3 Full Speed
    Keyboard.release('d');
    Keyboard.press('a');

  } else {

    // 4 PWM Speed (variable speed ramps when player is between idling and max speed zones)
    Keyboard.release('d');
    Keyboard.press('a');
    delay(speed*4);
    Keyboard.release('a');
    delay(100);
}

}

void HandleSteering() {
    // 1 Neutral (Deadzone) (-15 to 15)
  if (steer >= -15 && steer <= 15) {
    Keyboard.release(KEY_LEFT_ARROW);
    Keyboard.release(KEY_RIGHT_ARROW);

  } else if (steer <= -50) {
    // 2 Full Left (Hard steer) (-50 or less)
    Keyboard.release(KEY_RIGHT_ARROW);
    Keyboard.press(KEY_LEFT_ARROW);

  } else if (steer >= 50) {
    // 3 Full Right (Hard steer) (50 or more)
    Keyboard.release(KEY_LEFT_ARROW);
    Keyboard.press(KEY_RIGHT_ARROW);

  } else if (steer < -15) {
    // PWM Left (Partial steer)
    Keyboard.release(KEY_RIGHT_ARROW);
    Keyboard.press(KEY_LEFT_ARROW);
   
    // Hold then Release
    delay(180);
    Keyboard.release(KEY_LEFT_ARROW);
   
    // Wait a delay based on strength of steer
    int restTime = map(abs(steer), 15, 50, 80, 2);
    delay(restTime);

  } else if (steer > 15) {
    // PWM Right (Partial steer)
    Keyboard.release(KEY_LEFT_ARROW);
    Keyboard.press(KEY_RIGHT_ARROW);
   
    // Hold then Release
    delay(180);
    Keyboard.release(KEY_RIGHT_ARROW);
   
    // Wait a delay based on strength of steer
    int restTime = map(steer, 15, 50, 80, 2);
    delay(restTime);
  }
}

void HandleSlider() {
  // 1 Lowest Zone (Drift)
  if (slider <= 7) {
    Keyboard.release(' ');
    Keyboard.press('s');
    isSpacePressed = false;

  } else if (slider >= 8 && slider <= 17) {
  // 2 Middle Zone (Neutral)
    Keyboard.release('s');
    Keyboard.release(' ');
   
    // Prevents jitter at the 17/18 border and spamming fire of space (item) button
    if (slider <= 15) {
      isSpacePressed = false;
    }
   
  }   else if (slider >= 18) {
    // 3 Highest Zone (Collect/Use Item)
    Keyboard.release('s');

    if (isSpacePressed == false) {
      Keyboard.press(' ');
      delay(50);
      Keyboard.release(' ');
      isSpacePressed = true; // Set lock so player so slider only uses space ONCE per time it enters the zone (prevents item spam)
    }
  }
}

(Code) 








26 April 2026

Final Project Team 8: Hidden Folks Game Controller


Our game controller is made to reflect the theme of the game Hidden Folks. In the game, players must search through mini landscapes to find certain people, things, or objects. The game, which consists of a black and white color palette, has simple inputs, consisting of zoom in/zoom out buttons, a select button, and a pan button to move across the landscape.Due to the overall simplistic nature of the game, we chose to carry the theme into the controller by creating a treasure map controller. The controller consists of two cylinders and a middle portion, which simultaneously give the player somewhere to grab onto and also visually represent the two scroll portions of an ancient map. The middle portion is decorated overtop with a handdrawn map, integrating both a light sensor and a touchpad into the layout.

Controller Image:


Schematic:


Code:

#include <Mouse.h>
#include <Adafruit_CircuitPlayground.h>

// ---------------- PINS ------------------
// making variables for each pin and what it connects to on the CPE
const int fsrPin = A1;
const int lightSensorPin = A2;
const int potPin = A3;

// ---------------- STATES ----------------
// variables used for keeping info to reference later
bool leftHeld = false;
int lastPotValue = 0;

// ---------------- SETUP -----------------
void setup() {
  // circuit and mouse tracking, along with the 3 analog inputs
  Serial.begin(9600);
  CircuitPlayground.begin();
  Mouse.begin();
  pinMode(fsrPin, INPUT);
  pinMode(lightSensorPin, INPUT);
  lastPotValue = analogRead(potPin);
}

// ---------------- LOOP FUNCTIONS ------------------
void loop() {
  // repeated functions
  handleSensors();
  handleTilt();
  handlePot();
  //lightCheck();
  //fsrCheck;
 
  delay(10);
}

/*void lightCheck() {
  // checking light values
  int lightVal = analogRead(lightSensorPin);
  Serial.print("Light Sensor Value: ");
  Serial.println(lightVal);

  delay(200);
}
*/

/*void fsrCheck() {
  // checking pressure values
  int fsrReading;
  fsrReading = analogRead(fsrPin);
  Serial.print("Analog Reading = ");
  Serial.print(fsrReading);

  delay(200);
}
*/

// ---------------- SENSOR HANDLING ----------------
void handleSensors() {
  // variables to get readings on each sensor / how it activates
  int fsrVal = analogRead(fsrPin);
  int lightVal = analogRead(lightSensorPin);
  bool fsrPressed = fsrVal > 500;              // pressure pad threshold
  bool lightTriggered = lightVal < 600;        // darkness threshold

  // ---------- PRESSURE PAD = HOLD ------------
  // hold down mouse when fsr pressure increases
  if (fsrPressed && !leftHeld) {
    Mouse.press(MOUSE_LEFT);
    leftHeld = true;
  }
  else if (!fsrPressed && leftHeld) {
    Mouse.release(MOUSE_LEFT);
    leftHeld = false;
  }

  // ---------- LIGHT SENSOR = PRESS ----------
  // light sensor activating in darkness presses left click
  static bool lastLightState = false;
  if (lightTriggered && !lastLightState) {
    Mouse.click(MOUSE_LEFT);
  }
  lastLightState = lightTriggered;
}

// ---------------- TILT ----------------
// tilt movement around the screen, adjusting speed as needed
void handleTilt() {
  float x = CircuitPlayground.motionX();
  float y = CircuitPlayground.motionY();
  int moveX = x * 2;
  int moveY = y * 2;
  // dead zone
  if (abs(moveX) < 2) moveX = 0;
  if (abs(moveY) < 2) moveY = 0;
  Mouse.move(moveX,moveY);

// ---------------- POTENTIOMETER ----------------
void handlePot() {
  // potentiometer zooms in and out
  int potValue = analogRead(potPin);
  static int lastPotValue = 0;
  // smooth the reading
  static int potFiltered = 0;
  potFiltered = (potFiltered * 3 + potValue) / 4;

  int diff = potFiltered - lastPotValue;

  if (abs(diff) > 25) {
    int scrollAmount = diff / 30;

    // ignore zoom if fsr or light is active
    if (!leftHeld) {
      Mouse.move(0, 0, scrollAmount);
    }

    lastPotValue = potFiltered;
  }
}

Video:

Team 12- Game Controller

 


Our controller’s shape and overall design is based on the Type-A fighter which acts as a boss in ZeroRanger. This ship is the same type as those used by the player. To control their up and down movement the player tilts the controller forwards and back, this movement is tracked by the accelerometer on the Circuit Playground. To move left and right the player turns the potentiometer in the direction they want to move. The player’s three fire inputs are controlled by light sensors. When the sliding panel above the sensor is moved, it is revealed to the light and inputs the corresponding fire command in game. Fire 1 acts as the select button in menus while fire 2 is the back button. Lastly by moving the switch the player can transform their ship into melee mode then flip the switch back to return to the standard ship. The sections of the controller used to input are colored in orange, allowing players to find them easily and know which parts of the controller will move. The three light sensors are also placed roughly where that weapon will fire from in the game. Alongside the game’s audio and visual feedback the controller provides physical feedback with each input resulting in a motion on the controller. The controller also provides additional visual feedback with the player able to see the revealed light sensor or use the marks on the potentiometer to tell its current position.

Question: How could the design better allow the player to input different actions in quick succession?


Schematic- 


Code-

 #include <Adafruit_CircuitPlayground.h>

#include <Adafruit_Circuit_Playground.h>

#include "Keyboard.h"

 

//define variables for initial light values

int ZeroRead;

int TwoRead;

int ThreeRead;

 

void setup() {

Serial.begin(9600);

CircuitPlayground.begin();

Keyboard.begin();

delay(1000);

//set initial light values

int ZeroRead = analogRead(A0);

int TwoRead = analogRead(A2);

int ThreeRead = analogRead(A3);

}

 

//define variables for controlling release of fire buttons

bool fire1 = 0;

bool fire2 = 0;

bool fire3 = 0;

 

//define variables for controlling transform macro

bool oldform = 0;

bool newform = 1;

 

void loop() {

//Only read inputs if CircuitPlayground switch is set to on position

if (CircuitPlayground.slideSwitch() < 1) {

//Press Z(fire1) while light sensor 1 is uncovered and release it when it is covered

  if (analogRead(A2) > TwoRead + 20) {

    Keyboard.press('z');

    fire1 = 1;

  }

  if (fire1 == 1) {

  if (analogRead(A2) <= TwoRead + 20) {

     Keyboard.release('z');

     fire1 = 0;

     }

  }

//Press X(fire2) while light sensor 2 is uncovered and release it when it is covered

  if (analogRead(A0) < (ZeroRead + 20)) {

    Keyboard.press('x');

    fire2 = 1;

  }

  if (fire2 == 1) {

  if (analogRead(A0) >= (ZeroRead + 20)) {

     Keyboard.release('x');

     fire2 = 0;

     }

  }

//Press C(fire3) while light sensor 3 is uncovered and release it when it is covered

  if (analogRead(A3) < (ThreeRead + 20)) {

    Keyboard.press('c');

    fire3 = 1;

  }

  if (fire3 == 1) {

   if (analogRead(A3) > (ThreeRead + 20)) {

     Keyboard.release('c');

     fire3 = 0;

     }

  }

//Write Left Ctrl(fire1+2+3 macro) when the switch changes position

  if (oldform != newform) {

   if (analogRead(A7) > 1000 ) {

     Keyboard.write(KEY_LEFT_CTRL);

     newform = 0;

  }

  }

  if (oldform == newform) {

    if (analogRead(A7) < 50) {

      Keyboard.write(KEY_LEFT_CTRL);

      newform = 1;

    }

  }

//Move up and down based on accelerometer value

 if (CircuitPlayground.motionY() <= -2) {

  Keyboard.press(KEY_UP_ARROW);

 }

if (CircuitPlayground.motionY() > -2) {

  Keyboard.release(KEY_UP_ARROW);

  }

 if (CircuitPlayground.motionY() >= 2) {

  Keyboard.press(KEY_DOWN_ARROW);

 }

if (CircuitPlayground.motionY() < 2) {

  Keyboard.release(KEY_DOWN_ARROW);

  }

//Move left or right based on potentiometer value

 if (analogRead(A6) <= 400) {

  Keyboard.press(KEY_RIGHT_ARROW);

 }

if (analogRead(A6) > 400) {

  Keyboard.release(KEY_RIGHT_ARROW);

  }

 if (analogRead(A6) >= 700) {

  Keyboard.press(KEY_LEFT_ARROW);

 }

if (analogRead(A6) < 700) {

  Keyboard.release(KEY_LEFT_ARROW);

  }

}

}

Video-

(Due to sudden mechanical failures our controller was not functional to record the video)



 



Final Controller Team 23: golf it

 



Our project is a custom physical controller designed specifically for the game Golf It, with the goal of making the gameplay feel more intuitive, consistent, and thematically connected to real golf. The conceptual model behind our design is based on translating the game’s mouse‑based swing mechanic into a larger, more physical motion that resembles pulling back and swinging a golf club. Instead of relying on small, inconsistent mouse movements, our controller uses a large joystick connected to a potentiometer, allowing players to physically pull back and push forward to control swing strength. This creates a clearer mental model: the farther and faster you move the joystick, the stronger the hit—just like in real golf.

To reinforce the theme, we incorporated a real golf ball and designed the button as a golf tee. The top surface of the controller uses a textured material similar to golf turf, helping players immediately understand the purpose of the device through visual and tactile signifiers. When the player interacts with the joystick or presses the tee‑button, the controller provides direct feedback through on‑screen movement in the game. The potentiometer sends analog values through A2, mapping physical motion to vertical mouse movement, while the button on A1 tells the game that you are ready to swing. These mappings create clarity, pull back to aim power, tilt to aim direction, and press the tee to start swing.

Our design evolved from a small square base into a larger rectangular platform to give players more space for movement and to make the interaction more fun and immersive.

Team Contributions:  
Mathieu: Wiring, coding, input mapping, and controller mechanics
Kai: Physical design, theming, materials, and assembly

Peer Review Question:  
How effectively does our controller communicate its purpose and golf‑themed interactions to a first‑time player without explanation?





Code 


#include <Mouse.h>
#include <Adafruit_CircuitPlayground.h>

int potPin = A2;     // potentiometer
int buttonPin = A1;  // button on A1
int centerValue = 512;
int deadzone = 40;

void setup() {
  CircuitPlayground.begin();
  Mouse.begin();
  Serial.begin(9600);

  pinMode(buttonPin, INPUT_PULLUP);  // button to GND
}

void loop() {

  //  POTENTIOMETER  UP/DOWN WITH SAFE ZONE
  int potValue = analogRead(potPin);
  Serial.println(potValue);
  int diff = potValue - centerValue;

  if (abs(diff) > deadzone) {
    int moveY = diff / 5;
    Mouse.move(0, moveY, 0);
  }

  //  BOARD ROTATED SIDEWAYS  USE motionY FOR LEFT/RIGHT
  float tiltY = CircuitPlayground.motionY();  // up/down tilt
  int moveX = tiltY * 2;                      // convert to left/right
  Mouse.move(moveX, 0, 0);

  // BUTTON  LEFT CLICK 
  static bool buttonWasPressed = false;
  bool buttonPressed = (digitalRead(buttonPin) == LOW);

  if (buttonPressed && !buttonWasPressed) {
    Mouse.click();
    delay(150);
  }

  buttonWasPressed = buttonPressed;

  delay(10);
}








Final Team 1 - Mario Bros. controller

    For this project, our team decided to create a controller for the Game Boy Advance port of Mario Bros., the arcade game. A primary goal our team was connecting controller aesthetics to the aesthetic of the game, and attempting to bridge functionality and believability. To achieve this, we chose plumbing pipes for the base of our design, as plumbing as a job and levels in the sewers are a primary focus of the game. Continuing with the pipe design, we included a valve wheel and wrench as inputs. To connect to the whimsical aspect of the game, we used a POW block, an item from the game, as a button on top of the pipe. Using these aspects to lay everything out, we connected each item to an input to bring the player into the job of the plumber characters. To signify each aspect as a potential input, we displayed the valve wheel and wrench in red to contrast with the mainly green design. The pow block stands as an additional blue object, and the top is lightly lifted, hinting again to the user how it can be interacted with. To connect each input with functionality, the valve wheel can be turned to control the direction of the game character, like one might control water. The wrench on the side is a built-in lever, and moving it up and down can control the player’s jumping and crouching. In-game, hitting the pow block can damage all enemies on screen. In our controller designer, players can hit the POW block, and trigger their ability to run. As the later sequels, the Super Mario Bros. games are known for vocally emoting as the move, loudly speaking at the controller(which the CPE is inside) will additionally allow the player to pause the game.

    Our team’s open ended question is: What size of the controller do you think would best suite the immersion and target audience of the game?

Virgil West:

  • Controller modeling
  • Blog posting
  • Schematics
  • Painting

Finn Albritton:

  • Coding
  • Bug testing
  • Writing
  • Soldering
#include <Keyboard.h>
#include <KeyboardLayout.h>
#include <Adafruit_CircuitPlayground.h>
#include <Adafruit_Circuit_Playground.h>

// Code for Project:Game Controller, Super Mario Bros.
// Final Team 1, Finnegan Albritton and Virgil West

// Global Variables

// Establishes A4 as the first potentiometer
const int potPin = A4;
int potValue = 0;

// Establishes A2 as the second potentiometer
const int secondpotPin = A2;
int secondpotValue = 0;

// Establishes A1 as the button
const int buttonPin = A1;

// Variables to check for inputs that need to be held down
bool crouchToggle = false;
bool buttonState = false;
bool buttonPrevState = false;
bool runToggle = false;

void setup() {
  // Sets up the CPE and Keyboard Controls
  CircuitPlayground.begin();
  Keyboard.begin();

  // Establishes the button mode
  pinMode(buttonPin, INPUT_PULLDOWN);
}


void loop() {

  // Controls function while the CPE switch is on
  if(CircuitPlayground.slideSwitch() == true) {

    //Reads the potentiometers and button
    potValue = analogRead(potPin);
    secondpotValue = analogRead(secondpotPin);
    buttonState = digitalRead(buttonPin);

    // Local variable activates the microphone and checks for sound
    int micCheck = CircuitPlayground.mic.soundPressureLevel(10);


    // Checks the first potentiometer for left and right movement

    // If the potentiometer is turned to the left, the keyboard input makes the character move left
    if(potValue <= 400) {
      Keyboard.press(KEY_LEFT_ARROW);
      delay(100);
      Keyboard.release(KEY_LEFT_ARROW);
    }

    // If the potentiometer is in the middle, the character does not move
    else if (potValue >= 401 && potValue <= 599){
      Keyboard.release(KEY_RIGHT_ARROW);
      Keyboard.release(KEY_LEFT_ARROW);
    }
// If the potentiometer is turned to the right, the keyboard input makes the character move right
    if(potValue >= 600){
      Keyboard.press(KEY_RIGHT_ARROW);
      delay(100);
      Keyboard.release(KEY_RIGHT_ARROW);
    }

    //Checks for the sound level of the user
    // If the user is loud enough, the keyboard input pauses the game
    if(micCheck >= 50){
      Keyboard.press(KEY_RETURN);
      delay(100);
      Keyboard.release(KEY_RETURN);
    }


    // Checks the second potentiometer for crouching and jumping movement

    // If the potentiometer is turned up, the keyboard input makes the character jump
    if(secondpotValue < 300){
      Keyboard.press('z');
    }

    // If the potentiometer is in the middle, the character does not take either action
    if (secondpotValue >= 300 && secondpotValue <= 400){
      // Resets the bool to false if input is not being held down
      crouchToggle = false;
      Keyboard.release(KEY_DOWN_ARROW);
      Keyboard.release('z');
    }

    // If the potentiometer is turned down, the keyboard input makes the character crouch
    if(secondpotValue > 400 && crouchToggle == false){
      // Sets the bool to true to hold down input
      crouchToggle = true;
      Keyboard.press(KEY_DOWN_ARROW);
    }


     //Checks to see if the button is being pressed
    if(buttonState == true){

      //If it is and the bool has not been triggered, the input will be held
      if(runToggle == false){
        Keyboard.press('x');
        runToggle = true;
      }
    }

    //If the button is not being pressed, the bool will reset and input will be released
    if(buttonState == false && runToggle == true){
      Keyboard.release('x');
      runToggle = false;
    }

  }
 
}


Finals Controller Team 20 - Sonic

Final Controller



Video Link: https://www.youtube.com/watch?v=z_nKT3N6RUU

For our project, we chose to create a controller for classic Sonic due to its simplistic inputs and heavy flow-based gameplay. We wanted to develop a controller that would allow players to physically immerse themselves in the fast paced experience in some way. For our controls, we wanted to create two white gloves, inspired by Silver's gloves and powers from the Sonic franchise, that would allow the player to interact with the game world. One hand uses a color sensor, having players grab different-colored gems, paying homage to the chaos emeralds from within the game, with each tied to an input. Players quickly grab and swap different gems to help them control Sonic's movement within the level. But this is not the only form of input we have. On the alternate glove, we have two “rings,” a reference to Silver's power displayed through white or Silver’s rings  in the Sonic franchise. The two rings can come together to complete a circuit, causing Sonic to jump in-game. Through these two forms, players will rapidly move their hands across the surface in front of them to grab at different emeralds, connect rings, and soar through the various fast-paced levels found within the game. From each glove come wires connected to its internal circuits, which go back to a central housing unit that houses the circuit playground. We hoped the gloves would serve as a strong signifier when developing the project, being associated with grabbing and holding, which naturally led to the idea and relationship of grabbing the emeralds. During the primary test, we felt that the motion of bringing the thumb and pointer finger felt natural, and with the two “rings” providing a visual indication, we thought they would work well. Players get the physical feel of grabbing and dropping the emeralds with one glove and the sensation of bringing their fingers together creates tactical senses in the other. Through our controller, we wanted to create a fast-paced, tactile experience that would engage the player in varied ways through their hand, hoping to generate a sense of flow not only in the game but also through the controls. Do you feel that alternate controls in fast-paced games, with more involved elements, would enhance or detract from the experience?

Circuit


Programming

#include <Keyboard.h>

#define S2 A1
#define S3 A2
#define OUT_PIN A3
#define TOUCH_PIN A4
#define SLIDE_SWITCH 7

bool upHeld = false;
bool rightHeld = false;
bool leftHeld = false;

void setup() {
  Serial.begin(9600);

  pinMode(S2, OUTPUT);
  pinMode(S3, OUTPUT);
  pinMode(OUT_PIN, INPUT);

  pinMode(TOUCH_PIN, INPUT_PULLUP);
  pinMode(SLIDE_SWITCH, INPUT_PULLUP);

  Keyboard.begin();
}

unsigned long readColor(bool s2, bool s3) {
  digitalWrite(S2, s2);
  digitalWrite(S3, s3);
  delay(50);
  return pulseIn(OUT_PIN, LOW, 200000);
}

void loop() {

  // Slide switch OFF = everything disabled
  if (digitalRead(SLIDE_SWITCH) == HIGH) {
    Keyboard.releaseAll();
    upHeld = rightHeld = leftHeld = false;
    return;
  }

  // Jump input
  bool jumping = (digitalRead(TOUCH_PIN) == LOW);

  // Read colors
  unsigned long red = readColor(LOW, LOW);
  unsigned long green = readColor(HIGH, HIGH);
  unsigned long blue = readColor(LOW, HIGH);

  // DEBUG 
  Serial.print("R: "); Serial.print(red);
  Serial.print(" G: "); Serial.print(green);
  Serial.print(" B: "); Serial.println(blue);

  // yellow detec
bool isYellow =
  (green > red + 20) &&
  (blue > red + 20) &&
  (green > 130 && green < 200);

  bool isRed = (red < green && red < blue);
  bool isBlue = (blue < red && blue < green);
  bool isGreen = (green < red && green < blue);

  // PRIORITY: JUMP FIRST
  if (jumping) {

    Keyboard.releaseAll();
    upHeld = rightHeld = leftHeld = false;

    Keyboard.press('S');

  } else {

    Keyboard.release('S');

    // YELLOW = ENTER
    if (isYellow) {
      Keyboard.releaseAll();
      upHeld = rightHeld = leftHeld = false;

      Keyboard.write(KEY_RETURN);
      delay(300); // prevent spam
      return;
    }

    // UP (RED)
    if (isRed) {
      if (!upHeld) {
        Keyboard.press(KEY_DOWN_ARROW);
        upHeld = true;
      }
    } else if (upHeld) {
      Keyboard.release(KEY_DOWN_ARROW);
      upHeld = false;
    }

    // RIGHT (BLUE)
    if (isBlue) {
      if (!rightHeld) {
        Keyboard.press(KEY_RIGHT_ARROW);
        rightHeld = true;
      }
    } else if (rightHeld) {
      Keyboard.release(KEY_RIGHT_ARROW);
      rightHeld = false;
    }

    // LEFT (GREEN)
    if (isGreen) {
      if (!leftHeld) {
        Keyboard.press(KEY_LEFT_ARROW);
        leftHeld = true;
      }
    } else if (leftHeld) {
      Keyboard.release(KEY_LEFT_ARROW);
      leftHeld = false;
    }
  }

  delay(50);
}

Credits

Building of the Gloves: John and Charlotte

Creation of Emeralds: Charlot

Programming: Charlot

Writing: John

Video/Photo: Charlotte

Circuit Schematics: John