Stochastic Time Folding — User Guide

Iterative time-domain processing that alternates stochastically between recursive past/present/future folding and uniform amplitude scaling. Each pass uses a new temporal offset, while an adaptive probability threshold evolves across the sequence.

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

What this does

Stochastic Time Folding processes one selected Sound through a sequence of stochastic passes. Every pass draws a temporal offset and makes one probability decision for the complete Sound:

iteration → evolve threshold → choose one fold distance → draw one probability value → FOLD entire buffer or AMP-scale entire buffer → next iteration → attenuate only if final peak exceeds Scale_peak

The FOLD branch combines the current sample with material ahead of it and with already-processed material behind it. Repeating this operation over several iterations creates a cumulative temporal diffusion whose geometry changes from pass to pass.

The stochastic choice is made once per iteration. The resulting pass is therefore globally FOLD or globally AMP rather than a sample-by-sample mixture of the two branches.

Quick start

  1. Select exactly one Sound object containing at least three samples.
  2. Run Stochastic_Time_Folding.praat.
  3. Choose Default, Gentle Folds, Aggressive Folds, Micro Glitch, or Custom.
  4. For Custom, set the iteration count, threshold behavior, fold-divisor range and amplitude range.
  5. Leave Debug_output enabled if you want per-pass peak/RMS diagnostics in the Info window.
  6. Run. The result is named <source>_folded_<preset>.
There is no random-seed control. Threshold direction, fold distances, branch choices and AMP gains can therefore differ between runs.

One processing iteration

For iteration k, the script determines four values before applying any Sound Formula:

  1. the current adaptive threshold;
  2. one random fold divisor and its derived forward/backward distances;
  3. one random probMask in 0…1;
  4. one random iterAmpGain from the amplitude range.
if probMask < adaptiveThreshold: run FOLD pass else: run AMP pass

The chosen branch is then applied across every channel and every sample of the Sound for that iteration.

Probability interpretation

Because probMask is uniformly distributed between 0 and 1, the current threshold is the probability that a given iteration will take the FOLD branch. For example, threshold 0.7 corresponds to a 70% FOLD probability for that pass.

FOLD branch

Let D be the forward distance, B the backward distance, and A the effective averaging divisor.

Where both temporal references are inside the Sound:

y[n] = ( current[n] + future[n + D] + processedPast[n - B] ) / A

At samples where either reference would fall outside the Sound, the sample is left unchanged for that pass.

Recursive left-to-right behavior

Praat evaluates the Sound Formula from earlier samples toward later samples. Consequently:

The backward path therefore creates a recursive within-pass cascade rather than a three-tap feed-forward average from a frozen snapshot.

Averaging divisor

A = max( 3, Fold_average_divisor )

Values below 3 therefore have the same effective DSP value of 3. Larger values attenuate the folded contribution more strongly.

AMP branch

The alternative branch applies one uniform gain to the complete Sound:

iterAmpGain ~ Uniform( Amplitude_min, Amplitude_max ) y[n] = x[n] × iterAmpGain

The same gain is used for all samples and channels during that iteration. Consecutive AMP passes therefore multiply their gains cumulatively.

The amplitude range is internally reordered when necessary, so reversed Custom minimum/maximum values remain valid.

Adaptive threshold evolution

The initial value is clamped to the canonicalized threshold limits:

threshold = clamp( Initial_threshold, thresholdLimitLow, thresholdLimitHigh )

From iteration 2 onward:

change ~ Uniform( Threshold_var_min, Threshold_var_max ) direction = random choice of + or - threshold = clamp( threshold ± change, thresholdLimitLow, thresholdLimitHigh )

The threshold can therefore move upward or downward from one pass to the next. The limits themselves are sorted and clamped to the probability interval 0…1.

Threshold evolution changes the probability of selecting FOLD versus AMP on later passes. It does not alter the fold equation itself.

Fold distance

One divisor is drawn for every iteration:

