This project implements Julius O. Smith III's Extended Karplus-Strong (EKS) physical modeling algorithm for plucked string synthesis, realized in the Faust programming language. The system features a 32-step sequencer with progressive rock-style arpeggiated patterns, stereo width modulation, LFO-based auto-panning, and high-quality Zita reverb. The implementation extends the original Karplus-Strong algorithm with frequency-dependent damping, pick position modeling, and dynamic-level compensation for realistic string behavior.
Key Features:
- Full EKS algorithm with linear-phase FIR damping filters
- 16-step sequencer with Hz-based tempo control
- LFO modulation for spatial movement
- Zita reverb integration
- Real-time parameter control for live performance
Based on: Smith, J. O. (2010). "Physical Audio Signal Processing". CCRMA, Stanford University.
Physical modeling recreates instrument behavior through mathematical simulation of acoustic physics. The Extended Karplus-Strong algorithm models plucked string instruments by simulating:
- Wave propagation: Delay line representing string length
- Energy dissipation: Frequency-dependent damping filters
- Excitation: Initial displacement from plucking action
- Boundary conditions: Fixed endpoints (bridge and nut)
The EKS extends the basic Karplus-Strong (1983) with:
- Linear-phase damping: FIR3 filter for frequency-dependent decay
- Pick position modeling: Comb filter for pluck location effects
- Dynamic-level compensation: Adjustable high-frequency content
- Brightness control: Independent tuning invariance parameter
| Symbol | Name | Description | Units/Range |
|---|---|---|---|
| Z-transform variable | Complex frequency variable in discrete-time domain | ||
| Unit delay operator | Delays signal by one sample | - | |
| Sample index | Discrete time index | ||
| Input signal | Discrete-time input at sample |
- | |
| Output signal | Discrete-time output at sample |
- | |
| Sample period | Time between samples = |
seconds | |
| Sample rate | Samples per second (typically 44.1 or 48 kHz) | Hz | |
| Frequency | Fundamental frequency of string | Hz | |
| Fundamental frequency | Same as |
Hz |
| Symbol | Name | Description | Range |
|---|---|---|---|
| Damping filter | Z-domain transfer function for string damping | - | |
| Pick-position filter | Comb filter modeling pluck location | - | |
| Level lowpass filter | Dynamic-level compensation filter | - | |
| Analog lowpass | Continuous-time lowpass prototype | - | |
| Loop gain | Controls overall decay rate of string | ||
| Stretching factor | Controls frequency-dependent damping | ||
| Brightness | High-frequency content parameter |
| Symbol | Name | Description | Derivation |
|---|---|---|---|
| Feedforward coefficient 0 | Current sample weight | ||
| Feedforward coefficient 1 | Delayed sample weight | ||
| Center tap coefficient | FIR3 center tap | ||
| Side tap coefficient | FIR3 side taps (symmetric) | ||
| Impulse response (center) | Same as |
- | |
| Impulse response (sides) | Same as |
- | |
| Recursive coefficient | IIR feedback coefficient |
| Symbol | Name | Description | Typical Range |
|---|---|---|---|
| Period | String fundamental period in samples | ||
| Period (alternate) | Same as |
- | |
| Decay time | Time for -60 dB amplitude decay | 0.1 - 10 seconds | |
| Pick position | Normalized pluck location (0=bridge, 0.5=center) | ||
| Fractional delay | Fine-tuning fractional delay | ||
| Dynamic level | Playing dynamics / velocity |
| Symbol | Name | Description | Units |
|---|---|---|---|
| Angular frequency | rad/s | ||
| Fundamental angular freq | rad/s | ||
| Prewarped frequency | radians | ||
| Laplace variable | Complex frequency in continuous time |
| Symbol | Name | Description |
|---|---|---|
| Indicator function | 1 if condition true, 0 otherwise | |
| Floor function | Largest integer ≤ |
|
| Round function | Nearest integer to |
|
| Multiplication | Scalar or element-wise multiplication | |
| Addition | Summation operator |
| Symbol | Name | Description | Range |
|---|---|---|---|
| Step index | Current sequencer step |
|
|
| Gate/trigger | Binary trigger signal | ||
| Trigger function | Gate at sample |
- |
Period-Frequency relationship:
Loop gain from decay time:
Brightness to filter coefficients:
Pick-position delay (samples):
The simplest damping filter uses a single zero:
Difference equation:
where:
-
$S \in [0,1]$ is the stretching factor -
$b_0 = 1 - S$ (feedforward coefficient) -
$b_1 = S$ (delayed feedforward coefficient) -
$\rho \in (0,1)$ is loop gain controlling decay rate
Brightness mapping:
Decay time calculation:
where
Characteristics:
- DC gain =
$\rho$ (infinite decay at DC when$\rho=1$ ) - Fastest decay at
$S = 0.5$ (original Karplus-Strong) - Higher frequencies decay faster than lower frequencies
Perceptual Effect:
-
Low
$\rho$ (0.5-0.7): String dies out quickly (plucked staccato, muted guitar) -
High
$\rho$ (0.95-0.99): Long, ringing sustain (open strings, sitar-like drone) -
Low
$S$ (0.0-0.3): Dull, dark tone (damped with palm, jazz tone) -
Medium
$S$ (0.5): Classic Karplus-Strong pluck (vintage digital synth) -
High
$S$ (0.7-1.0): Bright, metallic timbre (new steel strings, twangy)
For improved tuning invariance, a symmetric FIR filter is used:
Impulse response:
Symmetric around time
Coefficients from brightness:
Implementation:
Loop gain:
Advantages:
- Linear phase (no phase distortion)
- Better tuning invariance than one-zero
- Symmetric impulse response reduces computational complexity
Perceptual Effect:
-
High
$B$ (0.7-1.0): Bright, clear attack; harmonics ring clearly (fresh strings, aggressive pick attack) -
Medium
$B$ (0.4-0.6): Balanced warmth and presence (well-worn strings, fingerstyle) -
Low
$B$ (0.0-0.3): Dark, mellow sound; emphasizes fundamental (old flatwound strings, thumb picking) - vs. One-Zero: Sounds more "in-tune" across the frequency range; less detuning artifacts
Models the effect of plucking at position
where:
-
$\beta \in (0, 0.5)$ is normalized pick position ($0$ = bridge,$0.5$ = center) -
$N = P$ is period in samples - Delay =
$\lfloor \beta N + 0.5 \rfloor$ (rounded to nearest integer) - Feedforward gain =
$-1$ (inverts and subtracts delayed signal)
Spectral effect: Creates nulls at frequencies
Physical interpretation: Plucking at position
Perceptual Effect:
-
$\beta$ = 0.02-0.05 (near bridge): Thin, nasal, "honky" tone; emphasizes high harmonics (mandolin, banjo) -
$\beta$ = 0.13 (default): Realistic guitar pluck position; balanced harmonic content (standard guitar) -
$\beta$ = 0.25: Rounder, fuller tone; some harmonics missing (classical guitar, neck pickup) -
$\beta$ = 0.4-0.5 (near center): Very soft, hollow sound; many harmonics cancelled (12th fret harmonic area) - Spectral nulls: Certain harmonics disappear completely, creating distinctive timbral "holes" in the sound
Compensates for high-frequency loss based on playing dynamics.
Continuous-time (analog):
where
Discrete-time (bilinear transform):
where
Simplified IIR form:
Dynamic blending:
where:
-
$L \in [0,1]$ is level parameter (fromdynamic_levelslider) -
$L_0(L) = L^{1/3}$ attenuates low levels -
$y_{\text{LP}}[n]$ is lowpass-filtered signal
Characteristics:
- Unity DC gain
- -3 dB at fundamental frequency
$f_1$ - -6 dB/octave rolloff above
$f_1$ - Bypassed at
$L=1$ (maximum dynamics)
Perceptual Effect:
-
High
$L$ (0 dB, near 1.0): Bright, snappy attack; full harmonic spectrum (hard pick attack, forte) -
Medium
$L$ (-20 dB, ~0.1): Softer attack; slightly rolled-off highs (normal playing, mezzo-forte) -
Low
$L$ (-40 to -60 dB, near 0): Very soft, muffled pluck; mostly fundamental (gentle finger pluck, pianissimo) - Musical analogy: Mimics how real strings sound duller when plucked softly vs. brightly when plucked hard
- Dynamic response: Creates velocity-sensitive timbre, not just volume
Basic relationship:
where
With fractional delay (for fine tuning):
where
Perceptual Effect:
-
Without fractional delay (
$\eta = 0$ ): Slight pitch inaccuracies; noticeable detuning on certain notes - With fractional delay: Perfect pitch tracking; all notes sound in-tune across the range
- Musical importance: Essential for realistic instrument simulation; prevents "digital" detuning artifacts
Trigger function:
where
Release envelope:
Creates exponential decay with time constant proportional to period
Physical interpretation: Simulates initial displacement of string at pluck time, with energy proportional to plucking force.
Perceptual Effect:
- Noise burst: Creates the initial "pluck" transient; sounds like the pick or finger hitting the string
- Broader burst: Softer, gentler attack (fingerstyle)
- Narrow burst: Sharp, percussive attack (hard pick, slap)
- White noise source: Provides full-spectrum excitation that the resonant delay line filters into pitched tone
- Without excitation: No sound (like a string that was never plucked)
graph TB
subgraph "EKS Physical Model"
N[White Noise] --> G{Gate?}
G -->|Trigger| E[Excitation<br/>Burst Generator]
E --> S[Smooth<br/>pick_angle]
S --> PP[Pick Position<br/>Comb Filter<br/>β·P delay]
PP --> LF[Level Filter<br/>HF Compensation]
LF --> DL[Delay Line<br/>P-2 samples]
DL --> OUT[Output]
DL --> DF[Damping Filter<br/>ρ · FIR3 LPF]
DF --> FB((+))
LF --> FB
FB --> DL
P[Period P<br/>= SR/freq] -.-> DL
P -.-> PP
T60[Decay T60] -.-> DF
BR[Brightness B] -.-> DF
end
classDef exciteStyle stroke:#f96,stroke-width:3px
classDef delayStyle stroke:#4ecdc4,stroke-width:3px
classDef dampStyle stroke:#95e1d3,stroke-width:3px
classDef outStyle stroke:#ffd93d,stroke-width:3px
class E exciteStyle
class DL delayStyle
class DF dampStyle
class OUT outStyle
graph TB
UI[User Interface] --> NR[note_rate Hz]
UI --> RUN[run_sequencer]
NR --> CLK[Clock Generator<br/>os.lf_imptrain]
RUN --> CLK
CLK -->|Impulse| SEQ[Step Counter<br/>ba.pulse_countup_loop<br/>0-31]
SEQ --> NOTE[Note Lookup<br/>note_at_step]
NOTE --> MIDI[MIDI Note<br/>root + offset]
MIDI --> FREQ[ba.midikey2hz]
CLK -->|Gate| GATE[Gate Signal]
FREQ --> EKS[EKS Synth]
GATE --> EKS
EKS --> OUT[Stereo Audio Output]
classDef uiStyle stroke:#f96,stroke-width:3px
classDef clockStyle stroke:#4ecdc4,stroke-width:3px
classDef noteStyle stroke:#95e1d3,stroke-width:3px
classDef eksStyle stroke:#f38181,stroke-width:3px
classDef outStyle stroke:#ffd93d,stroke-width:3px
class UI,NR,RUN uiStyle
class CLK,SEQ clockStyle
class NOTE,MIDI,FREQ noteStyle
class EKS,GATE eksStyle
class OUT outStyle
graph LR
subgraph "Modulation & Stereo Processing"
LFO[LFO Oscillator<br/>Sine Wave] --> MOD[×]
MR[mod_rate] -.-> LFO
MD[mod_depth] --> MOD
LFO --> MOD
PA[pan_angle<br/>Base Position] --> ADD((+))
MOD --> ADD
ADD --> CLIP[Clip 0-1]
ML[Mono L] --> SP[Stereo Panner]
MR2[Mono R<br/>+width delay] --> SP
CLIP --> SP
SP --> OL[Out L]
SP --> OR[Out R]
end
classDef lfoStyle stroke:#c77dff,stroke-width:3px
classDef panStyle stroke:#4ecdc4,stroke-width:3px
class LFO,MOD lfoStyle
class SP panStyle
| Parameter | Range | Default | Description |
|---|---|---|---|
run_sequencer |
{0,1} | 0 | Start/stop sequencer |
note_rate |
1-30 Hz | 12 Hz | Clock speed (12 Hz = 16th notes at 180 BPM) |
root_note |
MIDI 36-72 | 64 (E3) | Base pitch |
| Parameter | Range | Default | Description | Perceptual Effect |
|---|---|---|---|---|
gain |
0-10 | 1.0 | Output level | Overall volume control |
pick_angle |
0-0.9 | 0.9 | Pick sharpness (higher = brighter attack) | 0.0-0.3: soft/round attack; 0.5-0.7: normal; 0.8-0.9: sharp/percussive |
pick_position |
0.02-0.5 | 0.13 | Pluck position ( |
0.02-0.05: bridge (thin/bright); 0.13: guitar (balanced); 0.25-0.5: neck (mellow/hollow) |
decaytime_T60 |
0-10 s | 1.0 s | String decay time | 0.1-0.5s: muted/staccato; 1-2s: normal guitar; 5-10s: sustained/drone |
brightness |
0-1 | 0.7 | High-frequency content ( |
0.0-0.3: dull/warm; 0.4-0.6: balanced; 0.7-1.0: bright/metallic |
dynamic_level |
-60 to 0 dB | -10 dB | Nyquist-limit level ( |
-60 to -40: soft/muffled; -20 to -10: normal; 0: hard/bright attack |
| Parameter | Range | Default | Description | Perceptual Effect |
|---|---|---|---|---|
spatial_width |
0-1 | 0.5 | Stereo width | 0: mono (centered); 0.5: moderate width; 1.0: wide stereo (immersive) |
pan_angle |
0-1 | 0.5 | Base stereo position (0=L, 0.5=C, 1=R) | 0: hard left; 0.5: center; 1.0: hard right |
mod_rate |
0.01-10 Hz | 0.5 Hz | LFO speed for auto-panning | 0.01-0.1: slow drift; 0.5-2: gentle movement; 5-10: fast tremolo-like |
mod_depth |
0-1 | 0.5 | LFO modulation depth | 0: static position; 0.5: moderate sweep; 1.0: full L-R sweep |
| Parameter | Range | Default | Description | Perceptual Effect |
|---|---|---|---|---|
reverb_mix |
0-1 | 0.3 | Dry/wet balance | 0: completely dry; 0.3: subtle ambience; 0.5-0.7: hall reverb; 1.0: wet cathedral |
- Navigate to https://faustide.grame.fr/
- Copy contents of
eks_guitar_sequencer_final.dsp - Click "Run" to compile
- Enable
run_sequencercheckbox - Adjust
note_rateslider for tempo control
# Compile to C++
faust eks_with_mod.dsp -o eks_with_mod.cpp
# Compile to various targets
faust2jaqt eks_with_mod.dsp # JACK Qt application
faust2alsa eks_with_mod.dsp # ALSA standalone
faust2vst eks_with_mod.dsp # VST plugin
faust2daisy eks_with_mod.dsp # Electrosmith Daisy
faust2bela eks_with_mod.dsp # Bela platformEKS/
├── eks_guitar_sequencer_final.dsp # Main implementation (recommended)
├── eks_with_mod.dsp # Version with modulation + reverb
├── eks_guitar.dsp # Original EKS (fixed syntax)
├── eks_sequencer_test.dsp # Sequencer test version
├── bass_sequencer.dsp # Bass sequencer example
├── experiments/ # Development versions
└── README.md
The 32-step pattern creates cascading arpeggios through E minor pentatonic scale:
Steps 0-15 (main pattern): E, G, B, E+octave, with ascending/descending motion
Steps 16-31 (variation): Introduces A and F# passing tones
Musical context: Inspired by progressive rock keyboard arpeggios (e.g., Rick Wakeman's style in Yes - "Roundabout").
Tempo mapping:
- 6-8 Hz: Slower, deliberate
- 12 Hz: 16th notes at 180 BPM (sweet spot)
- 20-24 Hz: Rapid, virtuosic runs
- Karplus, K., & Strong, A. (1983). "Digital Synthesis of Plucked-String and Drum Timbres". Computer Music Journal, 7(2), 43-55.
- Smith, J. O. (2010). "Physical Audio Signal Processing for Virtual Musical Instruments and Audio Effects". CCRMA, Stanford University. https://ccrma.stanford.edu/~jos/pasp/
- Smith, J. O. (1992). "Physical Modeling Using Digital Waveguides". Computer Music Journal, 16(4), 74-91.
- Välimäki, V., et al. (2006). "Discrete-Time Modeling of Musical Instruments". Reports on Progress in Physics, 69(1), 1-78.
- Orlarey, Y., Fober, D., & Letz, S. (2009). "FAUST: an Efficient Functional Approach to DSP Programming". New Computational Paradigms for Computer Music, Editions Delatour, France.
- Faust Programming Language
This project uses the STK-4.3 license (Synthesis Toolkit), following the original EKS implementation by Julius O. Smith III. The code is free to use for research and educational purposes.
George Redpath (Ziforge)
- GitHub: @Ziforge
- Based on: Julius O. Smith III's EKS algorithm
- Julius O. Smith III - Original Extended Karplus-Strong algorithm and implementation
- CCRMA, Stanford University - Physical audio signal processing research
- Faust Community - DSP programming language and compiler
- STK Project - Synthesis Toolkit framework
@misc{redpath2025eks,
author = {Redpath, George},
title = {Extended Karplus-Strong Guitar Synthesizer},
year = {2025},
publisher = {GitHub},
url = {https://github.com/Ziforge/EKS},
note = {Based on Smith, J.O. (2010) Physical Audio Signal Processing}
}