Preparation

Downloads

Scroll down on this page and click “Arduino IDE” to download it. This software lets you upload code to your arcade device.

After installing the Arduino IDE, download the driver as well. The driver allows your computer to recognize the ESP32 so you can upload code through Arduino.

For the driver ensure you download the CP210x VCP as the versions that are not VCP are not the correct versions.

Preparation

Downloading Library's

In Arduino IDE, start by creating a new sketch. Open the Libraries Manager, search for “Adafruit GFX Library” and “Adafruit SSD1306,” and install both libraries.

Preparation

Downloading the Boards

Now, navigate to the Boards Manager page as shown in the image. Click the search bar and type “esp32.”

Preparation

Downloading the Boards

Next, you should see “esp32 by Espressif Systems.” Click Install to install the board in Arduino.

Preparation

Downloading the Boards

Then, go to the Tools tab and click the port that your Sparkbyte is plugged into. This may look different depending on whether you’re on Mac or Windows, but the steps are the same.

Last Step

Uploading the code

Once you’ve finished all preparation steps, you’re ready to upload the code. In the Arduino IDE, create a new sketch and delete the default text that appears. Then copy the code provided below and paste it into the empty sketch. Click the upload arrow in the top-left corner to send the code to your board. Your mini arcade is now ready to play!

Good to know

Functions

The green button moves the menu selector up, and the blue button moves it down. The red button is used to select items in the menu and during gameplay.

To upload the code, the arcade must be plugged into a computer. After the code is uploaded, it can be powered by any USB power source such as: a wall outlet, power bank, or computer - to play anywhere.

Learn what the code means!

