Learn

A Simon says game with four LEDs and four buttons

Advanced

Or see every lesson.

  • 455parts on the bench
  • 43boards running now
  • 1.00xreal time, on every board
ESP32-C3: Simon sayslive0.000 s 0.00x
Press Run, watch the lamp, then press the button under it
The Simon example: four lamps on GPIO 2 to 5, four buttons on GPIO 18 to 21. Watch the lamp, then press the button under it.

Simon is the memory game from the 1970s: the machine flashes a color, you press it back, it flashes that color and one more, and so on until you slip. It is one of the best first game projects because it uses only LEDs and buttons, and the code teaches arrays, loops and state in under a hundred lines.

The circuit above runs it. Press Run, watch for the lamp that lights (it is the yellow one), then press the button under it. The game adds one step every round, up to sixteen. Get one wrong and all four lamps flash three times and a new game starts. The serial monitor says how far you got.

The board here is an ESP32-C3, but the sketch is plain Arduino code: digitalWrite, digitalRead, delay and random. Change the eight pin numbers and it runs on an Uno or a Nano with no other change.

What you need

  • An ESP32-C3 board, or an Arduino Uno or Nano
  • Four LEDs: red, yellow, green and blue
  • Four 220 ohm resistors
  • Four pushbuttons
  • A breadboard and jumper wires

Wiring

Part and pinGoes toNote
Red LED, through 220 ohmGPIO 2LED cathode to GND
Yellow LED, through 220 ohmGPIO 3
Green LED, through 220 ohmGPIO 4
Blue LED, through 220 ohmGPIO 5
Button under redGPIO 18Other leg to GND, internal pull-up
Button under yellowGPIO 19
Button under greenGPIO 20
Button under blueGPIO 21

How the game works

At the start the sketch fills an array of sixteen steps with random numbers from 0 to 3, one per lamp. A variable called length says how many of them are in play, and it starts at 1. At the start of every round, the sketch flashes steps 0 to length minus 1, each lamp on for 350 ms with a 175 ms gap, then waits for the player to press them back in the same order.

Reading the buttons is a simple loop. pressedButton() checks the four pins and returns the first one that reads LOW, or -1 if none does. The game waits until it gets something, lights that lamp while the button is down, and compares it with the step it expects. A match moves on to the next step; a mismatch ends the game.

When the whole round is right, the serial monitor prints "round 1 correct" (or 2, or 3), length goes up by one and the next round starts. At sixteen the array is used up, the lamps flash five times and a new game begins. There is no timer, so a player can take as long as they like on each step. Adding one is the first thing most people change.

The sketch

The lamps and buttons live in two arrays, so the same index means the same color everywhere. Button i always answers lamp i, which is what keeps the code short.

Notice that random() is never seeded. On a board with no clock that means the sequence is the same every time it powers up: in the circuit above it always starts yellow, yellow, yellow. That is handy while you test. For a real game, add randomSeed(analogRead(A0)) on an Uno with A0 left unconnected, or seed from micros() at the moment of the first button press, which no one can predict.

The Simon sketch (the full version is in the editor)
const int LEDS[] = {2, 3, 4, 5};
const int BUTTONS[] = {18, 19, 20, 21};
const int COUNT = 4;
const int MAX_LEN = 16;

uint8_t sequence[MAX_LEN];
int length = 1;

static void flash(int i, int ms) {
  digitalWrite(LEDS[i], HIGH);
  delay(ms);
  digitalWrite(LEDS[i], LOW);
  delay(ms / 2);
}

static int pressedButton() {
  for (int i = 0; i < COUNT; i++) {
    if (digitalRead(BUTTONS[i]) == LOW) return i;
  }
  return -1;
}

void loop() {
  for (int i = 0; i < length; i++) flash(sequence[i], 350);
  delay(200);

  for (int i = 0; i < length; i++) {
    int got = -1;
    while ((got = pressedButton()) < 0) delay(1);  // delay(1) lets the watchdog see the chip is alive
    digitalWrite(LEDS[got], HIGH);
    if (got != sequence[i]) {
      digitalWrite(LEDS[got], LOW);
      Serial.print("wrong at step ");
      Serial.print(i + 1);
      Serial.print(" of ");
      Serial.println(length);
      allFlash(3, 120);
      waitForRelease();
      newGame();
      return;
    }
    waitForRelease();
    digitalWrite(LEDS[got], LOW);
  }

  Serial.print("round ");
  Serial.print(length);
  Serial.println(" correct");
  length++;
  delay(500);
}

Make it yours

Add sound. A passive buzzer on a spare pin and tone(pin, freq, 300) inside flash() gives each color its own note. Pick four that sound good together, such as 330, 392, 494 and 659 Hz.

Add a time limit. Record millis() when the round starts waiting, and end the game if no button comes in two seconds. That turns the read loop into the "do two things at once" pattern every bigger project needs.

Make it faster as it grows. Pass a shorter time to flash() as length rises, for example 350 minus 15 times the round, with a floor of 150.

Keep a high score. Store the best round in a variable and print it when a game ends. On an Uno, EEPROM.put() keeps it through a power cut.

Common mistakes

A button that reads pressed all the time. Each button goes from its pin to GND, and the pin needs INPUT_PULLUP. With plain INPUT and no resistor the pin floats and the game sees presses that never happened.

Every press counts twice. Without waitForRelease() the loop sees the same press on the next step and calls it the next answer. The sketch waits until all buttons are up, then 20 ms more for the contacts to settle.

Buttons wired across the same side of the breadboard gap. A four-leg button joins its legs in pairs; put it across the center channel and use legs on opposite sides, or it is always closed.

LEDs with no resistor. A 220 ohm resistor per LED keeps the current to about 6 mA on a 3.3 V board and about 14 mA on a 5 V one.

On a real ESP32-C3 board, check which pins are free. GPIO 18 and 19 are the chip’s own USB pins, so on boards that use native USB for programming, buttons there can cut the serial connection. Moving the buttons to other free GPIOs and changing the BUTTONS array fixes it.

Questions

How do I make a Simon says game with Arduino?

Wire four LEDs through 220 ohm resistors to four pins and four buttons from four more pins to GND. Fill an array with random numbers from 0 to 3, flash the first N, then read the buttons and compare. If the player gets all N right, add one and repeat.

Why does my Simon game play the same sequence every time?

Because random() was never seeded. Call randomSeed() once in setup with something unpredictable, such as analogRead on an unconnected pin, or the micros() value when the player first presses a button.

Can I build Simon in Tinkercad?

Tinkercad Circuits has LEDs, buttons and the Uno, so yes. In Mokxi you can open and run the version above with no account, and switch it to an Uno by changing the pin numbers.

Do I need a buzzer?

No. The game works with lights alone. A passive buzzer and tone() add a note per color, which makes it much easier to remember long sequences.

Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.