Try it in the browser: Pitch and tempo changer. Runs on this package, nothing is uploaded.
Time stretching algorithms — umbrella over @audio/stretch-* atoms.
| Atom | Algorithm | Domain | Quality | CPU | Best for |
|---|---|---|---|---|---|
@audio/stretch-wsola |
WSOLA | time | ★★★★ | low | speech, solo voice |
@audio/stretch-psola |
PSOLA | time | ★★★★ | medium | speech, monophonic instruments |
@audio/stretch-pvoc |
plain phase vocoder | freq | ★★ | medium | educational baseline |
@audio/stretch-pvoc-lock |
phase-locked vocoder | freq | ★★★★ | medium | general music |
@audio/stretch-pghi |
phase-gradient vocoder | freq | ★★★★ | medium | vibrato, glides, chirps |
@audio/stretch-pvsola |
vocoder reset to the waveform | freq+time | ★★★★★ | medium | slowing speech, solo voice |
@audio/stretch-transient |
transient-aware vocoder | freq | ★★★★★ | medium | music with percussion |
@audio/stretch-hybrid |
HPSS hybrid | freq+time | ★★★★ | high | full mixes — drums over tonal |
@audio/stretch-paulstretch |
PaulStretch | freq | — | medium | extreme stretch (ambient, drones) |
@audio/stretch-sms |
Sinusoidal Modeling | sinusoidal | ★★★★ | high | harmonic / tonal material |
For pitch shifting, see the @audio/shift-* family.
Install the umbrella (all atoms):
npm install @audio/stretch
import { transient, wsola } from '@audio/stretch'
let slower = transient(samples, { factor: 2 }) // 2× slower, same pitch
let fast = wsola(samples, { factor: 0.75 }) // 1.33× faster
let write = transient({ factor: 1.5 }) // real-time streaming
write(block1)
write(block2)
write() // → remaining samplesOr install just the atom you need — each is self-contained:
npm install @audio/stretch-transient
import transient from '@audio/stretch-transient'
let out = transient(samples, { factor: 2 })
Float32Arrayin/out; a channel array[L, R](orFloat64Array) is accepted and processed per channel — parity with@audio/shift. Output sizes may be variable — small or empty early chunks are normal in streaming.
Waveform Similarity Overlap-Add, in the segment form of SoundTouch and sox tempo. Segments of the input are laid one hop apart, each crossfaded into the one before, and each read where its head best continues the one before (normalized cross-correlation within ±delta of where the time map puts it). The waveform is copied, not resynthesized, so a voice keeps its glottal pulse shape and its consonants keep their attacks. A phase vocoder keeps each harmonic but not their alignment to one another, which is why speech through it sounds distant. Channels share one search. No FFT.
import wsola from '@audio/stretch-wsola'
wsola(data, { factor: 1.5 })
wsola([left, right], { factor: 0.5, sampleRate: 48000 })| Param | Default | |
|---|---|---|
factor |
1 |
Time stretch ratio, or a function of input seconds |
sampleRate |
44100 |
|
frameSize |
40 ms | Segment |
hopSize |
frameSize · 3/5 |
Output hop; segments cross over the rest (16 ms, one period at 62.5 Hz) |
delta |
8 ms | Search (±), half that period |
Use when: shortening speech, a solo voice or a monophonic instrument; slowing them, pvsola (copying repeats noise and reverberation into a flanger).
Not for: chords and mixes. One alignment cannot fit several pitches; use the frequency-domain methods.
Pitch-Synchronous Overlap-Add. Detects pitch period via autocorrelation, then windows grains at pitch cycle boundaries. Because grains align with the pitch cycle there are no phase discontinuities at overlaps — cleaner than WSOLA for monophonic pitched signals.
import psola from '@audio/stretch-psola'
psola(data, { factor: 1.5 })
psola(data, { factor: 0.75, sampleRate: 48000 })
psola(data, { factor: 2, minFreq: 100, maxFreq: 400 }) // male voice range| Param | Default | |
|---|---|---|
factor |
1 |
Time stretch ratio |
sampleRate |
44100 |
For pitch detection frequency range |
minFreq |
80 |
Lowest expected pitch (Hz) |
maxFreq |
500 |
Highest expected pitch (Hz) |
Use when: Speech, solo vocals, monophonic instruments, factors 0.5–2×.
Not for: Polyphonic material — autocorrelation finds one pitch period so chords get mangled. Extreme ratios (>2×) cause gaps.
Plain phase vocoder. Each bin's phase advances at its instantaneous frequency independently. Magnitudes are preserved but incoherent inter-harmonic phase relationships give complex signals a diffuse, "underwater" quality.
import pvoc from '@audio/stretch-pvoc'
pvoc(data, { factor: 2 })| Param | Default | |
|---|---|---|
factor |
1 |
Time stretch ratio |
frameSize |
2048 |
FFT size (power of 2) |
hopSize |
frameSize/4 |
Hop between frames |
Use when: Educational baseline, simple tonal signals.
Not for: General music — use pvoc-lock or transient.
Phase-locked vocoder (Laroche & Dolson, 1999). After propagating phases, locks non-peak bins to their nearest spectral peak's rotation. Restores harmonic phase coherence, eliminating phasiness.
import pvocLock from '@audio/stretch-pvoc-lock'
pvocLock(data, { factor: 2 })Same options as pvoc.
Use when: General music — tonal/ambient material where transient resets aren't needed.
Not for: Percussive material where attacks matter — use transient.
A phase-locked vocoder whose frames restart from the input's own waveform wherever it fits (Moinet & Dutoit, DAFx 2011). Where a voice is periodic its frames take the input's own phases and keep the shape of its glottal pulses; noise, breath and reverberation stay the vocoder's, so slowing never repeats them (WSOLA's flanger) nor spreads the voice (the vocoder's phasiness).
import pvsola from '@audio/stretch-pvsola'
pvsola(data, { factor: 1.5 })| Param | Default | |
|---|---|---|
factor |
1 |
Time stretch ratio, or a function of input seconds |
sampleRate |
44100 |
|
frameSize |
46 ms | Frame, a power of 2 |
shift |
4.5 ms | Shift searched for a reset (±) |
Use when: slowing speech or a solo voice.
Not for: shortening, where wsola keeps attacks better.
Phase Gradient Heap Integration — "Phase Vocoder Done Right" (Průša & Holighaus, 2017). Synthesis phase is integrated from the analysis phase gradients, visiting bins in magnitude order via a max-heap: no peak picking, no transient heuristics; chirps, vibrato and glides stay coherent by construction.
import pghi from '@audio/stretch-pghi'
pghi(data, { factor: 2 })| Param | Default | |
|---|---|---|
factor |
1 |
Time stretch ratio |
frameSize |
2048 |
FFT size (power of 2) |
hopSize |
frameSize/8 |
Hop — gradient integration wants dense frames |
tolerance |
1e-6 |
Bins below tolerance×max get random phase |
Use when: modulated material — vibrato, glissandi, pitch-unstable sources.
Not for: steady polyphony — pvoc-lock's identity locking reproduces intra-partial phases exactly where gradient integration only approximates them.
Transient-aware phase-locked vocoder (Röbel, 2003). Measures spectral flux between frames; on a sharp onset it resets to the original analysis phase instead of propagating it, preserving attack sharpness on drums and plucks. Implies phase locking.
import transient from '@audio/stretch-transient'
transient(data, { factor: 2 })
transient(data, { factor: 1.5, transientThreshold: 2.0 }) // less sensitive detection| Param | Default | |
|---|---|---|
factor |
1 |
Time stretch ratio |
frameSize |
2048 |
FFT size (power of 2) |
hopSize |
frameSize/4 |
Hop between frames |
transientThreshold |
1.5 |
Spectral flux threshold (higher = fewer resets) |
Use when: The right default for most music — percussion, mixed sources.
Not for: Voice/speech — use psola. Extreme stretch — use paulstretch.
Harmonic/percussive hybrid (Driedger & Müller). Median-filter HPSS splits the spectrogram into two layers; the harmonic layer goes through the phase-locked vocoder, the percussive layer through short-frame OLA — chords stay coherent and attacks stay sharp, where one algorithm must trade one for the other.
import hybrid from '@audio/stretch-hybrid'
hybrid(data, { factor: 2 })| Param | Default | |
|---|---|---|
factor |
1 |
Time stretch ratio |
frameSize |
2048 |
FFT size for HPSS + harmonic path |
percFrame |
512 |
OLA frame for the percussive layer |
harmMedian |
17 |
Median filter across time (frames) |
percMedian |
17 |
Median filter across frequency (bins) |
Use when: full mixes — drums over tonal material.
Cost: separation + two stretches ≈ 4–6× the CPU of pvoc-lock alone.
Extreme time stretching via phase randomization (Nasca, 2006). Preserves magnitudes but replaces all phases with random values, producing smooth, dreamlike textures. Designed for large factors.
import paulstretch from '@audio/stretch-paulstretch'
paulstretch(data, { factor: 8 })
paulstretch(data, { factor: 100, frameSize: 8192 })| Param | Default | |
|---|---|---|
factor |
8 |
Time stretch ratio (best >2×) |
frameSize |
4096 |
FFT size (larger = smoother) |
seed |
0x1f123bb5 |
PRNG seed (deterministic output) |
Use when: Ambient music, sound design, drone generation, 8×–1000× stretch.
Not for: Small ratios (<2×) — sounds washed out. Not for preserving rhythm or transients.
Sinusoidal Modeling Synthesis (Serra 1989, McAulay-Quatieri 1986). Decomposes audio into individually tracked sinusoidal partials and resynthesizes at the new time rate. Each partial's frequency and magnitude are interpolated independently — no phase spreading or bin-by-bin artifacts.
import sms from '@audio/stretch-sms'
sms(data, { factor: 2 })
sms(data, { factor: 0.5, maxTracks: 80 })
sms(data, { factor: 3, frameSize: 4096 })| Param | Default | |
|---|---|---|
factor |
1 |
Time stretch ratio |
frameSize |
2048 |
FFT frame size |
hopSize |
frameSize/4 |
Hop between frames |
maxTracks |
60 |
Max simultaneous sinusoidal tracks |
minMag |
1e-4 |
Peak detection threshold (linear) |
freqDev |
3 |
Max frequency deviation (bins) for track continuation |
residualMix |
1 |
Stochastic residual blended into the sinusoidal output |
Use when: Harmonic / tonal content — instruments, chords, vocals — where the phase vocoder introduces smearing. Default residualMix=1 blends breath, noise, and transient energy alongside the sinusoidal model.
Not for: Noise-dominated material.
Every atom is self-contained (framing/OLA helpers inlined; the phase-locking engine lives in @audio/spectral-pvoc). Output quality is evaluated with @audio/quality:
import { lsd, spectralSim, chordBalance, chordRetention, modulationDepth } from '@audio/quality'lsd (log-spectral distance), spectralSim (cosine similarity), chordBalance, chordRetention, and modulationDepth (AM depth per partial) evaluate algorithm output against a reference.
| Command | What it does |
|---|---|
node scripts/compare.js |
writes compare.html — interactive waveforms, playback, internal-vs-external comparisons |
node scripts/bench.js |
throughput and ×realtime numbers for batch and streaming |
node scripts/diagnose.js |
targeted diagnostics for specific algorithm behaviors |
Demo for a lightweight browser listening matrix.
- fourier-transform — FFT + STFT kernels
- @audio/shift-* — pitch shifting family
- @audio/filter — audio filters
- digital-filter — filter design
- Verhelst, W. & Roelands, M. (1993). "An overlap-add technique based on waveform similarity (WSOLA)." ICASSP.
- Laroche, J. & Dolson, M. (1999). "Improved phase vocoder time-scale modification of audio." IEEE Trans. Speech Audio Processing.
- Röbel, A. (2003). "A new approach to transient processing in the phase vocoder." DAFx.
- Nasca, P. (2006). "PaulStretch — extreme time stretching." paulnasca.com.
- Moulines, E. & Charpentier, F. (1990). "Pitch-synchronous waveform processing techniques for text-to-speech synthesis using diphones." Speech Communication, 9(5-6).
- Driedger, J. & Müller, M. (2016). "A review of time-scale modification of music signals." Applied Sciences, 6(2).
- Serra, X. (1989). "A System for Sound Analysis/Transformation/Synthesis Based on a Deterministic plus Stochastic Decomposition." PhD thesis, Stanford.
- McAulay, R.J. & Quatieri, T.F. (1986). "Speech analysis/synthesis based on a sinusoidal representation." IEEE Trans. ASSP, 34(4).