Tenebrae — user manual

A liturgy of shadows — cascaded high-gain distortion for the heaviest rhythm tone.

What it is

Tenebrae is a high-gain rhythm-guitar distortion built around a cascade of three oversampled waveshaper stages, each progressively tighter and darker than the last, so the tone converges onto a focused "chug" band instead of piling up into an ever-fizzier mess as gain stacks. It is not a boost/overdrive (that is the sibling plugin overture's job) and it is not a cab sim - it is the "wall of gain" itself: the core distortion stage a boost pedal pushes into, and the stage a cab sim/IR loader sits after.

Where it sits in a heavy production chain

Tenebrae is the main gain stage. A typical chain:

Guitar -> noise gate -> boost/tight-boost (optional) -> Tenebrae (high-gain distortion) -> cab sim / IR loader -> reverb/mix bus

Run a tightening boost (like overture) ahead of it if you want extra low-end control before the cascade, and a cab sim/IR loader after it - Tenebrae itself has no cabinet simulation, by design, so it stays a clean building block you can pair with whichever cab sim fits the rest of your chain.

Signal flow

Input -> Tight (HPF, 20-300 Hz) -> Bright (switch) -> Gain (0-40 dB) -> [8x oversampled]
              Cascade stage 1 -> Cascade stage 2 -> Cascade stage 3   (Voicing: Tight/Loose)
                                                              |
   Output <-- Mix <-- Level <-- Gate <-- Presence <-- Treble <-- Mid <-- Bass <--+  (tilted by Tone Voice)
     ^
     |
delay-compensated dry path

v0.3.0 adds a second engine. With Engine set to Triode the three waveshaper stages above are replaced by three modelled triode stages, and an optional Power Amp block sits after them (still inside the oversampled region):

... -> Gain -> [2x / 4x / 8x oversampled, set by Quality]
              Triode stage 1 -> Triode stage 2 -> Triode stage 3 -> Power Amp (optional)
                                                              |
   Output <-- Mix <-- Level <-- Gate <-- Presence <-- Treble <-- Mid <-- Bass <--+
     ^                            ^
     |                            +-- skipped when Power Amp is on (Presence moves into the
     |                                power amp's feedback loop instead)
delay-compensated dry path

  input --> pre-distortion key tap --> detector band-pass --> Gate detector  (Gate Key = Pre)

Engine defaults to Classic, so nothing changes until you choose otherwise.

The cascade runs inside an oversampled block so the harmonics generated by all three nonlinearities - not just the first - stay out of the audible band. The dry path used by Mix is automatically delay-compensated against that oversampling latency, and the plugin reports its total latency to the host so playback stays sample-accurately aligned with every other track. See docs/architecture.md for the full engineering breakdown, including the per-stage cascade voicing table and the latency-compensation strategy.

Reported latency

Oversampling is Tenebrae's only source of reported latency, and both Engine and Quality change it. Classic always runs its own fixed 8x oversampling. Triode runs at whichever factor Quality selects - Eco (2x, polyphase IIR half-band), Standard (4x, polyphase IIR half-band) or HQ (8x, equiripple FIR for linear phase).

The guarantee is that the reported figure is exactly the real one: for every Engine/Quality combination, the reported latency equals the measured impulse delay, and at Mix = 0% the output is the input delayed by exactly that figure, with worst residual below −100 dB (tests/LatencyTests.cpp, "T-L1: reported latency equals measured impulse delay"). Because setLatencySamples() can only be called from the message thread, both Engine and Quality are deliberately not automatable - switching either is a discrete, 2 ms-crossfaded configuration change (see Known limitations), never a live-automated control.

HQ reports the most latency of the three Triode options; Eco reports the least. No absolute sample count is published here - the exact figure depends on your build and sample rate, and no single number is pinned as a constant in the source tree. Check your host's own plugin-delay-compensation readout for the figure that actually applies to your session.

Presets

The preset bar at the top of the plugin window (< / preset name / > / Save / Save As... / Delete / Import... / Export...) gives you twelve factory starting points plus your own saved presets. Click the preset name to browse the full Factory/User menu, or use the arrows to step through presets alphabetically. See docs/presets.md for what each factory preset does. "Set current as default" (in the preset menu) makes whatever is currently dialled in load automatically the next time you open the plugin.

Parameter reference

Parameter Range Default Unit What it does
Tight 20 – 300 90 Hz High-pass filter placed before the gain cascade. Raising it strips more low end out of the signal before it hits the clipper stages, keeping palm mutes and low-string chugs percussive instead of farting out once the cascade saturates them. Lower it for a fuller, boomier low end (useful on drop tunings you want to feel "big" rather than tight); raise it for maximum palm-mute articulation.
Gain 0 – 40 24 dB Pre-gain into the oversampled 3-stage waveshaper cascade - the main "how much distortion" control. Each cascade stage also has its own fixed internal drive on top of this, so even Gain at 0 dB still produces a genuinely saturated, high-gain tone; this control is about pushing the cascade harder, not about switching distortion on and off.
Voicing Tight / Loose Tight Switches the cascade's fixed per-stage asymmetry and interstage filtering between two voicings. Tight (default) is the tighter, more modern-leaning cascade the plugin was originally voiced around. Loose is a softer-driven, wider-band alternative - less asymmetric clipping and looser interstage filtering at every stage, for a more vintage-leaning, slightly airier and boomier character. This is a discrete switch (like an amp channel select), not a smoothly-automatable control, so expect a small audible step at the instant you switch.
Bright Off / On Off Engages a fixed high-shelf pre-emphasis applied before the gain cascade, modelled on the "bright switch" found on many high-gain amp channels (and, loosely, on the presence peak of a brighter cabinet - Tenebrae has no cab simulation of its own, so this is the closest "cab-adjacent" control it offers). Because the boosted signal then passes through three cascaded clipping stages, its effect on overall loudness is intentionally subtle - saturation compresses the extra top end back down - what it changes is harmonic content and pick-attack sizzle feeding the cascade, not raw output level.
Bass -15 – +15 0 dB Low-shelf band of the post-cascade tone stack, centred at 150 Hz. Boost for a fuller, more low-end-forward chug; cut to tighten up further after the cascade (in addition to what Tight already removed before it).
Mid -15 – +15 0 dB Peaking band of the post-cascade tone stack, centred at 650 Hz with a moderately narrow Q. This is the classic "scooped mids" control for high-gain rhythm tone - cut it for the mid-scooped, mid-2000s-metal chug sound; boost it (or leave near 0) to keep enough mid presence to cut through a dense mix.
Treble -15 – +15 0 dB High-shelf band of the post-cascade tone stack, centred at 5 kHz (raised from 3.5 kHz in v0.2.0 to sit clearly above both Bright's pre-cascade shelf and the new Presence control - see below). Boost for more pick attack and top-end sizzle; cut to tame fizz left over from the cascade's harmonics, especially useful if you are not running a cab sim/IR loader after Tenebrae to do that top-end rolloff for you.
Tone Voice Flat / Scoop / Boost Flat A one-switch tilt applied on top of the (still fully live) Bass/Mid/Treble knobs above, for quickly auditioning a canned tone-stack character. Flat applies no tilt. Scoop tilts bass and treble up and mid down - the classic high-gain-rhythm "smiley" curve. Boost tilts mid up (and bass down slightly) for a tone that cuts through a mix, at some cost to low-end weight. Like Voicing, this is a discrete switch, not a smoothly-automatable control.
Presence -12 – +12 0 dB High-shelf control at 2.4 kHz, applied after the cascade and tone stack (unlike Bright, which is pre-cascade). Modelled on the reference high-gain amp class's Presence control - a power-amp feedback stage that shapes the already-distorted signal's upper-mid/treble energy. At 0 dB (the default) this control is a true passthrough - it introduces no coloration until you move it. Use it to add "cut" or bite on top of the cascade's own harmonic content without re-feeding the cascade itself (that's what Bright is for). With Power Amp on, this post-EQ shelf is bypassed and Presence instead becomes a cut-only control inside the power amp's feedback return path - see Under the hood.
Gate Threshold -80 – 0 -48 dB Level below which the Gate closes. Gates the fully-voiced wet signal (after the cascade and tone stack), so it catches noise generated by the cascade's own gain, not just the input's own noise floor. Raise it (towards 0 dB) for a more aggressive gate that clamps down harder between notes; lower it (towards -80 dB) to let quieter material (sustain tails, ambience) through untouched.
Gate Attack 0.1 – 20 1 ms How quickly the gate opens once the signal crosses Threshold. The default (1 ms) is fast enough that pick attacks are never clipped or delayed.
Gate Hold 0 – 500 20 ms How long the gate stays open after the signal drops back below Threshold, before Release begins. Prevents a held note or palm-muted chord's own natural amplitude ripple from re-triggering/chattering the gate.
Gate Release 5 – 2000 150 ms How slowly the gate closes once Hold expires. Shorter releases suit fast, percussive palm-muted rhythm parts; longer releases let sustained chords/notes decay naturally instead of being cut off abruptly.
Gate Off / On On Bypasses the entire Gate module when off (a true passthrough - Threshold/Attack/Hold/Release have no effect while off). Defaults to on, unlike every other v0.2.0 addition - the research behind this plugin's rework is unanimous that a gate is a structural expectation of "tight chug" tone in this genre for a cascaded high-gain distortion, not an optional add-on; loading an old (pre-v0.2.0) session will therefore engage the Gate at its default settings on top of whatever you had dialled in, which may audibly change the tail/silence behaviour of that session - see the CHANGELOG for details.
Level -24 – +24 0 dB Output trim, applied after Presence and the Gate, before the dry/wet Mix. Use it to match Tenebrae's output level to the rest of your chain, especially after pushing Gain or the tone-stack bands hard.
Engine Classic / Triode Classic Selects the tone-generating core. Classic is exactly the v0.2.0 distortion - the same three-stage waveshaper cascade, unchanged. Triode replaces those stages with modelled 12AX7 gain stages that carry state: the stage biases itself toward cutoff after you hit it hard and takes about 20 ms to recover (this is what makes palm mutes feel compressed and chugs "bloom"), the cathode network gives bass and treble genuinely different amounts of drive, and each stage's Miller capacitance rolls off the one after it. Expect more touch response and more variation between a light and a heavy pick attack; Classic is the more uniform, more predictable of the two. Changing Engine changes the plugin's reported latency, so it is not automatable - set it and leave it.
Quality Eco / Standard / HQ Standard Oversampling for the Triode engine (Classic always runs at its own fixed 8x, so this control does nothing while Classic is selected). Eco (2x) is the lowest-latency, lowest-CPU option for tracking. Standard (4x) is the default and what you should mix on. HQ (8x, linear phase) is a mixdown/bounce setting - it has the cleanest alias floor and the most latency. Like Engine, it changes reported latency and is not automatable.
Bias Shift 0 – 200 100 % How much the Triode stages bias themselves toward cutoff after being overdriven - the "blocking distortion" that makes a cranked preamp sag and breathe per note. 100 % is the voicing's own calibrated depth. Turn it down for a steadier, more modern, more compressed-sounding response; turn it up for more splat and more obvious recovery between chugs. No effect on the Classic engine.
Power Amp Off / On Off Engages a modelled power-amp stage after the cascade: a negative-feedback loop around a saturating output transformer, with supply sag. This is where Resonance and Sag do their work, and it changes what Presence does (see below). Off by default, so it never alters an existing session. Triode engine only.
Resonance 0 – 12 0 dB Low-frequency depth, and the closest thing Tenebrae has to a "how big does the cab feel" control. It works the way the hardware does - by cutting lows out of the power amp's feedback path, which lets more low end through the amp and pushes more of it into the output transformer. So it is not the same move as turning Bass up: Bass is an EQ after the distortion, Resonance changes how the power amp itself behaves. Requires Power Amp to be on.
Sag 0 – 100 0 % How much the power supply droops under load. As you dig in, the output transformer loses headroom and the block compresses; it recovers over about 120 ms. Small amounts add feel and note-to-note dynamics; large amounts get spongy and slow, which is the point for doom/sludge parts. Requires Power Amp to be on.
Gate Key Post / Pre Post Where the gate listens. Post (the v0.2.0 behaviour) listens to the distorted signal - which is the problem with every gate placed after a high-gain cascade: 40 dB of gain squashes the difference between "not playing" and "playing" down to a few dB, so no threshold setting separates them cleanly. Pre listens to a copy of the plugin's input taken before the distortion, through a fixed 80 Hz – 8 kHz detector filter, which keeps the full dynamic range the guitar actually has. If you find yourself unable to get the gate to both stay open on quiet notes and shut up between chugs, this is the control that fixes it.
Gate Hysteresis 0 – 12 0 dB Separates the level at which the gate opens from the level at which it closes, so a note hovering right at the threshold cannot chatter it open and shut. 3–6 dB is usually enough. Pairs naturally with Gate Hold.
Gate Range Mute / 90 – 20 Mute dB How far the gate closes. Mute (the default, and the v0.2.0 behaviour) is a hard silence. Anything else leaves the signal at that many dB down instead, which is often more natural on sustained parts - the noise floor drops out of the way without the track sounding like it was switched off.
Gate Release Mode Manual / Auto Manual Manual uses the fixed Gate Release time above. Auto works out for itself whether a note has stopped or is still decaying: if it stopped, the gate closes fast; if it is decaying, the gate fades at the note's own measured decay rate plus a small margin, so it stays just ahead of the note and the fade hides underneath it. Useful when one Release setting cannot cover both staccato chugs and ringing chords in the same part.
Mix 0 – 100 100 % Dry/wet blend of the whole "wet" chain (everything from Tight through Level) against the untouched input. At 100% (the default) Tenebrae behaves like a normal distortion in the signal path. Lower values blend in some of the original, unprocessed signal, useful for parallel/blended rhythm tones; at exactly 0% the output is a sample-accurate (delay-compensated) passthrough of the input.

Under the hood

Everything below is measured in-tree and runs on every CI push (macOS + Windows, .github/workflows/ci.yml, plus pluginval --strictness-level 10 and auval -strict). See docs/architecture.md for the full derivations.

The triode stages are solved, not shaped

Each Triode-engine stage is built at prepare() from the Dempwolf–Zölzer 12AX7 model (DAFx-11, Table 1 "RSD-1" fit). The stage first solves its own DC operating point (grid at 0 V through the grid leak, cathode capacitor open), then builds a 2048-point static plate curve with the grid stopper in circuit, so grid conduction compresses the positive swing the way the circuit does rather than the way a symmetric clipper does. The published equations are asserted against their published form, and the plate/grid current identity ia == ik − ig holds to within 1e−6 relative over the whole working grid (tests/TriodeStageTests.cpp, "T-C1"). The solved operating points land in the physical range a 12AX7 stage should produce (cathode 0.9–1.9 V, plate 140–210 V; measured Tight 1.475 V / 201.7 V, Loose 1.05 V / 171.9 V), and the resulting curve is not a tanh in disguise: at moderate drive the second harmonic leads the third by at least 6 dB, and at hard drive the two halves of the transfer curve differ by a ratio greater than 1.15 ("T-C2").

Around that static curve sit three time-variant effects a memoryless waveshaper cannot produce:

A power amp where Presence and Resonance are feedback controls

PowerAmp closes a unit-delay negative-feedback loop around an ADAA tanh output-transformer saturator. Resonance and Presence become cut-only shelves inside the feedback return path - cutting the return raises the closed-loop gain in that band, the way a real amp's Depth/Presence controls behave, rather than acting as a second EQ. The measured low-frequency lift matches the closed-loop 1/(1+L) prediction within 1.5 dB and stays within 0.5 dB of flat at both 1 kHz and 5 kHz (tests/PowerAmpTests.cpp, "T-P1"). Sag squeezes the transformer's headroom for a measured depth of 1.9 dB (tested bound 1.0–3.0 dB), with attack τ within ±50% of 5 ms and release τ within ±30% of 120 ms (fit r² > 0.99) ("T-P3").

Stability is computed, not hoped for: the loop's small-signal gain is bounded at 0.5 - at least 6 dB of margin at every frequency up to the oversampled Nyquist - asserted at prepare() and gated in CI across the whole Resonance × Presence grid at every Quality mode ("T-P5"). A separate soak test drives the real nonlinear loop with 10 seconds of adversarial full-scale programme and abrupt level changes: output stays finite throughout, peak stays below 10.0, and no limit cycle survives into silence (peak < −100 dB) ("T-P4").

Anti-aliasing that is consistent by construction

Every Triode-engine shaper runs through first-order antiderivative anti-aliasing (src/dsp/ADAAShaper.h). The stage's lookup table stores its interpolant as cubic Hermite segments and evaluates the antiderivative as the exact quartic antiderivative of those same segments, so F1′ ≡ S identically - tabulating F1 independently would leave a mismatch that the ADAA divided difference amplifies by 1/Δ. That consistency is pinned numerically: the antiderivative differentiates back to the lookup table with worst error under 1e−6, F1 is continuous across every knot, and ADAA agrees with direct waveshaping on a slowly varying signal to under 5e−3 (tests/TriodeStageTests.cpp, "T-C1b"). Published alias-to-signal spec at 1244 Hz and 36 dB pre-gain: −39.4 dB with no oversampling and no ADAA, −76.8 dB at Eco, −88.6 dB at Standard, −90.3 dB at HQ (tests/AliasingTests.cpp, "T-A1").

A gate that can hear the guitar before the distortion does

At 65 dB of pre-cascade gain, a source whose "noise floor" and "playing" levels sit 30 dB apart arrives at a post-distortion detector less than 6 dB apart - no threshold setting separates them. Gate Key = Pre keys the detector from a copy of the input taken before the distortion, through a fixed 80 Hz – 8 kHz band-pass, and preserves at least 25 dB of that range (tests/GateTests.cpp, "T-G6"). Gate Hysteresis is measured within 1.0 dB of the commanded value, and with 4 dB of hysteresis a ±1.5 dB dither around the threshold produces at most one transition ("T-G2"); the worst-case chatter test - a 70 Hz tone sitting exactly at threshold - holds to at most one transition per second ("T-G3"). Gate Range lands within 0.5 dB of the commanded floor, and Mute still reaches −120 dB or below ("T-G4"). Gate Release Mode = Auto distinguishes a stopped note from a decaying one: an abrupt stop reaches the floor within 150 ms, a decaying note is tracked within 1.5 dB, and the resulting fade is a straight line in dB (r² > 0.99) at the note's own measured rate, within 10% of the expected slope ("T-G5"). At its defaults the v0.3.0 gate is byte-identical to the v0.2.0 gate (tests/GateTests.cpp, "T-G1").

Nothing about your old sessions changes

All ten new v0.3.0 parameters default to neutral, and the Classic engine's code is untouched by this release. A v0.2.0 session state renders byte-identically to a fresh v0.3.0 instance at its defaults - compared float for float in the same process (tests/StateTests.cpp, "T-S1") - and all eight original factory presets render exactly as their v0.2.0 values do (tests/PresetManagerTests.cpp, "T-PR2"). With Power Amp off, the branch is never entered at all: rendering with Power Amp pointlessly engaged while Engine is Classic produces byte-identical output to Power Amp off (tests/PowerAmpTests.cpp, "T-P2").

Known limitations

Tips