Additive Particle Field — User Guide

Analysis-driven additive particle synthesis: the selected Sound supplies pitch and intensity contours, while a new stereo field is synthesized from short harmonic, inharmonic, or noise particles.

Author: Shai Cohen Affiliation: Department of Music, Bar-Ilan University, Israel Version: 0.5.1 (2026) License: MIT License Repo: GitHub
Contents:

What this does

Additive Particle Field does not cut the source into audio grains and rearrange them. Instead, it analyzes the selected Sound for pitch and intensity, then uses those contours as controllers for a newly synthesized stereo particle field.

At each particle time, a voiced analysis frame produces a short additive micro-spectrum anchored to the detected pitch. An unvoiced frame produces a short broadband noise particle instead of inventing a fallback pitch. Particle amplitude follows the source intensity contour, while panning, time distribution, spectral shape, grain envelope, and optional LFO determine the resulting texture.

Source vs. output: the source provides control information; its waveform is not copied into the particles. The output is synthesized from sinusoidal partials and noise, then mixed to stereo and peak-normalized.

What is a particle?

In this script, a particle is one short synthesized sound event with its own start time, pitch anchor, amplitude, spectrum, envelope, and stereo position.

Voiced particle

A set of sinusoidal partials built above the detected pitch. The partials can follow an integer harmonic series or a stretched, bell-like inharmonic series. Each partial receives a random starting phase.

Unvoiced particle

A windowed broadband noise burst. Its level still follows the source intensity contour, but it has no oscillator pitch. The pitch value shown for it in the visualization is only a display/panning anchor.

Quick start

  1. Select exactly one Sound object in Praat.
  2. Run Additive_Particle_Field.praat.
  3. Choose one of the five presets, or keep Custom.
  4. For Custom, set the particle count and grain duration, then choose the number of partials, spectral brightness, spectrum type, envelope, panning, LFO, and time distribution.
  5. Click OK. The result is created as <source>_particles_<preset>.
Starting point: Custom defaults to 100 particles, 50 ms grains, 10 harmonic partials, spectral brightness 0.78, Hann envelopes, pitch-derived panning, no LFO, and linear timing.

Presets

A named preset replaces the relevant synthesis settings with the values below.

PresetParticlesGrainPartialsSpectrumBrightnessNoise mixEnvelopePanTiming / LFO
Dense Cloud30030 ms9Harmonic0.820.40GaussianRandomRandom / Off
Sparse Field30150 ms10Harmonic0.680.30HannPitch-derivedLinear / Off
Rhythmic Pulse8040 ms8Harmonic0.900.45RectangularFixed centerLinear / 4 Hz
Shimmer15060 ms18Bell-inharmonic, B=0.0180.960.25GaussianRandomExponential / 0.25 Hz
Long Resonance15800 ms14Bell-inharmonic, B=0.0300.800.18HannPitch-derivedExponential / 0.15 Hz

Particle spectrum

Harmonic spectrum

partial ratio h = 1, 2, 3, ... frequency_h = detectedPitch × h

Bell-inharmonic spectrum

ratio_h = h × [1 + Inharmonicity × (h - 1)] frequency_h = detectedPitch × ratio_h

The inharmonic formula progressively stretches upper partials away from the integer harmonic series. Inharmonicity is limited to 0–0.10.

Spectral brightness

Spectral_brightness does not apply a filter. It changes the power-law roll-off of the additive partial amplitudes:

partialDecay = 2.6 - 2.0 × Spectral_brightness weight_h = 1 / h^partialDecay

Brightness 0 gives a steep 1/h^2.6 roll-off; brightness 1 gives a much shallower 1/h^0.6 roll-off. The weights of the partials actually used are summed and the carrier is divided by that sum, so increasing the partial count primarily changes timbre rather than causing uncontrolled level growth.

Nyquist protection and phase

Partials at or above 47.5% of the sample rate — 95% of Nyquist — are omitted. Each retained partial receives an independent random starting phase, reducing phase-locked buildup when many particles overlap.

