Most stages light everything at full, whether anyone is standing there or not. Spot Me, Please is a 150 mm cardboard theatre that measures where its actor stands and gives light only where it is needed.

Making Process
How it was built?



Concept
What is it?

The Loop
How it works

Schematic Diagram
How is it connected ?

Bills of Materials
What it takes?

The Code
This is what makes it work
// ================= CENTER STAGE LIGHTING – FINAL =================
// WIRING
// Ultrasonic (SIG) -> D7 Buzzer -> D2 OLED -> I2C
// Potentiometer -> A0 Servo -> D4 (Grove YELLOW wire = signal,
// red = 5V, black = GND, white = unused)
// Servo shaft 35 mm beside the floor slot, so +-60 degrees = 15-135 mm
// LEDs: left -> 10, 11 center -> 6, 9 right -> 3, 5
//
// HOW IT WORKS
// Turn the knob -> the servo walks the figure to that spot.
// Leave the knob -> the figure stays where it is.
// The sensor measures the figure -> left/right LEDs share the light,
// center LEDs + drums come on at CENTER STAGE.
// ==================================================================
#include “Arduino_SensorKit.h” // OLED screen
#define SENSOR 7
#define BUZZER 2
#define SERVO_PIN 4
#define POT A0
#define MIN_ANGLE 30 // servo end 1 (90 – 60)
#define MAX_ANGLE 150 // servo end 2 (90 + 60)
#define NEAR 20 // figure distance (mm) at the near end
#define FAR 130 // figure distance (mm) at the far end
#define EDGE 160 // readings from here up = back wall
#define MIN_GLOW 10 // LEDs never go below 10%
#define LOST 40 // empty readings before lights go out (~1.5 s)
#define STEP 3 // degrees per loop: 1 = slow, 2 = medium, 3 = fast
// LED pins in order: left, left, center, center, right, right
int leds[] = {10, 11, 6, 9, 3, 5};
// Brightness the eye sees as 0%, 5%, 10% … 100%
int eyeLevel[] = {0, 0, 2, 4, 7, 12, 18, 25, 34, 44, 56, 68, 83, 99, 116, 135, 156, 178, 202, 228, 255};
// Drum groove: kick hat hat kick snare hat kick hat
int drumPitch[] = {1000, 250, 250, 1000, 150, 250, 1000, 250};
int drumLength[] = {10, 6, 6, 10, 50, 6, 10, 6};
int angle = 90; // where the servo is now
int history[12]; // last 12 distances (mm)
long count = 0;
int missed = 0; // empty readings in a row
int zone = 1; // 0 = LEFT, 1 = CENTER, 2 = RIGHT
int confirm = 0; // readings a new zone has been seen
int centerFade = 0; // 0 – 10, center lights fade level
int beat = 0; // which drum hit plays next
unsigned long lastBeat = 0;
unsigned long lastScreen = 0;
void setup() {
Serial.begin(115200);
pinMode(BUZZER, OUTPUT);
pinMode(SERVO_PIN, OUTPUT);
Oled.begin();
Oled.setFlipMode(true);
Oled.setFont(u8x8_font_chroma48medium8_r);
Oled.clear();
Oled.setCursor(0, 0); Oled.print(“Dist mm”);
Oled.setCursor(0, 1); Oled.print(“Servo”);
Oled.setCursor(0, 2); Oled.print(“Left %”);
Oled.setCursor(0, 3); Oled.print(“Center%”);
Oled.setCursor(0, 4); Oled.print(“Right %”);
// LED TEST: every LED fades up and down once: left, center, then right
for (int i = 0; i < 6; i++) {
for (int b = 0; b <= 255; b = b + 5) { analogWrite(leds[i], b); delay(4); }
for (int b = 255; b >= 0; b = b – 5) { analogWrite(leds[i], b); delay(4); }
}
for (int i = 0; i < 12; i++) history[i] = EDGE;
// Start where the knob already is, so the servo does not jump at power-up
angle = map(analogRead(POT), 0, 1023, MIN_ANGLE, MAX_ANGLE);
}
void loop() {
// ———- 1. KNOB -> SERVO (follows the knob, only when it is turned) ———-
int knob = analogRead(POT); // 0 – 1023
int target = map(knob, 0, 1023, MIN_ANGLE, MAX_ANGLE); // where the knob points
if (target > angle + 1) angle = angle + STEP; // knob ahead: step forward
if (target < angle – 1) angle = angle – STEP; // knob behind: step back
angle = constrain(angle, MIN_ANGLE, MAX_ANGLE); // never past the slot ends
int pulse = map(angle, 0, 180, 544, 2400); // servo pulse (microseconds)
digitalWrite(SERVO_PIN, HIGH);
delayMicroseconds(pulse);
digitalWrite(SERVO_PIN, LOW);
// ———- 2. MEASURE the distance (mm) ———-
pinMode(SENSOR, OUTPUT);
digitalWrite(SENSOR, HIGH);
delayMicroseconds(10);
digitalWrite(SENSOR, LOW);
pinMode(SENSOR, INPUT);
long distance = pulseIn(SENSOR, HIGH, 30000) * 10 / 58;
if (distance >= 10 && distance < EDGE) { // a real object
history[count % 12] = distance;
count++;
missed = 0;
} else if (missed < LOST) { // nothing seen: keep last position
missed++;
}
bool present = missed < LOST;
// ———- 3. AVERAGE the last 12 ———-
long position = 0;
for (int i = 0; i < 12; i++) position = position + history[i];
position = position / 12;
// ———- 4. ZONE (center = 70 – 90 mm), accepted after 4 readings ———-
int seen = 1;
if (position < 70) seen = 0;
if (position > 90) seen = 2;
if (seen != zone) confirm++;
else confirm = 0;
if (confirm >= 4) {
zone = seen;
confirm = 0;
}
bool centerStage = present && zone == 1;
// ———- 5. LIGHT SHARE (%) ———-
int left = constrain(map(position, NEAR, FAR, 100, 0), 0, 100); // near end = left 100%
int right = 100 – left; // far end = right 100%
if (centerStage && centerFade < 10) centerFade++;
if (!centerStage && centerFade > 0) centerFade–;
int center = centerFade * 10; // 0 -> 100%
left = map(centerFade, 0, 10, left, left / 2); // sides drop to half
right = map(centerFade, 0, 10, right, right / 2);
left = constrain(left, MIN_GLOW, 100); // never fully dark
right = constrain(right, MIN_GLOW, 100);
if (!present) {
left = 0;
center = 0;
right = 0;
}
int level[] = {left, left, center, center, right, right};
for (int i = 0; i < 6; i++) analogWrite(leds[i], eyeLevel[level[i] / 5]);
// ———- 6. DRUMS on center stage, one hit every 150 ms ———-
if (centerStage) {
if (millis() – lastBeat >= 150) {
lastBeat = millis();
for (int i = 0; i < drumLength[beat]; i++) {
digitalWrite(BUZZER, HIGH);
delayMicroseconds(drumPitch[beat]);
digitalWrite(BUZZER, LOW);
delayMicroseconds(drumPitch[beat]);
}
beat++;
if (beat == 8) beat = 0;
}
} else {
beat = 0;
}
// ———- 7. SCREEN + SERIAL, 4 times a second ———-
if (millis() – lastScreen > 250) {
lastScreen = millis();
Oled.setCursor(8, 0); Oled.print(position); Oled.print(” “);
Oled.setCursor(8, 1); Oled.print(angle); Oled.print(” “);
Oled.setCursor(8, 2); Oled.print(left); Oled.print(” “);
Oled.setCursor(8, 3); Oled.print(center); Oled.print(” “);
Oled.setCursor(8, 4); Oled.print(right); Oled.print(” “);
Oled.setCursor(0, 6);
if (!present) Oled.print(“NO OBJECT “);
else if (zone == 0) Oled.print(“LEFT ZONE “);
else if (zone == 1) Oled.print(“CENTER STAGE!”);
else Oled.print(“RIGHT ZONE “);
Serial.print(“Knob “); Serial.print(knob);
Serial.print(” Servo “); Serial.print(angle);
Serial.print(” Dist “); Serial.print(position);
Serial.print(” L “); Serial.print(left);
Serial.print(” C “); Serial.print(center);
Serial.print(” R “); Serial.println(right);
}
delay(15); // loop pace: 3 degrees every ~20 ms
}
The Model
The Stage and the performance

The Video Narrative
Watch here ->
Digital twin Video
Scan the QR and Watch!
