IoTSimulator

Build a 4-Key Piano Synthesizer with a Raspberry Pi Pico W

Build an interactive musical synthesizer with the Raspberry Pi Pico W. Play distinct notes (Do, Re, Mi, Fa) using 4 tactile pushbuttons, driving a piezo audio buzzer with synchronized color-coded LED visual indicators.
Muhammad Ichsanul Fadhil
IoTSim Editor
October 11, 2026
Build a 4-Key Piano Synthesizer with a Raspberry Pi Pico W

Live project track

Interactive hardware & logic preview

From electronic stage keyboards and retro 8-bit video game synthesizers to handheld toys, electronic musical instruments work by vibrating air at precise speeds. When an electrical pulse oscillates a tiny speaker cone or ceramic disc hundreds of times every second, your ears perceive that vibration not as random noise, but as a clear musical pitch. Building your own pocket synthesizer is an exciting way to explore how code creates melody.

In this project, you will build a 4-key musical piano synthesizer using a Raspberry Pi Pico W, four tactile pushbuttons, four color-coded LEDs, and a passive piezo buzzer. Pressing any key plays the corresponding musical note (Do, Re, Mi, or Fa) through the buzzer while illuminating that key's matching LED, and releasing the button silences the sound immediately.

Musical Notes and Sound Frequencies

Musical Notes, Sound Frequencies, and LED Visuals
Figure 1: Each piano key corresponds to a specific pitch frequency in Hertz and illuminates a matching color LED.

In music acoustics, every musical note has a fundamental pitch measured in Hertz (Hz), which indicates how many times the sound wave completes a full vibration each second. Faster vibrations produce higher-pitched notes, while slower vibrations sound lower in pitch.

Piano KeyMusical SolfegeStandard NoteAcoustic FrequencyIndicator LED Color
Key 1 (GP10)DoC4 (Middle C)262 HzRed LED (GP16)
Key 2 (GP11)ReD4294 HzYellow LED (GP17)
Key 3 (GP12)MiE4330 HzGreen LED (GP18)
Key 4 (GP13)FaF4349 HzBlue LED (GP19)

Our synthesizer stores these four frequencies in a simple array in code. When you press Key 1, the microcontroller tells the buzzer to vibrate 262 times per second, producing the familiar sound of Middle C. Pressing Key 4 increases the frequency to 349 vibrations per second, playing Note F.

Wiring Buttons with Internal Pull-Up Resistors

Understanding Active-Low Pushbuttons with INPUT_PULLUP
Figure 2: The Pico W's internal pull-up resistor keeps the pin HIGH until pressing the button shorts it to Ground (LOW).

When connecting pushbuttons to a microcontroller, a common hurdle is handling a disconnected or floating pin when the button is released. Without a steady voltage reference, the input wire acts like a tiny antenna that catches random electrical noise, flickering unpredictably between HIGH and LOW.

Key StateCircuit PathVoltage at Pico GPIO PinDigital Logic ReadingSynthesizer Response
Button Released (Idle)Switch open; internal 50kΩ resistor pulls to 3.3V3.3VHIGH (1)Silent (LED turns OFF, noTone)
Button Pressed (Active)Switch closed; shorts directly to Ground rail0.0VLOW (0)Playing (LED turns ON, tone active)

By declaring pinMode(btnPin, INPUT_PULLUP) in software, the Raspberry Pi Pico W enables an internal 50,000 ohm resistor connected to 3.3V. This guarantees the pin stays at a solid 3.3V (HIGH) until you push the button and connect it to ground. This active-low design saves space on your breadboard by removing the need for external resistors.

Everyday Magic: How 4 Keys Make Music

4 colorful musical piano keys with glowing LED feedback and notes
Figure 3: Fun musical keys: pressing any button plays its unique musical note and lights up its matching color LED.

Think of this project like a pocket electronic xylophone or mini synth. Each of the four buttons is assigned to a musical note—Do, Re, Mi, and Fa. When your finger presses a button, the Raspberry Pi Pico W immediately vibrates the piezo speaker at that exact pitch and lights up the corresponding color LED. The interactive connection tables below show how each module connects to the Raspberry Pi Pico W.

