Granular Particle Field — User Guide
A stereo granular renderer in which each grain is treated as a particle with its own output time, source position, pitch, envelope, amplitude and pan position.
What this does
Granular Particle Field converts the selected Sound to a mono synthesis source, extracts a fixed number of short grains, and places them into a new stereo output buffer. Each grain is independently assigned a source position, optional varispeed pitch shift, envelope, optional LFO-derived amplitude, and pan position.
The tool separates three different coordinates that are easy to confuse:
- Output time: where the particle is written in the new composition.
- Source position: where its audio is read from the selected Sound.
- Pan: where the particle is placed between left and right.
What is a particle here?
In this script, particle is the event-level representation of one granular sound fragment. It is not a physical particle simulation. One particle stores:
output time source start position grain duration pitch shift envelope LFO amplitude pan position
The audio fragment itself is the grain; “particle” emphasizes that the grain also has a position and other attributes inside a time–pan field.
Quick start
- Select exactly one Sound object.
- Run
Granular_Particle_Field.praat. - Choose Custom or one of the five named presets.
- For Custom, set the grain count and duration, then choose the envelope, pitch, panning, time distribution and source traversal.
- Set Output_duration_s to 0 to use the input duration, or enter another duration.
- Run the script. The result is named
<source>_particles_<preset>.
Presets
| Preset | Grains | Base duration | Envelope | Time | Source | Pan | Pitch / LFO |
|---|---|---|---|---|---|---|---|
| Dense Cloud | 300 | 30 ms | Gaussian | Random | Random | Random | Pitch off; LFO off |
| Sparse Field | 30 | 150 ms | Hann | Linear | Sequential | Position-derived | Pitch off; LFO off |
| Rhythmic Pulse | 80 | 40 ms | Rectangular | Linear | Sequential | Fixed centre | Pitch off; 4 Hz LFO |
| Shimmer | 150 | 60 ms | Gaussian | Exponential | Random | Random | 0 ± 7 st; 0.25 Hz LFO |
| Long Resonance | 15 | 800 ms | Hann | Exponential | Random | Position-derived | 0 ± 3 st; 0.15 Hz LFO |
Named presets overwrite their synthesis settings, but Output_duration_s, Draw_visualization and Play_result remain user-controlled.
Output-time distribution
Pitch is drawn first because downward varispeed can lengthen a grain. The script therefore reserves one common legal start span based on the longest possible pitch outcome:
baseGrainDur = min(requestedGrainDur, inputDuration) minSemitones = basePitch - pitchVariation minPitchFactor = 2^(minSemitones / 12) maxPossibleGrainDur = baseGrainDur / minPitchFactor outputGridSpan = max(0, outputDuration - maxPossibleGrainDur)
Linear
Particles are evenly spaced across 0 ... outputGridSpan. With one particle, it is placed at the midpoint of that span.
Exponential
Particle indices are mapped through:
u = (i - 1) / (N - 1) time = span × (1 - exp(-3u)) / (1 - exp(-3))
This curve compresses successive event spacing toward the end, so the field becomes progressively denser there.
Random
Each particle receives an independent uniform random start inside the safe span.
Source traversal
The requested grain duration is first limited to the source duration:
baseGrainDur = min(Grain_duration_s, inputDuration) maxSourceStart = inputDuration - baseGrainDur
Sequential
Source starts move linearly from the first legal start position to the last. With one grain, the source position is the midpoint of the legal range.
Random
Each grain start is chosen independently and uniformly from the same legal range.
The script keeps these positions as zero-based offsets and adds the Sound's actual start time only when extracting audio, so non-zero Praat time domains are handled correctly.
Pitch shift & grain duration
If pitch shifting is enabled, each particle draws:
semitones = Pitch_shift_semitones
+ uniform(-Pitch_variation_semitones,
+Pitch_variation_semitones)
pitchFactor = 2^(semitones / 12)
The grain is then processed with Override sampling frequency. This is varispeed, not pitch-preserving transposition:
new sampling frequency = sourceSampleRate × pitchFactor rendered duration = baseGrainDur / pitchFactor
- Positive semitones → higher pitch and shorter grain.
- Negative semitones → lower pitch and longer grain.
- Pitch variation is uniform within the requested ± range.
The script does not resample the grain back to the original sampling frequency before mixing; the time-domain lookup performs the varispeed read. Strong upward shifts can therefore have the usual aliasing risk of this simple method.
Envelope & LFO
Hann
gain(x) = 0.5 × [1 - cos(2πx / grainDuration)]
Gaussian
The Gaussian is centred at the grain midpoint with σ = duration/6 and is explicitly edge-normalized so that the theoretical boundaries reach zero.
Rectangular
No envelope is applied. The original extracted samples are used directly, so discontinuities at grain edges are possible.
LFO
The LFO is evaluated once per particle at that particle's output start time:
grainAmplitude =
0.5 × [1 + sin(2π × LFO_frequency × outputTime)]
That scalar is then applied to the entire grain. The script does not run a continuous sine modulation through the samples inside the grain.
Stereo panning
Pan uses the interval 0–1: 0 = left, 0.5 = centre, 1 = right.
| Mode | Pan law |
|---|---|
| Position-derived | pan = sourceOffset / maxSourceStart. The complete legal source-start range maps exactly from L to R. If only one source start is possible, pan = 0.5. |
| Random | Independent uniform pan in 0–1 for every particle. |
| Fixed | Use Fixed_pan, validated to 0–1. |
Channel gains use constant-power panning:
gainL = sqrt(1 - pan) gainR = sqrt(pan)
The two output channels receive the same mono grain at these gains; there is no separate source material for L and R.
Parameters & limits
| Parameter | Default | Behavior |
|---|---|---|
| Preset | Custom | Custom plus five named particle-field settings. |
| Number_of_grains | 100 | 1–5000. |
| Grain_duration_s | 0.050 | Must be > 0; internally limited to the input duration. |
| Envelope_shape | Hann | Hann, Gaussian or Rectangular. |
| Apply_pitch_shift | Off | Enable per-grain varispeed pitch changes. |
| Pitch_shift_semitones | 0 | Centre of the pitch range. |
| Pitch_variation_semitones | 0 | Uniform ± variation; must be ≥ 0. |
| Panning_mode | Position-derived | Position-derived, Random or Fixed. |
| Fixed_pan | 0.5 | Validated to 0–1 when Fixed is active. |
| Apply_LFO | Off | Apply one sine-derived amplitude value per grain. |
| LFO_frequency | 0.5 Hz | Must be > 0 when LFO is active. |
| Time_distribution | Linear | Linear, Exponential or Random output starts. |
| Grain_source | Sequential | Sequential or Random source starts. |
| Output_duration_s | 0 | 0 = input duration; otherwise the requested output duration. |
| Draw_visualization | On | Draw Source → Output → Particle field → Summary. |
| Play_result | On | Play the created Sound. |
Both input and output must be at least two samples long at the source sampling rate.
Visualization
The Picture window contains four user-facing regions:
- Source: the exact unnormalized mono signal used by the granular engine. A multichannel input is therefore labelled Source (mono).
- Output: the final stereo result drawn with the same amplitude scale as Source.
- Particle field: x = output time and y = pan. L/C/R and time references are marked explicitly.
- Summary: grain count, base duration, preset, source/output duration and the particle-marker encoding.
Particle blue has one semantic meaning. With LFO disabled, all particles use the same marker style. With LFO enabled, marker size and blue intensity encode the stored per-grain LFO amplitude.
Output behavior
Mixing
Each shifted grain is added directly into the two mono output buffers only over its own write interval. Overlapping particles are summed.
Normalization
After L/R are combined to stereo, the script measures the Sinc70 absolute peak. Every non-silent result is peak-normalized to 0.95:
if peak > 0:
Scale peak to 0.95
This is true peak normalization and can increase a quiet particle field.
Naming
<source name>_particles_<preset name>
The original Sound remains unchanged. Temporary mono source and mix buffers are removed after rendering. Because no random-seed control is exposed, presets and Custom settings that use random source positions, random pan or pitch variation can produce a different realization on each run.