Fractal Convolution Matrix — User Guide

A cascade of sparse causal FIR delay kernels whose base delay is halved at each depth. The repeated 2:1 scaling creates a self-similar, fractal-like echo structure while preserving the source channel count.

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

What this does

Fractal Convolution Matrix processes the selected Sound through a sequence of feed-forward delay stages. Each stage contains one dry tap plus several delayed copies of the previous stage. The delayed taps are causal — they read earlier samples only — and every stage uses a fixed snapshot of its input, so the process is an FIR cascade rather than feedback.

The base delay at depth d is derived from the original source length:

D[d] = round(originalSamples / (Kernel_divisor × 2^d))

Depth 1 therefore has the longest base delay. Every deeper level approximately halves that delay. Repeating a similar sparse tap pattern at these 2:1 scales creates the fractal-like temporal organization.

This is not convolution reverb with an external impulse response. Each depth convolves the previous result with an internally constructed sparse FIR kernel made from a dry path and delayed taps.

What is “fractal” here?

The term refers to self-similar organization across temporal scales. The same tap layout is reused at each depth, while the delay unit changes geometrically:

Depth 1: base delay ≈ T / (divisor × 2) Depth 2: base delay ≈ T / (divisor × 4) Depth 3: base delay ≈ T / (divisor × 8) ...

where T is the original source duration, apart from sample rounding. The script does not calculate a fractal dimension and does not claim that the resulting waveform is a mathematical fractal. “Fractal” describes the repeated 2:1 delay hierarchy.

Quick start

  1. Select exactly one Sound object.
  2. Run Fractal_Convolution_Matrix.praat.
  3. Choose Custom, Subtle, Medium, Heavy, or Extreme.
  4. For Custom, set Tail_duration_s, Fractal_depth, Convolution_width, Kernel_divisor, and Amplitude_reduction.
  5. Set Scale_peak and Fadeout_duration_s.
  6. Run the script. The result is named <source>_fractal_<preset>.
Useful starting point: Medium Fractal gives five active depth requests, width 3, a 2-second tail, and moderate delayed-layer decay.

The sparse FIR kernel

At each active depth, the script first snapshots the complete result from the previous depth. It then rebuilds the current result from that snapshot plus delayed copies read from the same fixed snapshot.

Dry path

y[n] starts as x[n]

Delayed taps

For tap k = 1 ... Convolution_width:

weight[k] = 1 / (1 + k) weightSum = Σ weight[k] depthGain[d] = (1 - Amplitude_reduction)^d tapGain[d,k] = depthGain[d] × weight[k] / weightSum tapDelay[d,k] = k × D[d] y[n] = x[n] + Σ tapGain[d,k] × x[n - tapDelay[d,k]]

Samples before the beginning of the stage are treated as zero. There are no future-sample taps and no zero-delay tap duplicated inside the wet bank.

Because the delayed weights are normalized by weightSum, increasing Convolution_width adds more delay taps and changes their distribution without automatically multiplying the total delayed-layer gain. At one depth, the delayed taps together sum to depthGain.

What Amplitude_reduction means

Amplitude_reduction controls only the newly added delayed layer:

depthGain[d] = (1 - reduction)^d

For example, with reduction = 0.15, the delayed-bank gains are approximately 0.85, 0.72, 0.61, 0.52... across successive depths. The dry path of each stage is not multiplied by this value.

Depth & delay scale

The base delay is tied to the original source length, not to the extended source-plus-tail canvas:

baseDelaySeconds[d] ≈ originalDuration / (Kernel_divisor × 2^d)

Sample rounding makes the exact value:

D[d] = round(originalSamples / (Kernel_divisor × 2^d)) baseDelayMs[d] = 1000 × D[d] / sampleRate

The farthest tap at one depth is:

maxTapDelay[d] = Convolution_width × D[d]

Because the depth kernels are cascaded, their delays can accumulate. The script reports the theoretical maximum cumulative span:

fractalSpan = Σ activeDepths (Convolution_width × D[d]) / sampleRate

If a requested depth rounds to a base delay below one sample, that depth is skipped and reported. Depth is limited to 30.

Tail & fadeout

Tail duration

Tail_duration_s appends digital silence before the FIR cascade. Its purpose is to provide output space into which delayed copies can arrive. It does not participate in the base-delay calculation.

output canvas duration = original duration + Tail_duration_s

If the tail is shorter than the theoretical cumulative delay span, the late part of the cascade is truncated by the finite output canvas. The script reports this condition rather than changing the requested delay scales.

Fadeout

The final portion of the complete output receives a half-cosine fade:

gain(x) = 0.5 + 0.5 × cos( π × (x - fadeStart) / effectiveFadeout )

effectiveFadeout = min(Fadeout_duration_s, outputDuration). A value of 0 disables the fade. The fade always acts on the end of the whole result; if no tail is used, it therefore fades the end of the original-duration signal.

Presets

PresetTailDepthWidthDivisorReductionPeakFade
Subtle1.5 s32120.120.920.8 s
Medium2.0 s53100.150.901.0 s
Heavy2.8 s7480.180.881.4 s
Extreme4.0 s10560.220.861.8 s
Custom2.0 s53100.150.901.0 s

Named presets overwrite the synthesis parameters shown above. Draw_visualization and Play_result remain under the user's control.

Parameters & limits

ParameterDefaultMeaning / validation
PresetCustomCustom or four named presets.
Tail_duration_s2.0Extra silent canvas after the source; must be ≥ 0.
Fractal_depth5Requested cascade depth, 1–30. Very deep sub-sample delays are skipped.
Convolution_width3Number of causal delayed taps per active depth, 1–64.
Kernel_divisor10Sets the depth-1 delay scale; must be > 0 and must leave at least a one-sample delay at depth 1.
Amplitude_reduction0.15Controls geometric decay of each depth's delayed bank; valid range 0 ≤ value < 1.
Scale_peak0.90Peak-normalization target before the final fadeout; valid range (0, 1].
Fadeout_duration_s1.0Requested final half-cosine fade; must be ≥ 0.
Draw_visualizationOnDraws Source → Fractal delay matrix → Output → Summary.
Play_resultOnPlays the result after processing.

Visualization

The Picture window directly shows the implemented FIR structure:

Source and Output use a shared amplitude scale. Multichannel Sounds are folded to mono only for visualization; the rendered audio retains its channels.

Output behavior

Channel count

The FIR formulas operate independently on every channel, so the output retains the input channel count.

Peak scaling

After the cascade, the script measures the absolute peak with Sinc70. Any non-silent result is peak-scaled to Scale_peak. This is true normalization and can increase a quiet result.

Order matters: normalization occurs before the final fadeout. There is no second normalization afterward. If the fade attenuates the sample that previously contained the maximum peak, the final peak can therefore end below Scale_peak.

Duration and naming

The result duration is the original duration plus the requested tail. It is named:

<source name>_fractal_<preset name>

The original Sound is left unchanged and temporary processing objects are removed.