If you’ve ever followed a buzzer tutorial and ended up with a component that just makes one loud, fixed beep — you probably had an active buzzer. Useful for a simple alarm, not much else. The passive buzzer looks almost identical but behaves completely differently: it makes whatever sound you tell it to. Different frequencies, different durations, different rhythms. That means melodies, musical scales, audio feedback tones, and alert patterns — all from a component that costs almost nothing and connects with two wires.
This guide covers everything you need to go from a bare buzzer on a breadboard to playing actual music and building practical alert systems. The code is copy-paste ready, and every concept is explained before it’s used.
Active vs Passive — The Fundamental Difference
Both buzzers are piezoelectric — they use a crystal that physically deforms when voltage is applied, moving a diaphragm to create sound. The difference is what circuitry sits between the pins and that crystal.
An active buzzer has a built-in oscillator circuit. Apply power and it generates its own fixed frequency — typically somewhere between 2 kHz and 4 kHz. You control it with a simple on/off signal: HIGH to beep, LOW to stop. Simple, but limited. The pitch is fixed by the hardware. You can make it beep faster or slower by toggling it in code, but you cannot change the tone.
A passive buzzer has no oscillator. It’s just a piezoelectric element with two connections. It makes sound when you drive it with an alternating signal — a square wave. Change the frequency of that square wave and you change the pitch. The Arduino generates this signal in software, which means you have complete control over the frequency, and therefore the note. This is why passive buzzers are fundamentally more capable: the intelligence lives in your code, not the component.
The easiest way to tell them apart without a datasheet: apply 5 V directly across the pins. An active buzzer beeps immediately. A passive buzzer makes no sound (or a faint click) — it needs an oscillating signal, not a steady one.
How a Passive Buzzer Generates Sound
Sound is pressure waves in air — rapid compressions and rarefactions that your ear interprets as pitch. The frequency of those waves determines what note you hear: 440 Hz is concert A, 261 Hz is middle C, 523 Hz is the C an octave above that.
The Arduino generates a square wave on a digital output pin: the pin switches between HIGH (5 V) and LOW (0 V) at a set frequency. When this signal reaches the passive buzzer’s piezoelectric element, the crystal flexes toward HIGH and away during LOW, physically moving the diaphragm back and forth at that frequency. The diaphragm pushes air, creating the pressure waves your ear detects as a tone.
The quality and volume of the sound depends on the buzzer’s resonant frequency — the frequency at which its physical structure naturally vibrates most efficiently. Most passive buzzers have a resonant peak somewhere between 2 kHz and 4 kHz. Notes near that peak will sound louder and cleaner; notes far below or above it will be quieter and sometimes buzzy. This is a hardware characteristic you can’t change in code, but you can work around it by choosing repertoire and alert tones that stay in the buzzer’s comfortable range.
Arduino generates this square wave through its PWM (Pulse Width Modulation) hardware — specifically, the timer peripherals. The tone() function handles all of this automatically, so you never need to think about timers directly.
Wiring to Arduino
The passive buzzer has two pins. Most modules and bare components are marked with a positive (+) pin and a negative (−) or ground pin. Some are unmarked — in that case, either polarity usually works for passive buzzers, though check your specific datasheet if available.
You’ll need an Arduino Uno, a breadboard, and a pair of jumper wires. The connection is minimal:
- Buzzer positive (+) pin → Arduino digital pin 8 (or any digital pin)
- Buzzer negative (−) pin → Arduino GND
No resistor is required for basic operation — the Arduino’s output current is low enough that it won’t damage the buzzer or the pin. If you want to reduce volume, you can add a resistor (100 Ω to 1 kΩ) in series on the positive wire. Higher resistance = quieter sound. This is the easiest way to tame an uncomfortably loud buzzer in a quiet environment.
You can use any digital pin that supports tone(). On the Uno, all digital pins work. Note that tone() uses Timer 2 on the Uno, which means it will interfere with PWM output on pins 3 and 11 while a tone is playing. If your project also uses analogWrite() on those pins, plan your pin assignments accordingly.
The tone() Function Explained
Arduino’s built-in tone() function does all the heavy lifting. You don’t need to manage timers, calculate duty cycles, or toggle pins manually. The function has two forms:
tone(pin, frequency);
tone(pin, frequency, duration);- pin: the digital pin connected to the buzzer
- frequency: the pitch in Hz — an integer between 31 and 65535
- duration: how long to play in milliseconds (optional). If omitted, the tone plays until you call
noTone(pin)
When you include a duration, the function starts the tone and immediately returns — it does not pause your code for the duration. The tone continues in the background while your code moves on. This matters: if you want a 500 ms tone followed by silence, you need to call delay(500) yourself after tone(), then call noTone().
tone(8, 440); // Start playing 440 Hz (note A4) — runs indefinitely
delay(1000); // Wait 1 second while tone plays
noTone(8); // Stop the tone
delay(500); // 500ms of silence before the next noteThe gap between notes matters. Without a brief silence between consecutive notes at the same pitch, they blur together and sound like one continuous tone. A small delay() after noTone() — even 20–50 ms — creates the articulation that makes individual notes audible.
Playing a Scale
Before building melodies, it helps to understand how musical notes map to frequencies. The standard equal-tempered scale assigns a specific Hz value to every note. The most useful range for a buzzer spans a couple of octaves around middle C:
// Note frequencies in Hz — octave 4 (middle octave)
#define NOTE_C4 262
#define NOTE_D4 294
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_G4 392
#define NOTE_A4 440
#define NOTE_B4 494
#define NOTE_C5 523The full set of standard note definitions is available in the Arduino pitches.h header, which you can either download or copy from the official Arduino examples. Here’s a complete sketch that plays a C major scale up and back down:
#define BUZZER_PIN 8
// Note frequencies (Hz)
#define NOTE_C4 262
#define NOTE_D4 294
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_G4 392
#define NOTE_A4 440
#define NOTE_B4 494
#define NOTE_C5 523
int scale[] = {
NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4,
NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5
};
int noteCount = 8;
int noteDuration = 300; // milliseconds per note
int noteGap = 50; // silence between notes
void setup() {
pinMode(BUZZER_PIN, OUTPUT);
}
void loop() {
// Play scale ascending
for (int i = 0; i < noteCount; i++) {
tone(BUZZER_PIN, scale[i], noteDuration);
delay(noteDuration + noteGap);
}
delay(500); // Pause at the top
// Play scale descending
for (int i = noteCount - 1; i >= 0; i--) {
tone(BUZZER_PIN, scale[i], noteDuration);
delay(noteDuration + noteGap);
}
delay(2000); // Wait before repeating
}Upload this, and you’ll hear a clean ascending and descending scale. Adjust noteDuration to change tempo. Notice that the delay() inside the loop is set to noteDuration + noteGap — the tone plays for noteDuration milliseconds, then there’s a noteGap silence before the next note begins. This articulation is what makes the scale sound like separate notes rather than a continuous glide.
Playing a Simple Melody
A melody is just a sequence of notes with specific durations. The convention borrowed from Arduino’s official examples uses a simple notation: positive integers represent note durations (4 = quarter note, 8 = eighth note) and the tempo is derived from a base beat length in milliseconds.
Here’s a complete working example playing a recognisable tune — the opening of “Ode to Joy” by Beethoven:
#define BUZZER_PIN 8
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_G4 392
#define NOTE_A4 440
#define NOTE_B4 494
#define NOTE_C5 523
#define NOTE_D5 587
// Notes: 0 means rest
int melody[] = {
NOTE_E4, NOTE_E4, NOTE_F4, NOTE_G4,
NOTE_G4, NOTE_F4, NOTE_E4, NOTE_D5,
NOTE_C5, NOTE_C5, NOTE_D5, NOTE_E4,
NOTE_E4, NOTE_D5, NOTE_D5
};
// Durations: 4 = quarter note, 8 = eighth note
int durations[] = {
4, 4, 4, 4,
4, 4, 4, 4,
4, 4, 4, 4,
4, 2, 2 // last two are half notes
};
int noteCount = 15;
int tempo = 400; // quarter note duration in ms — lower = faster
void setup() {
pinMode(BUZZER_PIN, OUTPUT);
playMelody();
}
void loop() {
// Play once on startup, then stop
}
void playMelody() {
for (int i = 0; i < noteCount; i++) {
int duration = tempo / durations[i] * 4; // calculate ms from note value
if (melody[i] == 0) {
delay(duration); // rest
} else {
tone(BUZZER_PIN, melody[i], duration);
delay(duration * 1.1); // slight gap between notes for articulation
}
}
noTone(BUZZER_PIN);
}The duration calculation — tempo / durations[i] * 4 — converts the note value notation into milliseconds. A quarter note (4) with tempo 400 becomes 400 ms. A half note (2) becomes 800 ms. An eighth note (8) becomes 200 ms. Adjust tempo to change how fast the melody plays.
To add a rest (silence) between notes in a melody, set the corresponding entry in melody[] to 0 and handle it as a delay() with no tone() call, as shown in the playMelody() function above.
Using the Buzzer for Alerts and Feedback
Melodies are fun, but the most practical use of a passive buzzer is audio feedback in real projects. Because you control frequency, you can create distinct sounds for different events — something an active buzzer simply cannot do.
Confirmation Beep
A short rising two-tone beep feels positive and affirmative — useful for confirming a button press, a successful read, or a completed action:
void confirmBeep() {
tone(BUZZER_PIN, 880, 80); // high note
delay(100);
tone(BUZZER_PIN, 1175, 120); // higher note
delay(150);
noTone(BUZZER_PIN);
}Error / Warning Tone
A descending or harsh tone signals a problem. Low frequencies feel more urgent and attention-grabbing than high ones:
void errorTone() {
for (int i = 0; i < 3; i++) {
tone(BUZZER_PIN, 200, 150);
delay(200);
noTone(BUZZER_PIN);
delay(100);
}
}Threshold Alert (e.g. temperature or distance warning)
Combine the buzzer with a sensor and vary the beep rate based on the reading — faster beeping as you approach a threshold, continuous tone when exceeded:
void thresholdAlert(int level) {
// level: 0 = safe, 1 = warning, 2 = critical
if (level == 0) {
noTone(BUZZER_PIN);
} else if (level == 1) {
tone(BUZZER_PIN, 500, 100);
delay(500);
} else if (level == 2) {
tone(BUZZER_PIN, 1000); // continuous tone — call noTone() separately
}
}Button Feedback
In projects with physical buttons, a brief click or tick sound on every press dramatically improves the feel of the interface — even if the action takes a moment to complete:
void buttonClick() {
tone(BUZZER_PIN, 1200, 30); // very short, high click
delay(35);
noTone(BUZZER_PIN);
}These short, functional tones are where the passive buzzer earns its place in projects. An active buzzer makes the same beep every time regardless of context. A passive buzzer lets your device communicate — success, failure, warning, confirmation — through sound alone.
Limitations and Practical Tips
Volume is fixed (mostly)
The Arduino’s digital output is either HIGH or LOW — there’s no in-between, and tone() always drives the pin to full voltage. You cannot make the buzzer quieter in software by adjusting the signal amplitude. Your options for volume control are hardware-only: add a series resistor (100 Ω–1 kΩ) to reduce current and therefore loudness, or cover the buzzer’s sound port with tape or foam for a quick-and-dirty reduction. Some passive buzzers also have a volume control hole on the back that can be partially covered.
Resonant frequency sweet spot
Your buzzer will have a frequency range where it sounds best — typically printed on the datasheet as the “resonant frequency.” Notes far below this range (below ~200 Hz) will be very quiet and buzzy. Notes far above it lose efficiency too. For most common passive buzzers, the range between 500 Hz and 4000 Hz sounds clear and reasonably loud. The very lowest bass notes in a melody may barely be audible; prioritise mid-to-upper ranges for anything that needs to be heard clearly.
tone() blocks Timer 2
While tone() is active, Timer 2 is in use. This means analogWrite() on pins 3 and 11 (which use Timer 2 on the Uno) will not work correctly. If your project needs PWM output on those pins simultaneously with audio, you have two options: use the TimerFreeFastPWM or toneAC library which handles the conflict, or relocate your PWM loads to pins 5, 6, 9, or 10 (which use different timers).
Only one tone at a time
tone() can only drive one pin at a time on the standard Uno. Calling tone() on a second pin stops the first. If you need simultaneous tones (harmony, chord), you’ll need either multiple Arduinos, a dedicated audio module, or a software library like TimerFreeFastPWM that uses bit-banging at the cost of CPU cycles.
noTone() after you’re done
If you use tone() without a duration argument and then forget to call noTone(), the buzzer will continue playing indefinitely — including while your code does other things. Make it a habit to explicitly call noTone(BUZZER_PIN) when a sound should stop, particularly in event-driven code where you can’t rely on a delay to run out.
More Useful Than It Looks
The passive buzzer is one of the most underrated components in the Arduino ecosystem. It’s cheap, it requires two wires and no driver circuitry, and it adds an entire dimension of feedback to your projects that blinking LEDs can’t match. Sound is immediate and attention-grabbing in a way that visual indicators simply aren’t — and because you control the frequency, one component can communicate a dozen different states.
Start with the scale example to confirm everything’s wired correctly, then build the melody sketch to get comfortable with note arrays and timing. Once you have that intuition, adding audio feedback to any project becomes a five-minute job.
Pick up a passive piezo buzzer and pair it with an Arduino Uno. A breadboard and a few jumper wires are all the hardware you need — the rest is in the code.
