The theremin is an electronic instrument from 1920 that you play without touching it: you move your hands near two antennas, and the pitch changes. Your version uses the ultrasonic sensor instead of antennas. The closer your hand is, the higher the note. A button switches between a smooth sound that slides like a siren, and a "musical" mode that snaps to the notes of a C major scale, so you can play real tunes.
What you'll learn
- How to turn a distance into a pitch with
map() - How to play notes with
tone()and stop them withnoTone() - How to store a musical scale in an array
- How to smooth jumpy sensor readings
What you need
- UNO R3 board and USB cable (UNO R3 Starter Kit)
- Ultrasonic sensor (UNO R3 Starter Kit)
- Passive buzzer (UNO R3 Starter Kit)
- 1 push button (UNO R3 Starter Kit)
- The printed parts from the 3D printing box below (theremin stand), or the cardboard version described there
- Breadboard and jumper wires (UNO R3 Starter Kit)
Wiring

| From | To |
|---|---|
| Ultrasonic VCC | 5V |
| Ultrasonic Trig | UNO pin 12 |
| Ultrasonic Echo | UNO pin 11 |
| Ultrasonic GND | GND |
| Passive buzzer + | UNO pin 8 |
| Passive buzzer − | GND |
| Push button, one leg | UNO pin 2 |
| Push button, the diagonally opposite leg | GND |
Use the passive buzzer: it plays any note you ask for. The active buzzer can only beep at one pitch.
The sketch
const int TRIG_PIN = 12;
const int ECHO_PIN = 11;
const int BUZZER_PIN = 8;
const int BUTTON_PIN = 2;
const int NEAREST = 5; // cm: the highest note
const int FARTHEST = 50; // cm: the lowest note; further away is silence
// C major scale over two octaves, in hertz
const int SCALE[] = {262, 294, 330, 349, 392, 440, 494, 523, 587, 659, 698, 784, 880, 988, 1047};
const int NOTES = sizeof(SCALE) / sizeof(SCALE[0]);
bool musical = true; // true: notes of the scale, false: smooth siren
bool lastButton = HIGH;
float smooth = 0;
long readCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long us = pulseIn(ECHO_PIN, HIGH, 6000); // give up after 6 ms (about 1 m)
if (us == 0) return -1; // nothing in range
return us / 58;
}
void setup() {
Serial.begin(9600);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
}
void loop() {
bool button = digitalRead(BUTTON_PIN);
if (button == LOW && lastButton == HIGH) {
musical = !musical;
Serial.println(musical ? "Musical mode" : "Siren mode");
delay(200);
}
lastButton = button;
long cm = readCm();
if (cm < 0 || cm > FARTHEST) { // no hand: silence
noTone(BUZZER_PIN);
smooth = 0;
delay(30);
return;
}
cm = constrain(cm, NEAREST, FARTHEST);
if (smooth == 0) smooth = cm;
smooth = smooth * 0.7 + cm * 0.3; // smooth out the jitter
int freq;
if (musical) {
int index = map((long)smooth, FARTHEST, NEAREST, 0, NOTES - 1);
freq = SCALE[constrain(index, 0, NOTES - 1)];
} else {
freq = map((long)smooth, FARTHEST, NEAREST, 150, 1200);
}
tone(BUZZER_PIN, freq);
delay(30);
}
Play it
- Upload the sketch and hold your flat hand about 40 cm above or in front of the sensor. You hear a low note.
- Move your hand slowly closer. In musical mode the sound steps up through the scale, like a piano.
- Press the button for siren mode and move your hand quickly up and down.
- Try a tune: "Twinkle, Twinkle, Little Star" starts with the 1st, 1st, 5th, 5th, 6th, 6th and 5th notes of the scale.
How it works
map() turns one range into another. Here it turns 50 … 5 cm into note number 0 … 14. Because the ranges are written "far to near", a closer hand gives a bigger number and a higher note. In siren mode it maps straight to a frequency between 150 and 1200 Hz instead.
Ultrasonic readings jump by a centimetre or two even when your hand is still. The line smooth = smooth * 0.7 + cm * 0.3 mixes each new reading with the old value, so a single odd reading only moves the note a little. That is a simple low-pass filter.
Try this next
- Change the scale to a pentatonic one (C, D, E, G, A): every combination of notes sounds good.
- Add the LCD from the LCD lesson and show the name of each note.
- Add a second button that plays a short tune you recorded by remembering the last notes.
Troubleshooting
-
It plays all the time, even with no hand: something else is within 50 cm, such as a wall or the ceiling. Make
FARTHESTsmaller, or point the sensor away from it. - No sound at all: check that you used the passive buzzer, and that its + leg is on pin 8.
- The notes jump around: hold your hand flat. Fingers and edges reflect the sound badly.
- A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.