divisor ~ Uniform( Fold_distance_min, Fold_distance_max ) forwardSamples = max( 1, round(totalSamples / divisor) ) backwardSamples = max( 1, round( forwardSamples / Fold_backward_divisor ) )

The forward delay in seconds is approximately:

forwardTime ≈ sourceDuration / divisor

Because the form values are divisors, a smaller divisor produces a larger temporal offset and a larger divisor produces a shorter offset.

The distance pair is internally sorted, so reversed Custom minimum/maximum entries are accepted. Both forward and backward offsets are clamped to at least one sample.

Presets

PresetIterationsInitial thresholdVariationLimitsFold divisorsAmplitudeAvg / Back div
Default60.500.20–0.200.10–0.903–120.70–1.203 / 2
Gentle Folds40.400.10–0.150.15–0.855–150.90–1.103 / 2
Aggressive Folds90.600.25–0.350.05–0.952–100.50–1.503 / 2
Micro Glitch120.550.15–0.250.10–0.902–60.60–1.403 / 3

Named presets overwrite the processing values shown in this table. They do not overwrite Scale_peak, Debug_output, Draw_visualization, or Play_result.

Parameters & effective limits

ParameterDefaultExact behavior
PresetDefaultDefault, Gentle, Aggressive, Micro Glitch, or Custom.
Fold_iterations6Natural number of complete-buffer stochastic passes.
Initial_threshold0.5Initial FOLD-branch probability, clamped to the effective threshold limits.
Threshold_var_min / max0.2 / 0.2Magnitude range for the random positive-or-negative threshold step.
Threshold_limit_min / max0.1 / 0.9Internally sorted and clamped to 0…1.
Fold_distance_min / max3 / 12Divisor range used to derive the per-iteration forward offset.
Amplitude_min / max0.7 / 1.2Uniform gain range for AMP iterations; internally sorted.
Fold_average_divisor3Division applied to the three-term FOLD sum; effective minimum is 3.
Fold_backward_divisor2Forward distance is divided by this value to derive the backward offset; result is at least one sample.
Scale_peak0.96Maximum final sample peak; the result is attenuated only when it exceeds this value.
Debug_outputOnReport branch, peak, RMS and gain change for every iteration.
Draw_visualizationOnDraw source/result waveforms, threshold evolution, fold distances and summary.
Play_resultOnPlay the completed Sound.

Channels, duration & sample rate

The selected Sound is copied directly and the Formula operates on its existing channel rows.

The same per-iteration threshold, branch decision, fold distances and AMP gain are shared across all channels, while each channel's sample values are processed independently.

Final peak ceiling

After all iterations, the script measures the ordinary absolute sample extremum:

if resultPeak > Scale_peak: Scale peak: Scale_peak else: leave level unchanged

This is an attenuate-only safety ceiling, not target normalization. A result whose peak is below the default 0.96 remains below 0.96, so cumulative AMP attenuation remains audible.

Digital silence is left untouched.

Visualization

The v0.5 Picture view contains:

  1. Original waveform.
  2. Folded waveform.
  3. Threshold evolution — the actual threshold stored for each iteration.
  4. Fold distances — the actual forward distance in milliseconds selected for each iteration.
  5. Summary bar — preset, source, iterations, threshold limits, divisors, amplitude range and output name.

Waveform scale

The Original and Folded panels each use Praat's automatic waveform range. They are therefore not a shared-amplitude-scale comparison.

Threshold panel

The plotted points show the actual threshold values used by the stochastic branch decision. The dotted horizontal references represent the entered threshold-limit values.

If Custom threshold limits were entered outside 0…1 or in reverse order, the DSP uses canonicalized limits. The title and summary use those effective limits; the dotted reference calls themselves use the form variables.

Fold-distance panel

Each bar is the actual selected forward fold distance converted to milliseconds. The shorter backward distance is not drawn separately.

Output behavior