Tone.PolySynth: Managing Polyphony in Tone.js

In web audio development, creating rich harmonic structures requires the ability to play multiple notes simultaneously. This article explains the purpose of Tone.PolySynth in Tone.js and details how it manages polyphony through dynamic voice allocation and recycling, allowing developers to easily trigger chords and complex synthesizer arrangements.

The Purpose of Tone.PolySynth

Most basic synthesizer instruments in Tone.js, such as Tone.Synth, Tone.FMSynth, or Tone.AMSynth, are inherently monophonic. This means they can only output a single pitch at any given time. If you attempt to trigger a new note while a previous one is still sounding, the engine will cut off or glide the original note to play the new one.

Tone.PolySynth solves this limitation. Rather than being a unique synthesizer engine itself, it acts as a polyphonic controller or wrapper. It takes a monophonic voice class as an argument (defaulting to Tone.Synth if none is specified) and handles the complex logic required to play chords, overlapping melodies, and lush ambient pads.

How PolySynth Manages Polyphony

Managing polyphony in web browsers is highly resource-intensive. If an application constantly creates new synthesizer objects for every note played, memory usage will spike, leading to garbage collection pauses and audio glitches.

To prevent this, Tone.PolySynth utilizes an efficient voice-allocation and recycling system:

  1. Internal Voice Pool: Instead of creating synths on the fly, Tone.PolySynth maintains an internal pool of monophonic synthesizer instances.
  2. Dynamic Allocation: When you trigger a chord—such as calling triggerAttack(["C4", "E4", "G4"])—the PolySynth checks its pool for idle voices. If it finds idle synthesizers, it assigns one to each note in the chord.
  3. Voice Creation and Limits: If there are no idle voices in the pool, Tone.PolySynth will instantiate new ones, but only up to a set limit. This limit is defined by the maxPolyphony option (which defaults to 32).
  4. Voice Stealing: If the maximum polyphony threshold is reached and a new note is triggered, the controller performs “voice stealing.” It will automatically release the oldest active note to free up a synthesizer instance for the incoming pitch.
  5. Recycling: Once a note’s envelope completes its release phase and falls to silence, the controller marks that specific synthesizer voice as idle. The idle voice remains in memory, waiting to be reused for the next note trigger rather than being destroyed.

By automating this entire lifecycle behind the scenes, Tone.PolySynth allows developers to focus on musical composition and interactive audio logic without having to manually track, trigger, and dispose of individual synthesizer instances.