Play Melodies with Arduino and a Piezo Buzzer
Every project we've built so far has been silent. LEDs blink, sensors read, servos move — but the Arduino hasn't made a sound. Time to change that.
A tiny piezo buzzer and a few lines of code are all it takes to give your Arduino a voice. Not a recording — actual notes, generated in real time. A startup jingle when power comes on. A warning beep when a sensor triggers. Or an entire melody, note by note.
The secret is Arduino's built-in tone() function, which generates sound at any frequency you want. Pair that with a simple data structure — two arrays, one for notes and one for durations — and you've got a music engine in about 30 lines of code.
Two wires. No resistor. No breadboard. Probably the simplest wiring in this entire series.
What You'll Need
- Arduino Uno board (or compatible clone)
- USB cable (Type A to Type B)
- 1 × Passive piezo buzzer (the small black disc — make sure it's passive, not active)
- 2 × jumper wires
- A computer with the Arduino IDE installed
Almost every Arduino starter kit includes a buzzer. The critical detail: it must be a passive buzzer. We'll explain the difference below.
Know Your Component
Active vs. Passive Buzzer
There are two types of piezo buzzers, and they look almost identical. Getting the wrong one is the most common mistake in this project.
An active buzzer has a built-in oscillator circuit. Apply voltage, and it produces a single fixed tone. You can't change the pitch — it's always the same beep. Useful for simple alarms, but useless for melodies.
A passive buzzer has no oscillator. It's just a thin disc that vibrates when you push current through it at a specific frequency. You control the frequency, which means you control the pitch. Different frequencies produce different musical notes.
How to tell them apart: Look at the bottom. An active buzzer often has a sealed back with no visible circuit board. A passive buzzer may have an open back where you can see the disc inside. The most reliable test: connect it to 5V and GND. If it makes a sound on its own, it's active. If it stays silent, it's passive (and that's the one you want).
How Sound Works (in 30 seconds)
Sound is vibration. When something vibrates back and forth rapidly, it pushes air molecules, creating pressure waves that reach your ear. The number of vibrations per second is the frequency, measured in Hertz (Hz).
Higher frequency = higher pitch. Lower frequency = lower pitch.
Musical notes are just agreed-upon frequencies. For example, the note A4 (the A above middle C, the standard tuning reference) vibrates at exactly 440 Hz. The note C5 is 523 Hz. B3 is 247 Hz.
Arduino's tone() function generates a square wave at whatever frequency you specify. The buzzer converts that electrical signal into physical vibration — and you hear a note.
Wiring It Up
Two wires. That's it.
Orange wire: Buzzer + (longer leg) → Arduino Pin 8
Black wire: Buzzer − (shorter leg) → Arduino GND
The buzzer has polarity — the longer leg is positive. If your buzzer has a + symbol printed on top, that side goes to the signal pin.
No resistor is needed. The buzzer's internal impedance (typically 16–40Ω) limits the current to safe levels for the Arduino's GPIO pins.
The Code
Step 1: Play a single note
Let's start simple — one note to confirm everything works:
int buzzerPin = 8;
void setup() {
tone(buzzerPin, 440, 1000);
}
void loop() {
}
Upload this. You should hear a one-second tone at 440 Hz (the note A4). If you hear nothing, check that you have a passive buzzer and that the polarity is correct.
Step 2: Play a scale
Now let's play the C major scale — the white keys from C to C:
int buzzerPin = 8;
int scale[] = {262, 294, 330, 349, 392, 440, 494, 523};
// C4 D4 E4 F4 G4 A4 B4 C5
void setup() {
for (int i = 0; i < 8; i++) {
tone(buzzerPin, scale[i], 300);
delay(400);
}
noTone(buzzerPin);
}
void loop() {
}
You should hear a clear ascending scale: Do-Re-Mi-Fa-Sol-La-Ti-Do.
Step 3: Play a melody
Now the real payoff. Let's play "Twinkle Twinkle Little Star":
int buzzerPin = 8;
// Note frequencies (0 = rest/silence)
#define NOTE_C4 262
#define NOTE_D4 294
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_G4 392
#define NOTE_A4 440
// Melody: each note in sequence
int melody[] = {
NOTE_C4, NOTE_C4, NOTE_G4, NOTE_G4,
NOTE_A4, NOTE_A4, NOTE_G4, 0,
NOTE_F4, NOTE_F4, NOTE_E4, NOTE_E4,
NOTE_D4, NOTE_D4, NOTE_C4, 0,
NOTE_G4, NOTE_G4, NOTE_F4, NOTE_F4,
NOTE_E4, NOTE_E4, NOTE_D4, 0,
NOTE_G4, NOTE_G4, NOTE_F4, NOTE_F4,
NOTE_E4, NOTE_E4, NOTE_D4, 0,
NOTE_C4, NOTE_C4, NOTE_G4, NOTE_G4,
NOTE_A4, NOTE_A4, NOTE_G4, 0,
NOTE_F4, NOTE_F4, NOTE_E4, NOTE_E4,
NOTE_D4, NOTE_D4, NOTE_C4, 0
};
// Duration for each note (in ms)
int durations[] = {
400, 400, 400, 400,
400, 400, 800, 200,
400, 400, 400, 400,
400, 400, 800, 200,
400, 400, 400, 400,
400, 400, 800, 200,
400, 400, 400, 400,
400, 400, 800, 200,
400, 400, 400, 400,
400, 400, 800, 200,
400, 400, 400, 400,
400, 400, 800, 200
};
int noteCount = sizeof(melody) / sizeof(melody[0]);
void setup() {
for (int i = 0; i < noteCount; i++) {
if (melody[i] == 0) {
noTone(buzzerPin);
} else {
tone(buzzerPin, melody[i], durations[i]);
}
delay(durations[i] * 1.1);
}
noTone(buzzerPin);
}
void loop() {
}
Understanding the code
tone(pin, frequency, duration) — The core function. It generates a square wave on the specified pin at the given frequency (Hz) for the given duration (ms). The Arduino handles all the timing — you don't need to toggle the pin manually.
noTone(pin) — Stops any tone on that pin. Without this, the last note would continue playing.
#define NOTE_C4 262 — We use #define to give friendly names to frequencies. This is the same approach Arduino's built-in pitches.h file uses (we'll mention that below).
int melody[] and int durations[] — Two parallel arrays: one stores the sequence of notes, the other stores how long each note plays. These arrays are the same length, and index 0 in melody corresponds to index 0 in durations. This two-array pattern is used in almost every Arduino music project.
melody[i] == 0 — A frequency of 0 represents a rest (silence). We call noTone() instead of tone() for rests.
delay(durations[i] * 1.1) — We wait slightly longer than the note duration. The extra 10% creates a tiny gap between notes, making the melody sound more natural instead of a continuous drone.
sizeof(melody) / sizeof(melody[0]) — A common C++ pattern to calculate how many elements are in an array. sizeof(melody) gives the total bytes, divided by the size of one element gives the count.
Using Arduino's Built-in Note Definitions
Instead of defining note frequencies yourself, you can use Arduino's built-in pitches.h file. Add this line at the top of your sketch:
#include "pitches.h"
This file defines all standard musical notes from B0 (31 Hz) to D#8 (4978 Hz). You can then write NOTE_C4, NOTE_FS5 (F-sharp), NOTE_BB3 (B-flat), and so on.
To use it: in the Arduino IDE, go to File → Examples → 02.Digital → toneMelody. This example sketch includes pitches.h — you can copy it into your own projects.
Note Frequency Reference
Here are the most commonly used notes:
| Note | Frequency (Hz) | Note | Frequency (Hz) |
|---|---|---|---|
| C4 | 262 | C5 | 523 |
| D4 | 294 | D5 | 587 |
| E4 | 330 | E5 | 659 |
| F4 | 349 | F5 | 698 |
| G4 | 392 | G5 | 784 |
| A4 | 440 | A5 | 880 |
| B4 | 494 | B5 | 988 |
Each octave doubles the frequency: A4 is 440 Hz, A5 is 880 Hz, A3 is 220 Hz.
Troubleshooting
No sound at all.
You likely have an active buzzer (which needs a simple HIGH/LOW, not tone()). Test by connecting it directly to 5V and GND — if it beeps, it's active. Swap it for a passive one.
Sound is very quiet. The Arduino's GPIO pins output 5V at up to 20 mA, which is enough for a buzzer but not loud. This is normal — piezo buzzers are small and not meant to fill a room. For more volume, use a transistor to drive the buzzer from a higher-current source.
Notes sound wrong or out of tune. Cheap buzzers have a resonant frequency (often around 2–4 kHz) where they're loudest, and they can distort notes far from that range. Very low notes (below C4) may sound weak or buzzy. This is a hardware limitation, not a code issue.
Only one tone plays at a time.
The Arduino Uno has only one hardware timer for tone(), so it can only produce one frequency simultaneously. For polyphonic music, you'd need multiple buzzers on different boards or an audio shield.
Ideas to Try
Doorbell chime: Play a two-note "ding-dong" when a button is pressed.
Alarm clock: Combine with an RTC (Real Time Clock) module to play a melody at a set time.
Game sounds: Add sound effects to projects — a rising tone when a sensor triggers, a descending tone for errors, a victory jingle for success.
Simon Says game: Generate random sequences of tones and colors (with LEDs), and have the player repeat them using buttons.
Song library: Store multiple melodies and use a button to cycle between them. With the parallel-array pattern, adding a new song is just a matter of defining two more arrays.
Tempo control: Add a potentiometer on an analog input to control playback speed in real time — turn the knob to speed up or slow down the melody.
Wrapping Up
Your Arduino isn't silent anymore. That alone changes the feel of every project you build from now on — you can add audio feedback to sensor readings, button presses, alarms, games, anything.
But the bigger takeaway is the parallel array pattern: one array for what to do, another for how long to do it, both indexed together. You'll see this structure everywhere — in animation sequences, sensor calibration tables, state machines. It's one of those simple ideas that unlocks a lot of complexity.
And underneath it all, tone() is doing something the Arduino has been doing all along — toggling a pin at a precise frequency. The same principle drives PWM for servos and LED dimming. Here, you just happen to hear it.
Part of the Arduino Intermediate series. Previous: Temperature Monitor • Wi-Fi Weather Station • Motion-Activated Light • Distance-Controlled Servo