Skip to content

Polyphonic Synth

Polyphonic synthesis means multiple notes can play simultaneously, each on its own independent voice. When you press more keys than the configured voice limit, the synth must decide which existing voice to silence and reuse -- this is called voice stealing.

Play chords on the piano keyboard and explore how voice allocation works. Adjust the maximum number of voices, try different steal strategies, and shape the sound with ADSR envelope presets.

Polyphony with visible voice allocation — watch voices get stolen.

Voices:0 / 4
4
ADSR Presets:
0.01s
0.30s
0.40
0.50s
A
W
S
E
D
F
T
G
Y
H
U
J
K
AK trigger notes · black keys W E T Y U

How It Works

The PolySynth manages a pool of oscillator voices. When you play more notes than the maximum voice count, it uses the selected steal strategy to decide which voice to replace:

  • Oldest (LRU) -- replaces the least recently used voice
  • Oldest Active -- replaces the voice that has been playing the longest
  • Quietest -- replaces the voice with the lowest current gain

Try setting max voices to 1 for monophonic mode -- every new note steals the previous one, making the concept immediately obvious.

Code Example

typescript
import { createPolySynth, frequencyMap } from 'ez-web-audio'

// Create a 4-voice polyphonic synth
const synth = await createPolySynth({
  maxVoices: 4,
  stealStrategy: 'lru',
  type: 'triangle',
  envelope: { attack: 0.01, decay: 0.3, sustain: 0.4, release: 0.5 },
})

// Play a chord — each call returns a VoiceHandle
const c4 = synth.play({ frequency: frequencyMap.C4 })
const e4 = synth.play({ frequency: frequencyMap.E4 })
const g4 = synth.play({ frequency: frequencyMap.G4 })

// Listen for voice stealing
synth.on('voicestolen', (event) => {
  const { stolenFrequency, newFrequency, time } = event.detail
  console.log(`Voice at ${stolenFrequency} Hz stolen by ${newFrequency} Hz at t=${time}`)
})

// Stop individual notes
c4.stop()
e4.stop()
g4.stop()

voicestolen Event Payload

PropertyTypeDescription
stolenFrequencynumberFrequency (Hz) of the voice that was stolen
newFrequencynumberFrequency (Hz) of the incoming note that triggered the steal
timenumberAudioContext time when the steal occurred
sourcePolySynthThe PolySynth instance that emitted the event

Cleanup

typescript
synth.stopAll() // Immediately stop all active voices
synth.dispose() // Full cleanup -- releases all voices and disconnects effects

Further Reading