Pin Connection Map
Pushbutton Key Terminal
Key 1 (C4 / Do) Terminal 1
→
Raspberry Pi Pico W Pin
GP10 (Physical Pin 14)
Explanation
Key 1 active-low digital input
Pushbutton Key Terminal
Key 2 (D4 / Re) Terminal 1
→
Raspberry Pi Pico W Pin
GP11 (Physical Pin 15)
Explanation
Key 2 active-low digital input
Pushbutton Key Terminal
Key 3 (E4 / Mi) Terminal 1
→
Raspberry Pi Pico W Pin
GP12 (Physical Pin 16)
Explanation
Key 3 active-low digital input
Pushbutton Key Terminal
Key 4 (F4 / Fa) Terminal 1
→
Raspberry Pi Pico W Pin
GP13 (Physical Pin 17)
Explanation
Key 4 active-low digital input
Pushbutton Key Terminal
All Keys Terminal 2
→
Raspberry Pi Pico W Pin
GND (Physical Pin 13)
Explanation
Shared ground connection for pushbuttons

Each tactile pushbutton has two sides. Connecting one terminal to its assigned GPIO pin and the other terminal to the ground rail ensures that closing the switch pulls the input to ground.

Pin Connection Map
Indicator LED Anode (+)
Red LED Anode
→
Raspberry Pi Pico W Pin
GP16 (Physical Pin 21)
Explanation
Illuminates when Key 1 (C4) is pressed
Indicator LED Anode (+)
Yellow LED Anode
→
Raspberry Pi Pico W Pin
GP17 (Physical Pin 22)
Explanation
Illuminates when Key 2 (D4) is pressed
Indicator LED Anode (+)
Green LED Anode
→
Raspberry Pi Pico W Pin
GP18 (Physical Pin 24)
Explanation
Illuminates when Key 3 (E4) is pressed
Indicator LED Anode (+)
Blue LED Anode
→
Raspberry Pi Pico W Pin
GP19 (Physical Pin 25)
Explanation
Illuminates when Key 4 (F4) is pressed
Indicator LED Anode (+)
All LED Cathodes (-)
→
Raspberry Pi Pico W Pin
GND (Physical Pin 23)
Explanation
Shared ground return for all four indicator LEDs

Always remember that LEDs are polarized: the longer lead is the anode (positive) and connects to the signal pin, while the shorter lead with a flat edge is the cathode (negative) and connects to the ground rail.

Pin Connection Map
Piezo Buzzer Terminal
Pin 1 (+ Positive Lead)
→
Raspberry Pi Pico W Pin
GP20 (Physical Pin 26)
Explanation
Tone frequency signal output
Piezo Buzzer Terminal
Pin 2 (- Negative Lead)
→
Raspberry Pi Pico W Pin
GND (Physical Pin 28)
Explanation
Audio circuit ground return

A passive piezo buzzer contains a flexible ceramic disc that bends in response to electric signals. When the Pico W toggles pin GP20 at high speeds, this disc flexes back and forth, generating clear audible sound waves.

Complete Code

Upload the following complete sketch to your Raspberry Pi Pico W. As soon as upload completes, you can begin playing musical melodies on the pushbuttons:

C++ Source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// ============================================================================
// Raspberry Pi Pico W 4-Key Tone Piano Synthesizer
// Pushbuttons on GP10, GP11, GP12, GP13 (INPUT_PULLUP)
// Visual LEDs on GP16, GP17, GP18, GP19
// Synthesizer Audio Buzzer on GP20
// ============================================================================

const int btnPins[4] = {10, 11, 12, 13};
const int ledPins[4] = {16, 17, 18, 19};
const int BUZZER_PIN = 20;

// Diatonic Scale Fundamental Frequencies (Hz):
// C4 (Middle Do) = 262 Hz | D4 (Re) = 294 Hz | E4 (Mi) = 330 Hz | F4 (Fa) = 349 Hz
const int noteFrequencies[4] = {262, 294, 330, 349};

void setup() {
  Serial.begin(115200);
  delay(200); // USB settling delay
  
  for (int i = 0; i < 4; i++) {
    pinMode(btnPins[i], INPUT_PULLUP);
    pinMode(ledPins[i], OUTPUT);
    digitalWrite(ledPins[i], LOW);
  }
  
  pinMode(BUZZER_PIN, OUTPUT);
  
  Serial.println(F("Raspberry Pi Pico W 4-Key Tone Synthesizer Initialized!"));
}

