BACK TO BLOG
2026-06-08Meghmalhar Bhowmick

Turning My Webcam into a Harmonium Bellows

PythonAudioOpenCVSignal ProcessingCreative CodingMusic

A harmonium is an acoustic reed organ powered by a hand-pumped leather bellows. If you stop pumping, the air reservoir empties and the reeds choke into silence. If you pump hard, air pressure swells the sound with saturated, ringing harmonics. It's a physically expressive instrument — the feel of playing is inseparable from how it sounds.

I wanted to replicate that in Python. The problem: I don't own a MIDI breath controller, I don't have pressure sensors, and buying specialized hardware for a personal project felt like cheating. Then I noticed that every laptop ships with a sensor most developers completely ignore.

The optical bellows idea

If you cover your webcam with your hand, the average frame luminance drops sharply. If you lift your hand away, it jumps back up. If you wave your hand back and forth over the lens in a pumping motion, you create rapid alternating spikes in mean frame brightness. That luminance delta is motion-correlated — it responds to exactly the same physical action as pumping a bellows.

In sensor_loop(), OpenCV captures frames at ~100 Hz. Each frame is converted to grayscale, and mean luminance is computed with np.mean(gray). The frame-to-frame absolute difference gets compared against a threshold:

diff = abs(curr - last_brightness)
if diff > 0.3:
    reservoir += (diff / 255.0) * sens_mult * 8.0
last_brightness = curr

# Continuous air leak
reservoir = max(0.0, min(1.0, reservoir - 0.008))

Every frame bleeds 0.008 from the reservoir, simulating the continuous air leak through open harmonium valves. Motion above the camera pumps it back up. Stop moving, and the reservoir empties in about four seconds — exactly like releasing the bellows.

Synthesizing the actual sound

I didn't want to play recorded WAV samples of a harmonium. That would just be a sample player. I wanted the synthesis to actually respond to reservoir pressure — more air should mean more harmonic richness, exactly like the real instrument.

The reed_wave() function synthesizes each note dynamically based on current reservoir pressure p:

def reed_wave(freq, phase, n, p):
    p_smooth = math.sin(p * math.pi / 2)
    t = (np.arange(n) + phase) / SAMPLE_RATE
    p_curve = p_smooth ** 1.1
    wave_data = np.zeros(n)
    n_h = max(2, int(p_smooth * len(HARMONICS)))
    for i, (h, amp) in enumerate(HARMONICS[:n_h]):
        h_mult = max(0, p_smooth - (i * 0.04))
        wave_data += amp * h_mult * np.sin(2 * math.pi * freq * h * t * scale_offset)
    return np.tanh(wave_data * 1.4) * 0.4 * p_curve

The harmonic profile is [(1, 1.0), (2, 0.45), (3, 0.75), (4, 0.20), (5, 0.55), (6, 0.10), (7, 0.35)]. At low pressure, only the fundamental and first partial sound. As the reservoir fills, more harmonics bloom in. np.tanh applies non-linear saturation — the same kind of clipping a physical brass reed produces when air pressure peaks. When pressure drops below 0.001, active notes decay exponentially:

decay = np.exp(-np.linspace(0, 18, frames))

Just intonation instead of equal temperament

Indian classical music doesn't work in standard Western equal temperament. Ragas have their own intonation — microtonal shades that disappear in the 12-tone grid. Instead of using standard chromatic frequencies, the keyboard layout maps three rows (z–m, a–k, q–i) to 22 swara positions tuned to pure rational frequency ratios relative to a base root of 261.63 Hz (Middle C). Playing in Sa-Re-Ga feels correct in a way equal-tempered notes don't.

What it feels like

You press a key on the keyboard. Nothing happens if the reservoir is empty — you need to pump first. Wave your hand over the webcam in a pumping motion, watch the air meter climb on the web UI (built with Eel so Python can talk to a browser frontend in real-time), and then press a key. The reed synthesizes and blooms. Hold your hand still — the sound decays over four seconds. Wave again and it surges back with richer upper harmonics.

It is, objectively, a silly way to play music. It is also, subjectively, way more fun than dragging a MIDI slider.

Takeaway

Most laptops ship with more sensors than developers ever touch. A webcam is usually treated as an image classifier input. But pointing it at your hand and reading luminance flux turns it into a one-dimensional motion detector that responds at 100 Hz with zero additional hardware. The question isn't "what sensors do I have?" — it's "what physical information can I extract from sensors I already have?"

[ GALLERY ]