Last updated on

Nano Theremin — A 15-Minute Build with My Son


Leo wandered into the lab on a rainy afternoon and asked what I was working on. I had nothing planned — so I grabbed an Arduino Nano, a starter-kit ultrasonic sensor, and a tiny buzzer, and we built a hand-waving theremin in fifteen minutes. Wave your hand above the sensor and the pitch changes: close = high, far = low.

The whole point was the explanation, not the build. Two components, three jumper wires, and a chance to actually show him what each part does instead of just saying “it’s like magic, buddy.”

The two components

The HC-SR04 is the ear. It’s an ultrasonic rangefinder — two little metal cylinders on the front, one a transmitter and one a receiver. The Nano pulses the trigger pin for 10 microseconds; the sensor fires off an ultrasonic chirp; the chirp bounces off your hand; the receiver hears the echo and pulses the echo pin HIGH for as long as the round trip took. Divide that pulse length by 58 and you get the distance in centimeters. That last bit is fun to explain: sound travels at ~343 m/s, and the pulse measures the round trip, so half of (343 m/s × microseconds) gives you a distance that conveniently divides by 58 for centimeters.

The passive buzzer is the mouth. A passive buzzer is just a tiny speaker — it doesn’t make any sound on its own. You have to drive it with a square wave at the frequency you want to hear. Send it 440 Hz and it plays an A. The Arduino’s built-in tone() function generates the square wave for you. (An active buzzer is the opposite: power it and it beeps at a fixed pitch. We didn’t want that. We wanted a real instrument.)

The whole instrument is: read distance, map distance to frequency, send frequency to the buzzer. Repeat 50 times a second.

Parts

  • Inland Nano 3 (ATmega328P, CH340 USB-serial)

  • Keyes 37-in-1 module #37, 4-pin (VCC/Trig/Echo/GND)

  • Keyes 37-in-1 module #27, 2-pin

  • Half-size or larger

  • Data-capable

Wiring

ComponentPinArduino Nano
HC-SR04VCC5V
HC-SR04GNDGND
HC-SR04TrigD9
HC-SR04EchoD10
Buzzer+D8
BuzzerGND

Both components run off the Nano’s 5V rail. Total draw is around 60 mA — USB power is plenty.

Breadboard wiring close-up

The sketch

No libraries. About 30 lines.

const int TRIG_PIN  = 9;
const int ECHO_PIN  = 10;
const int BUZZ_PIN  = 8;

const int DIST_MIN  = 5;    // cm — closer than this = silence
const int DIST_MAX  = 50;   // cm — farther than this = silence
const int FREQ_LOW  = 150;  // Hz at DIST_MAX
const int FREQ_HIGH = 2000; // Hz at DIST_MIN

void setup() {
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
}

void loop() {
  int dist = readDistanceCm();

  if (dist < DIST_MIN || dist > DIST_MAX) {
    noTone(BUZZ_PIN);
  } else {
    int freq = map(dist, DIST_MIN, DIST_MAX, FREQ_HIGH, FREQ_LOW);
    tone(BUZZ_PIN, freq);
  }

  delay(20);
}

int readDistanceCm() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  long duration = pulseIn(ECHO_PIN, HIGH, 30000);
  if (duration == 0) return 999; // no echo = out of range
  return duration / 58;
}

A few small choices worth pointing out:

  • map() is reversed on purpose. I wanted close hand = high pitch, which feels right for a theremin. Mapping DIST_MIN → FREQ_HIGH and DIST_MAX → FREQ_LOW gives that.
  • 5–50 cm window. Closer than 5 cm and the sensor reads garbage; further than 50 cm and the noise floor takes over. Outside that window, silence.
  • 30 ms pulse-in timeout. A reflection from 5 m round-trip takes ~30 ms. If we get nothing back in that time, treat it as out of range instead of blocking.
  • 20 ms loop delay. That’s 50 readings per second — fast enough to feel continuous, slow enough that the buzzer doesn’t get retriggered mid-cycle.

See it in action

What Leo got out of it

He immediately figured out that you can play a tune by waving your hand in steps, that holding still gives you a steady pitch, and that the cat does not enjoy a 2 kHz tone. He also asked why the buzzer sounded different when he held his hand at an angle — which kicked off a quick detour about how the ultrasonic chirp reflects off the closest surface, so a tilted hand sends most of the echo somewhere other than back at the sensor.

Fifteen minutes, two components, one rainy afternoon. Worth more than it cost.


First Arduino project? I wrote a quickstart guide for getting arduino-cli installed and a first sketch flashed — friendly to total beginners, links out to the official docs for everything else.

Raw markdown for LLMs: /posts/nano-theremin.md  ·  full index