Unvoiced/noise particles

When the Pitch object is undefined at a particle time, the carrier becomes bounded white noise:

carrier = Unvoiced_noise_mix × randomUniform(-1, 1)

The noise is still multiplied by the particle amplitude and the selected grain envelope. Unvoiced_noise_mix therefore controls the carrier level of unvoiced particles relative to voiced additive particles.

Pitch & intensity control

Pitch

The script creates a Praat Pitch object over a fixed analysis range of 75–600 Hz. At each particle time it queries the Pitch object with linear interpolation. Defined values are clamped to this range; undefined values are classified as unvoiced and rendered as noise.

Intensity

Praat Intensity values are in dB SPL, not dBFS. The script measures the maximum of the Intensity contour and converts each particle's level relative to that maximum into a linear amplitude ratio:

relative_dB = min(0, I(t) - Imax) intensityAmp = 10^(relative_dB / 20) grainAmp = 0.2 × intensityAmp

This means the loudest analyzed region receives a base particle amplitude of 0.2 before LFO and panning. Particle amplitude is not derived from an instantaneous source waveform sample.

Time, panning & LFO

Time distributions

Particle times are scheduled over the range in which a full grain can fit. If the requested grain duration is longer than the source, the effective grain duration is clamped to the source duration.

ModeBehavior
LinearEvenly spaced starts from the first valid position to the last. With one particle, it is placed halfway through the valid start range.
ExponentialUses (1-exp(-3t))/(1-exp(-3)). Spacing becomes progressively tighter, so particle density increases toward the end.
RandomEach start is drawn uniformly from the valid start range.

The same scheduled time is used to query the source Pitch and Intensity contours. For Sounds whose time domain does not start at zero, the script adds the source start time for analysis while keeping synthesis and visualization zero-based.

Panning

ModePan rule
Pitch-derived75 Hz maps to full left, 600 Hz to full right, with a monotonic linear mapping between them. Unvoiced particles use a random display/pan anchor in the same range.
RandomUniform random pan from 0 to 1 for every particle.
FixedAll particles use Fixed_pan from 0 (left) to 1 (right).

Stereo gains use constant-power panning: L = sqrt(1-pan), R = sqrt(pan).

LFO

When enabled, the LFO modulates the particle amplitude according to zero-based output time:

lfo(t) = 0.5 × [1 + sin(2π × LFO_frequency × t)] grainAmp = grainAmp × lfo(t)

This is an amplitude controller sampled once per particle; it is not a continuous tremolo applied sample-by-sample to each particle.

Parameters

ParameterDefaultValid behavior
PresetCustomCustom plus five named presets.
Number_of_particles1001–10,000.
Grain_duration_s0.050Must be positive and at least two samples at the source sampling rate. If longer than the source, it is clamped to the source duration.
Number_of_partials101–32. Combined complexity particles × partials must not exceed 50,000.
Spectral_brightness0.780–1; maps to partial roll-off exponent 2.6–0.6.
Spectrum_typeHarmonicHarmonic or Bell-inharmonic.
Inharmonicity0.0200–0.10. Used by Bell-inharmonic spectra.
Unvoiced_noise_mix0.350–1. Scales the carrier of unvoiced/noise particles.
Envelope_shapeHannHann, edge-normalized Gaussian, or Rectangular.
Panning_modePitch-derivedPitch-derived, Random, or Fixed.
Fixed_pan0.5Validated to 0–1 only when Fixed panning is selected.
Apply_LFOOffEnables per-particle amplitude modulation.
LFO_frequency0.5 HzMust be greater than zero when LFO is enabled.
Time_distributionLinearLinear, Exponential, or Random.
Draw_visualizationOnDraws the process visualization.
Play_resultOnAutomatically plays the result.

Visualization

When Draw_visualization is enabled, the Picture window shows:

For unvoiced particles: the plotted y-position is only the random anchor used for display and pitch-derived panning. The actual particle carrier is broadband noise.

Output & limits