void loop() {
  bool isAnyKeyPressed = false;

  for (int i = 0; i < 4; i++) {
    // Check active-low button press (Connecting to GND pulls pin LOW)
    if (digitalRead(btnPins[i]) == LOW) {
      digitalWrite(ledPins[i], HIGH);              // Turn on matching color LED
      tone(BUZZER_PIN, noteFrequencies[i]);        // Play note frequency
      isAnyKeyPressed = true;
    } else {
      digitalWrite(ledPins[i], LOW);               // Turn off LED when key released
    }
  }

  // If no buttons are held down, silence the audio buzzer immediately
  if (!isAnyKeyPressed) {
    noTone(BUZZER_PIN);
  }

  delay(20); // 20ms debounce and scan polling window
}

How the Code Works, Part by Part

The sketch scans all four keys in a loop 50 times per second, managing audio output and visual LED feedback through three simple steps.

Scanning the Piano Keys in a Loop

Rather than writing four separate if statements for each button, a concise for loop iterates through the pin arrays:

C++ Source
1
2
3
4
5
6
7
8
9
for (int i = 0; i < 4; i++) {
  if (digitalRead(btnPins[i]) == LOW) {
    digitalWrite(ledPins[i], HIGH);
    tone(BUZZER_PIN, noteFrequencies[i]);
    isAnyKeyPressed = true;
  } else {
    digitalWrite(ledPins[i], LOW);
  }
}

If button index i is pressed, digitalRead() returns LOW. The program immediately lights that key's matching LED and sets isAnyKeyPressed to true so the system knows a note is currently playing.

Playing Musical Frequencies with Tone

The tone() function generates the acoustic melody by vibrating pin GP20:

C++ Source
1
tone(BUZZER_PIN, noteFrequencies[i]);

The Pico W's hardware timers handle the high-speed switching automatically. When Key 1 is held down, pin GP20 toggles 262 times per second, producing a rich Middle C note without slowing down the rest of your program.

Silencing the Instrument on Release

After checking all four keys, the code evaluates whether any button remains pressed:

C++ Source
1
2
3
if (!isAnyKeyPressed) {
  noTone(BUZZER_PIN);
}

If no keys are currently held down, noTone() turns off the buzzer immediately. This ensures clean, responsive notes that stop the exact millisecond your finger lifts off the key.

Fixing Common Problems

If your synthesizer is not making sounds or behaving as expected, check the troubleshooting table below:

Observed ProblemPossible CauseHow to Fix
Buzzer makes a continuous whining sound without pressing buttonsButtons not configured with pull-upsEnsure pinMode(btnPins[i], INPUT_PULLUP) is present in setup().
LED turns on when key is pressed, but buzzer stays silentActive buzzer used instead of passive modelActive buzzers cannot play different musical pitches. Replace with a passive piezo sounder.
Notes sound in reverse or scrambled pitch orderButton or LED pin wiring order swappedVerify Key 1 connects to GP10, Key 2 to GP11, Key 3 to GP12, and Key 4 to GP13 in sequence.
Sound clicks or stutters rapidly when holding a buttonLoop delay is too short or wire looseKeep the 20 ms delay at the bottom of loop() to filter out mechanical contact chatter.

Because passive buzzers are polarized, ensure the side marked with a small '+' sign connects to GP20 and the opposite pin connects to Ground.

Try It in the Simulator

Click the Start Simulation button in the top toolbar to turn on your piano. Click and hold down Key 1 with your mouse to hear Middle C and watch the red LED illuminate. Try pressing the other buttons in sequence—Key 1 (Do), Key 2 (Re), Key 3 (Mi), and Key 4 (Fa)—to play simple musical melodies directly in your browser.

Keywords
#Raspberry Pi Pico W #RP2040 #Pushbutton #Buzzer #LED #Synthesizer #Audio #Music #Beginner
Total word count: 1127 words

Project discussion

Questions, feedback, and community insights
No discussions yet. Be the first to start!