Hysteresis Distortion — User Guide
A stateful tanh distortion followed by a one-pole recursive lag. The output depends on the current input and the previous output, producing rate-dependent path separation and controllable smoothing/lag.
What this does
The script applies a static tanh saturation with bias and then blends each sample with the previous output. This makes the processor stateful: two identical input samples can produce different outputs when they occur after different histories.
y = tanh((x + Bias) × Drive). It has no modeled coercivity, remanence, domain switching, or multiple stable branches. The visible loop is produced because the recursive lag cannot follow a changing input instantaneously; if the test sweep is made sufficiently slow, the loop collapses toward the single static tanh curve.The effect is nevertheless musically useful: Drive controls saturation, Bias shifts the tanh operating point, and Memory controls the lag applied to the nonlinear signal.
Quick start
- Select exactly one Sound object in Praat.
- Run
Hysteresis_Distortion_v0.5.1.praat. - Choose a preset or keep Manual.
- Choose Memory_mode: legacy per-sample coefficient or sample-rate-independent time constant in milliseconds.
- Adjust Drive, Bias and Output_Gain as needed.
- Choose the output-level policy. The default remains Normalize to 0.95 for compatibility with earlier renders.
- Optionally enable the spectrum panel, then run the script.
Algorithm
1. Static nonlinear stage
The current input sample is shifted by Asymmetry_Bias, multiplied by Drive, and passed through tanh():
With Bias = 0, the static tanh is odd-symmetric. A non-zero Bias moves the zero crossing to x = −Bias and makes the static stage asymmetric. Drive may be positive, zero, or negative: zero produces silence at the nonlinear stage; a negative value reverses the polarity of the tanh mapping.
2. Recursive lag
The nonlinear sample is then blended with the previous output. Praat evaluates the Formula from left to right in place, and the script explicitly reads self[row, col − 1], so each channel feeds back only its own previous output.
The first sample is intentionally initialized as y[1] = f[1], rather than assuming an earlier zero state. This avoids a forced startup transient for a file beginning at a steady non-zero value.
3. Mean removal and gain
After the recursion, the script runs Subtract mean, then applies Output_Gain. Mean removal is a file-dependent post-process: it removes the constant component left by bias/asymmetry but is not part of the recursive equation itself.
Memory modes
Per-sample coefficient (legacy)
Hysteresis_Memory is the coefficient m. For 0 < m < 1, its equivalent one-pole time constant is:
This means a coefficient that sounds large numerically can still represent a very short lag. At 44.1 kHz, for example, m = 0.90 is about 0.215 ms and m = 0.99 about 2.26 ms. The report prints the actual time constant for the current file.
- m = 0: no memory; the processor reduces to the static tanh stage followed by mean removal and level processing.
- m = 1: a non-decaying state / infinite time constant. The script reports this and reduces it to 0.99.
- m > 1: outside the supported stable coefficient range; reduced to 0.99 with a report note.
- m < 0: not supported by this intentionally non-negative memory model; raised to 0. Negative coefficients are not described as inherently unstable.
Time constant in ms
In this mode, Memory_time_ms is the active memory control and must be above zero. The script computes:
Because the coefficient is derived from the file's sampling rate, the requested time constant is consistent across files with different sample rates.
Parameters
| Parameter | Default | What it controls |
|---|---|---|
| Preset | Manual | Sets Drive, legacy memory coefficient, Bias and Output_Gain. It does not change Memory_mode, Memory_time_ms, Output_level, spectrum settings or loop-test frequency. |
| Drive | 2.0 | Multiplier inside the tanh stage. |
| Memory_mode | Per-sample coefficient | Selects legacy coefficient control or a sample-rate-independent time constant. |
| Hysteresis_Memory | 0.3 | Legacy per-sample coefficient. |
| Memory_time_ms | 5.0 ms | Time constant used only in time-constant mode. |
| Asymmetry_Bias | 0.0 | Offsets the input before tanh; non-zero values create an asymmetric static mapping before mean removal. |
| Output_Gain | 0.9 | Constant multiplier after recursion and mean removal. Negative values invert the final polarity before the output-level stage; zero gives silent output. |
| Output_level | Normalize to 0.95 | Preserve, conditional global attenuation, or peak normalization. |
| Show_spectrum | Off | Replaces the short waveform zoom with a spectrum comparison; adds analysis time. |
| Spectrum_reference | Matched peak | Matched peak removes the global level difference before the spectral comparison; it does not isolate harmonics. |
| Loop_test_frequency_Hz | 220 Hz | Requested sweep rate for the synthetic loop panel. The panel title reports the actual rate obtained from its integer step count. |
| Draw_visualization | On | Draws the analysis/diagnostic page. |
| Play_result | On | Plays the processed Sound after completion. |
Presets
The names describe musical behavior rather than claiming models of specific analog devices. Numeric values are unchanged from the corresponding legacy presets.
| Preset | Drive | Memory coefficient | Bias | Output Gain |
|---|---|---|---|---|
| Warm Saturation | 1.5 | 0.25 | 0.00 | 0.95 |
| Dark Lag Saturation | 2.5 | 0.75 | 0.00 | 0.80 |
| Biased Lag Saturation | 3.0 | 0.40 | 0.20 | 0.80 |
| Sluggish Fuzz | 10.0 | 0.50 | 0.05 | 0.50 |
| Hard Saturation | 20.0 | 0.10 | 0.00 | 0.40 |
Output & level behavior
The processed object is named <source>_Hyst_<presetName>, using internal names such as WarmSaturation, DarkLagSaturation, BiasedLagSaturation, SluggishFuzz and HardSaturation. The original Sound remains unchanged.
| Output level | Behavior |
|---|---|
| Preserve shaped level | No final peak scaling. Output_Gain retains its full magnitude effect; peaks above 1.0 are reported as a warning. |
| Attenuate to 0.95 only if peak > 0.95 | If the measured peak exceeds 0.95, the whole Sound is scaled down to 0.95. If it is already below 0.95, nothing changes. This is global attenuation, not a sample-by-sample limiter. |
| Normalize to 0.95 | For any non-silent output, scales the peak to 0.95. A silent-output guard skips scaling when the peak is zero. |
The processor preserves the number of input channels. The recursion is independent per channel; the visualization shows both channels for stereo and the first two channels for larger multichannel Sounds.
Visualization
The 8-inch analysis page is diagnostic rather than a claim that one static curve completely describes the rendered audio.
Recursive loop
A synthetic ascending/descending input sweep through the same recurrence. Blue is ascending and red descending. Its width depends on both memory and sweep rate. The title states the actual test frequency. The loop is drawn before mean removal and output-level scaling.
Static tanh stage
The non-recursive tanh((x + Bias) × Drive) curve, including the bias zero-crossing reference. This is also before memory, mean removal and level processing.
Zoom or spectrum
With Show_spectrum off, the first 30 ms (or the whole file if shorter) compares original gray with distorted red, using channel 1 for multichannel display. With spectrum on, original/result spectra are compared up to 8 kHz or Nyquist.
Full output
Displays the actual rendered output after mean removal, Output_Gain and the selected output-level stage.
Spectrum reference
- Matched peak: temporary mono comparison copies are independently peak-scaled to 0.95 before spectrum analysis. This removes the global level difference so spectral-shape changes are easier to see.
- Absolute rendered levels: keeps the actual relative levels of the source and final result.
Matched peak does not isolate harmonics. Differences may reflect the tanh nonlinearity, recursive low-pass/lag behavior, bias, mean removal and the source material itself.
How to interpret the sound
For small signals around the tanh operating point, the memory stage behaves approximately like a first-order low-pass filter:
As Drive increases, tanh contributes nonlinear saturation and harmonics. With Bias = 0 the static waveshaper is odd-symmetric; a non-zero Bias breaks that symmetry before the script removes the final mean, so even-order components can appear for suitable periodic inputs. The recursive lag then changes the magnitude and phase of those components according to frequency.
It is therefore more precise to describe the result as stateful nonlinear saturation plus one-pole lag than as a tape, transformer, or ferromagnetic model.
Limitations & notes
- Not a physical hysteresis model: there is one static equilibrium for each input; no coercivity, remanence, domain dynamics or rate-independent hysteresis is modeled.
- Coefficient mode is sample-rate dependent: use the millisecond mode when an absolute lag time matters.
- Mean removal is global/file-dependent: the final rendered relationship cannot be represented by the pre-mean-removal loop or tanh panel alone.
- No oversampling: tanh is nonlinear and can generate content above Nyquist; the script does not oversample this waveshaper.
- Loop panel is a synthetic diagnostic: it traces a controlled sweep, not the source file's actual input/output trajectory.
- Very high time constants: coefficients approach 1 closely; the recursion remains causal but reacts increasingly slowly.
- Offline Praat processing: this script renders a new Sound object; it is not presented as a real-time plug-in.
Further Reading
- Physical magnetic hysteresis (for contrast): Jiles, D. C., & Atherton, D. L. (1986). “Theory of ferromagnetic hysteresis.” Journal of Magnetism and Magnetic Materials, 61(1–2), 48–60. DOI: 10.1016/0304-8853(86)90066-1.
- Nonlinear audio modeling context: Pakarinen, J., & Yeh, D. T. (2009). “A Review of Digital Techniques for Modeling Vacuum-Tube Guitar Amplifiers.” Computer Music Journal, 33(2), 85–100. DOI: 10.1162/comj.2009.33.2.85.
- Discrete-time systems and IIR filtering: Orfanidis, S. J. (1996). Introduction to Signal Processing. Prentice Hall.
- Band-limiting and aliasing context: Stilson, T., & Smith, J. O. (1996). “Alias-Free Digital Synthesis of Classic Analog Waveforms.” Proceedings of the International Computer Music Conference (ICMC).