XY Pad
Control frequency and gain in real-time with a visual XY pad. This demonstrates smooth parameter modulation without audible clicks — a key feature for expressive musical interfaces.
Try It: XY Pad
Click or drag on the pad to play. The X-axis controls frequency (100-2000 Hz, logarithmic scale), and the Y-axis controls gain (0-100%).
How to Use
- Click and hold anywhere on the pad to start the oscillator
- Drag to change frequency (horizontal) and gain (vertical)
- Release to stop the sound
The frequency scale is logarithmic, meaning equal horizontal distances represent equal musical intervals. This makes the pad feel "musical" — sliding left-to-right sounds like moving up the musical scale.
How It Works
The XY pad uses the update() API to change oscillator parameters smoothly while playing. This is crucial for click-free parameter modulation.
Real-Time Parameter Updates
import { createOscillator } from 'ez-web-audio'
// Create oscillator
const synth = await createOscillator({
frequency: 440,
type: 'sine'
})
synth.play()
// Update parameters while playing (no clicks!)
synth.update('frequency').to(880).as('ratio') // Change to 880 Hz
synth.update('gain').to(0.5).as('ratio') // Change to 50% volumeWhy update() Instead of Recreating?
You might be tempted to stop and recreate the oscillator when parameters change:
// BAD: Creates audible clicks
function changeFrequency(newFreq: number) {
oscillator.stop()
oscillator = await createOscillator({ frequency: newFreq })
oscillator.play()
}This creates an audible click every time you change the frequency. The update() method changes parameters smoothly using Web Audio's built-in scheduling:
// GOOD: Smooth parameter changes
function changeFrequency(newFreq: number) {
oscillator.update('frequency').to(newFreq).as('ratio')
}Logarithmic Frequency Mapping
The XY pad uses logarithmic frequency scaling so equal horizontal distances sound like equal pitch intervals:
// Linear scaling (sounds wrong - lower frequencies cramped on left)
const linearFreq = minFreq + (maxFreq - minFreq) * ratio
// Logarithmic scaling (sounds musical - equal steps = equal intervals)
const logFreq = minFreq * (maxFreq / minFreq) ** ratio
// For 100-2000 Hz range:
const frequency = 100 * 20 ** ratio // where ratio is 0-1This is why piano keys are evenly spaced even though the frequencies double each octave (100 Hz → 200 Hz → 400 Hz → 800 Hz).
Building Your Own XY Controller
Here's a simplified version of the XY pad logic:
import { createOscillator } from 'ez-web-audio'
let oscillator: any = null
let isPlaying = false
async function handleMouseDown(e: MouseEvent) {
const rect = canvas.getBoundingClientRect()
const x = e.clientX - rect.left
const y = e.clientY - rect.top
// Calculate frequency (logarithmic)
const xRatio = x / canvas.width
const frequency = 100 * 20 ** xRatio // 100-2000 Hz
// Calculate gain (inverted - top is high, bottom is low)
const gain = 1 - (y / canvas.height)
// Create and play oscillator
oscillator = await createOscillator({
frequency,
type: 'sine'
})
oscillator.changeGainTo(gain)
oscillator.play()
isPlaying = true
}
function handleMouseMove(e: MouseEvent) {
if (!isPlaying)
return
const rect = canvas.getBoundingClientRect()
const x = e.clientX - rect.left
const y = e.clientY - rect.top
// Update parameters in real-time
const frequency = 100 * 20 ** (x / canvas.width)
const gain = 1 - (y / canvas.height)
oscillator.update('frequency').to(frequency).as('ratio')
oscillator.update('gain').to(gain).as('ratio')
}
function handleMouseUp() {
if (oscillator) {
oscillator.stop()
oscillator = null
}
isPlaying = false
}API Used
createOscillator()- Creates a synthesizerupdate()- Real-time parameter modulationchangeGainTo()- Set volume