Hover over any line of code to see what it does.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// OLED setup
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Pin assignments
const int ledPins[4] = {25, 26, 27, 14};
const int btnPins[4] = {5, 18, 12, 19};
const int buzzerPin = 4;
// Button mapping for menu navigation
#define BTN_GREEN 3
#define BTN_BLUE 1
#define BTN_RED 0
// Game names for the menu
const char* gameNames[] = {"Memory Game", "Tower Stack", "Reaction Game", "Dino Runner"};
int menuIndex = 0;
const int numGames = 4;
// Game states
enum GameState {MENU, MEMORY, STACKER, REACTION, DINO, GAME_OVER, WINNER};
GameState currentGame = MENU;
int memoryHighScore = 0;
int stackerHighScore = 0;
int reactionHighScore = 0;
int dinoHighScore = 0;
int lastScore = 0;
bool gotNewHigh = false;
// --- Animated Menu Variables ---
int menuSelY = 0;
const int menuItemHeight = 13;
// --- Buzzer beep helper ---
void beep(int duration = 100, int freq = 1000) {
tone(buzzerPin, freq, duration);
delay(duration);
noTone(buzzerPin);
}
bool buttonPressed(int index) {
if (digitalRead(btnPins[index]) == LOW) {
delay(20);
while (digitalRead(btnPins[index]) == LOW);
return true;
}
return false;
}
// --- Loading Animation ---
void showLoadingAnimation() {
int x = 54, y = 28, r = 6;
display.clearDisplay();
display.setTextSize(2);
display.setCursor(24, 10);
display.print("Loading");
display.display();
for (int i = 0; i < 18; i++) {
int phase = i % 4;
for (int d = 0; d < 4; d++) {
float angle = d * 1.5708; // 90 deg
int dx = x + cos(angle) * r;
int dy = y + sin(angle) * r;
if (d == phase)
display.fillCircle(dx, dy, 3, SSD1306_WHITE);
else
display.fillCircle(dx, dy, 2, SSD1306_WHITE);
}
display.display();
delay(60);
for (int d = 0; d < 4; d++) {
float angle = d * 1.5708;
int dx = x + cos(angle) * r;
int dy = y + sin(angle) * r;
display.fillCircle(dx, dy, 3, SSD1306_BLACK);
}
}
display.clearDisplay();
display.display();
}
// --- Animated Menu Drawing ---
void drawMenu(int highlightY) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.fillRect(0, highlightY, SCREEN_WIDTH, menuItemHeight, SSD1306_INVERSE);
for(int i=0; i<numGames; i++) {
int y = i * menuItemHeight;
display.setCursor(8, y+2);
if (y == highlightY) {
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
} else {
display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
}
display.print(gameNames[i]);
display.print(" HS:");
if(i == 0) display.print(memoryHighScore);
else if(i == 1) display.print(stackerHighScore);
else if(i == 2) display.print(reactionHighScore);
else if(i == 3) display.print(dinoHighScore);
display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
}
display.setCursor(0, highlightY + 2);
display.print(">");
display.display();
}
// --- Animated showMenu() ---
void showMenuAnimated(int oldIndex, int newIndex) {
int oldY = oldIndex * menuItemHeight;
int newY = newIndex * menuItemHeight;
int steps = abs(newY - oldY) / 2;
if (steps < 1) steps = 1;
for(int s = 0; s <= steps; s++) {
int y = oldY + (newY - oldY) * s / steps;
drawMenu(y);
delay(15);
}
menuSelY = newY;
drawMenu(menuSelY);
}
void showMenuFirst() {
menuSelY = menuIndex * menuItemHeight;
drawMenu(menuSelY);
}
void gameOver(bool winner) {
display.clearDisplay();
display.setTextSize(2);
display.setCursor(12,12);
if (gotNewHigh) {
display.print("NEW HIGH");
display.setCursor(24,35);
display.print("SCORE!");
} else if (winner) {
display.print("Winner!!!");
} else {
display.print("Game Over");
}
display.display();
beep(300);
display.setTextSize(1);
display.setCursor(0,48);
display.print("Score: "); display.println(lastScore);
display.setCursor(0,56);
display.print("Red=Again Blue=Menu");
display.display();
while(true) {
if(buttonPressed(BTN_RED)) {
delay(500);
gotNewHigh = false;
if (menuIndex == 0) currentGame = MEMORY;
else if (menuIndex == 1) currentGame = STACKER;
else if (menuIndex == 2) currentGame = REACTION;
else if (menuIndex == 3) currentGame = DINO;
return;
}
if(buttonPressed(BTN_BLUE)) {
delay(500);
gotNewHigh = false;
currentGame = MENU;
showMenuFirst();
return;
}
}
}
// --- Memory Game with Easy/Hard Mode ---
void playMemoryGame() {
int mode = 0;
while (1) {
display.clearDisplay();
display.setTextSize(2);
display.setCursor(10, 10);
display.print("Memory");
display.setTextSize(1);
display.setCursor(15, 36);
display.print("Green: Easy Blue: Hard");
display.display();
if (buttonPressed(BTN_GREEN)) { mode = 0; break; }
if (buttonPressed(BTN_BLUE)) { mode = 1; break; }
delay(100);
}
int maxSeqLen = mode ? 16 : 10;
int patternLen = mode ? 4 : 2;
int playDelay = mode ? 180 : 350;
int pauseDelay = mode ? 120 : 200;
int numColors = mode ? 4 : 2;
int pattern[maxSeqLen];
randomSeed(millis());
lastScore = 0;
gotNewHigh = false;
for(int i=0; i<maxSeqLen; i++) {
pattern[i] = random(0, numColors);
}
while(1) {
display.clearDisplay();
display.setCursor(0,0);
display.setTextSize(1);
display.println("Watch...");
display.display();
delay(700);
for(int i=0; i<patternLen; i++) {
digitalWrite(ledPins[pattern[i]], HIGH);
beep(100,600+pattern[i]*200);
delay(playDelay);
digitalWrite(ledPins[pattern[i]], LOW);
delay(pauseDelay);
}
display.clearDisplay();
display.setCursor(0,0);
display.setTextSize(1);
display.println("Repeat!");
display.display();
for(int i=0; i<patternLen; i++) {
bool pressed = false;
int pressedColor = -1;
unsigned long t0 = millis();
while(!pressed && (millis()-t0 < 3500)) {
for(int j=0;j<numColors;j++) {
if(buttonPressed(j)) {
digitalWrite(ledPins[j], HIGH);
beep(100,600+j*200);
delay(120);
digitalWrite(ledPins[j], LOW);
pressedColor = j;
pressed = true;
break;
}
}
}
if(pressedColor != pattern[i]) {
lastScore = patternLen-1;
if(lastScore > memoryHighScore) {
memoryHighScore = lastScore;
gotNewHigh = true;
}
display.clearDisplay();
display.setTextSize(3);
display.setCursor(40,20);
display.print("X");
display.display();
beep(500,200);
delay(1000);
currentGame = GAME_OVER;
return;
}
}
patternLen++;
if(patternLen > maxSeqLen) {
lastScore = patternLen-1;
if(lastScore > memoryHighScore) {
memoryHighScore = lastScore;
gotNewHigh = true;
}
currentGame = WINNER;
return;
}
display.clearDisplay();
display.setCursor(20,20);
display.setTextSize(2);
display.print("Good!");
display.display();
beep(200,1200);
delay(800);
}
}
// --- Tower Stack (now taller!) ---
void playStackerGame() {
const int rows = 16;
const int cols = 8;
const int blockWidth = SCREEN_WIDTH / cols;
const int blockHeight = SCREEN_HEIGHT / rows;
const int startStackCols = 3;
int stackCols = startStackCols;
int stackRow = 0;
int stackPos = 0;
int dir = 1;
int speed = 150;
int blocks[rows][cols] = {0};
lastScore = 0;
gotNewHigh = false;
while (true) {
display.clearDisplay();
for (int r = 0; r < stackRow; r++) {
for (int c = 0; c < cols; c++) {
if (blocks[r][c]) {
display.fillRect(c * blockWidth, SCREEN_HEIGHT - (r + 1) * blockHeight, blockWidth - 1, blockHeight - 1, SSD1306_WHITE);
}
}
}
if (stackRow < rows) {
for (int i = 0; i < stackCols; i++) {
int col = stackPos + i;
if (col < cols) {
display.fillRect(col * blockWidth, SCREEN_HEIGHT - (stackRow + 1) * blockHeight, blockWidth - 1, blockHeight - 1, SSD1306_WHITE);
}
}
}
display.display();
delay(speed);
if(dir == 1) {
stackPos++;
if(stackPos > cols - stackCols) {
stackPos = cols - stackCols;
dir = -1;
}
} else {
stackPos--;
if(stackPos < 0) {
stackPos = 0;
dir = 1;
}
}
if (buttonPressed(BTN_RED)) {
int newBlocks[cols] = {0};
if (stackRow == 0) {
for (int i = 0; i < stackCols; i++) {
int col = stackPos + i;
if (col < cols) newBlocks[col] = 1;
}
} else {
for (int i = 0; i < stackCols; i++) {
int col = stackPos + i;
if (col < cols && blocks[stackRow - 1][col]) {
newBlocks[col] = 1;
}
}
}
int newCols = 0;
for (int c = 0; c < cols; c++) {
blocks[stackRow][c] = newBlocks[c];
if (newBlocks[c]) newCols++;
}
if (newCols == 0) {
lastScore = stackRow;
if(lastScore > stackerHighScore) {
stackerHighScore = lastScore;
gotNewHigh = true;
}
currentGame = GAME_OVER;
return;
}
stackCols = newCols;
stackRow++;
if (stackRow >= rows) {
lastScore = stackRow;
if(lastScore > stackerHighScore) {
stackerHighScore = lastScore;
gotNewHigh = true;
}
currentGame = WINNER;
return;
}
speed = max(35, speed - 15);
stackPos = 0;
dir = 1;
beep(100, 800 + stackRow * 40);
}
}
}
// --- Reaction Game (2 seconds) ---
void playReactionGame() {
int score = 0;
randomSeed(millis());
lastScore = 0;
gotNewHigh = false;
while(true) {
int color = random(0,4);
digitalWrite(ledPins[color], HIGH);
display.clearDisplay();
display.setCursor(0,0);
display.setTextSize(1);
display.println("Press the");
switch(color){
case 0: display.println("RED!"); break;
case 1: display.println("BLUE!"); break;
case 2: display.println("WHITE!"); break;
case 3: display.println("GREEN!"); break;
}
display.display();
unsigned long t0 = millis();
bool pressed = false;
while(millis()-t0 < 2000 && !pressed) {
for(int i=0;i<4;i++) {
if(buttonPressed(i)) {
if(i == color) {
score++;
beep(100,1200);
pressed = true;
break;
} else {
lastScore = score;
if(lastScore > reactionHighScore) {
reactionHighScore = lastScore;
gotNewHigh = true;
}
display.clearDisplay();
display.setTextSize(3);
display.setCursor(40,20);
display.print("X");
display.display();
beep(500,200);
delay(1000);
currentGame = GAME_OVER;
digitalWrite(ledPins[color], LOW);
return;
}
}
}
}
digitalWrite(ledPins[color], LOW);
if(!pressed) {
lastScore = score;
if(lastScore > reactionHighScore) {
reactionHighScore = lastScore;
gotNewHigh = true;
}
display.clearDisplay();
display.setTextSize(3);
display.setCursor(40,20);
display.print("X");
display.display();
beep(500,200);
delay(1000);
currentGame = GAME_OVER;
return;
}
delay(500);
}
}
// --- Dino Runner Game (slower difficulty increase) ---
void playDinoGame() {
const int groundY = SCREEN_HEIGHT - 12;
const int dinoX = 16;
const int dinoW = 12, dinoH = 12;
int dinoY = groundY - dinoH;
int dinoVY = 0;
bool isJumping = false;
int jumpPower = 9;
int gravity = 1;
int obstacleW = 8, obstacleH = 16;
int obstacleX = SCREEN_WIDTH;
int obstacleGapMin = 30, obstacleGapMax = 80;
int obstacleY = groundY - obstacleH + 4;
int speed = 4;
int speedupInterval = 3;
int maxSpeed = 10;
unsigned long lastFrame = 0;
unsigned long minFrameDelay = 30;
int score = 0;
gotNewHigh = false;
randomSeed(millis());
while (true) {
unsigned long now = millis();
if (now - lastFrame < minFrameDelay) continue;
lastFrame = now;
if (isJumping) {
dinoY -= dinoVY;
dinoVY -= gravity;
if (dinoY >= groundY - dinoH) { dinoY = groundY - dinoH; isJumping = false; }
}
if (!isJumping && buttonPressed(BTN_RED)) {
dinoVY = jumpPower;
isJumping = true;
beep(40, 880);
}
obstacleX -= speed;
if (obstacleX + obstacleW < 0) {
obstacleX = SCREEN_WIDTH + random(obstacleGapMin, obstacleGapMax);
score++;
beep(10, 1400);
if (score % speedupInterval == 0 && speed < maxSpeed) {
speed++;
}
}
if (dinoX + dinoW > obstacleX && dinoX < obstacleX + obstacleW) {
if (dinoY + dinoH > obstacleY) {
lastScore = score;
if(lastScore > dinoHighScore) {
dinoHighScore = lastScore;
gotNewHigh = true;
}
beep(300, 200);
currentGame = GAME_OVER;
return;
}
}
display.clearDisplay();
display.drawLine(0, groundY + dinoH, SCREEN_WIDTH, groundY + dinoH, SSD1306_WHITE);
display.fillRect(dinoX, dinoY, dinoW - 2, dinoH, SSD1306_WHITE);
display.fillCircle(dinoX + dinoW - 2, dinoY + 4, 3, SSD1306_WHITE);
display.fillRect(obstacleX, obstacleY, obstacleW, obstacleH, SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0,0);
display.print("Score: "); display.print(score);
display.print(" HS:"); display.print(dinoHighScore);
display.display();
}
}
// --- Arduino Setup/Loop ---
void setup() {
Serial.begin(115200);
for (int i = 0; i < 4; i++) {
pinMode(ledPins[i], OUTPUT);
pinMode(btnPins[i], INPUT_PULLUP);
digitalWrite(ledPins[i], LOW);
}
pinMode(buzzerPin, OUTPUT);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("SSD1306 allocation failed");
for(;;);
}
display.clearDisplay();
display.display();
showMenuFirst();
}
void loop() {
switch (currentGame) {
case MENU: {
static int prevMenuIndex = 0;
if (buttonPressed(BTN_GREEN)) {
int oldIndex = menuIndex;
menuIndex--;
if (menuIndex < 0) menuIndex = numGames - 1;
showMenuAnimated(oldIndex, menuIndex);
prevMenuIndex = menuIndex;
delay(120);
}
if (buttonPressed(BTN_BLUE)) {
int oldIndex = menuIndex;
menuIndex++;
if (menuIndex >= numGames) menuIndex = 0;
showMenuAnimated(oldIndex, menuIndex);
prevMenuIndex = menuIndex;
delay(120);
}
if (buttonPressed(BTN_RED)) {
showLoadingAnimation();
display.clearDisplay();
display.display();
delay(80);
if (menuIndex == 0) currentGame = MEMORY;
if (menuIndex == 1) currentGame = STACKER;
if (menuIndex == 2) currentGame = REACTION;
if (menuIndex == 3) currentGame = DINO;
}
break;
}
case MEMORY:
playMemoryGame();
break;
case STACKER:
playStackerGame();
break;
case REACTION:
playReactionGame();
break;
case DINO:
playDinoGame();
break;
case GAME_OVER:
gameOver(false);
break;
case WINNER:
gameOver(true);
break;
}
}