← Back to app

Documentation

Everything you need to know about using, hacking, and understanding Resample-Lab.

Overview

Resample-Lab is a browser-based audio mutation lab. Upload any sound, pick a DSP preset, dial in the amount of chaos, and download a sample pack. The entire processing pipeline runs in a Web Worker using raw Float32Array math — no server, no AI, no WebAudio nodes, no external dependencies.

It was built to scratch a specific creative itch: fast, destructive, interesting sample generation that doesn't require loading a DAW, patching a modular synth, or praying to a model. Every parameter is deterministic. Every effect is a known DSP technique. If you want to know exactly why an output sounds the way it does, the code will tell you.

Local-first: All audio is decoded via AudioContext.decodeAudioData(), processed in a Web Worker, encoded to WAV, and zipped — all in the browser tab. Your files never leave your machine.

Zero AI: No neural networks, no black boxes. Every sample is produced by explicit signal processing: biquad filters, overlap-add time-stretching, granular slicing, convolution, and a dozen other classic techniques.

Chaos-driven: A single 0–1 knob modulates every parameter in the active preset simultaneously, letting you dial in anything from subtle texture to total destruction.

Preset Reference

Every preset follows the same architecture: source → mutation → tape/tone → delay/reverb → finishing rack → output. Chaos maps into 8 lanes per preset (mutation, degradation, space, modulation, instability, finish, stereo, tail), so each preset responds differently to the same chaos value.

Ambient Stretch Lab

5 outputs · tape: cinematic_dark · length: absurd (120s)

WSOLA time-stretch from 8× (clean) to 20× (illegal texture), followed by tape warmth, modulated hall reverb, and finishing rack. Chaos prioritizes space, tail, modulation, and stereo width — not degradation.

OutputChainCharacter
cathedral_bedWSOLA → tape(warm) → modulatedHall → finish(gentle)Lush, cinematic, warm drone
toxic_airResample → reverse → diffusionDelay → darkRoom → tape(warm) → finish(gentle)Smeared reverse wash with diffusion
doom_choir_driftLowpass → tape(cinematic_dark) → convolutionSmear → finish(warm)Heavy, dark, convolution-soaked pad
submerged_padLowpass → tape(sub_heavy) → modulatedHall → finish(gentle)Deep, sub-heavy, underwater
reverse_bloom_longReverse → reverseBloom → tape(warm) → finish(gentle, long tail)Swelling reverse bloom with warm tape

Ghost Reverse Lab

4 outputs · tape: warm · length: long (90s)

Reverse-based effects with reverse delays, diffusion clouds, dark rooms, and metallic textures. Chaos prioritizes space, mutation, and tail extension.

OutputChainCharacter
pre_impact_suckReverse → reverseDelay → darkRoom → tape(warm) → finish(gentle)Short reverse pre-impact with suck
ghost_swell_longResample → reverse → darkRoom → tape(warm) → finish(gentle, long fade)Long swelling ghost reverse
reverse_delay_cloudReverse → diffusionDelay → convolutionSmear → tape(subtle) → finish(gentle)Diffused reverse delay into cloud
haunted_room_tailReverse → dirtyMetallic → tape(degraded) → finish(gentle)Haunted metallic reverse decay

Granular Shards

10 outputs · tape: subtle · length: medium (45s)

Two engine modes: Concatenative shards (slices audio at 4 window sizes: 40–200ms, shuffles with seeded LCG PRNG, per-grain processing) and cloud mode (overlap-add with Hann/Tukey envelopes, random pan, pitch distribution, reverse probability, density/jitter control). Chaos prioritizes mutation, pan spread, and instability.

— Shard mode —

  • stereo_shrapnel_loop — Wide stereo micro-chop with fades
  • crushed_shards — 2–8 bit quantization per grain
  • glitch_bits — Saturated (tanh) grains
  • pitch_cloud — ±4–24 semitones per grain
  • verb_throws — Dark room reverb on each grain
  • stutter_bits — Loop-based stutter repeats with tape

— Cloud mode —

  • particle_cloud — Overlap-add grains, pitch spread, pan drift
  • frozen_texture — Sustained drone from short freeze region
  • granular_delay_swarm — Grains fed into delay feedback network
  • grain_reverb_bloom — Grains with exponential decay wash

Bitrot Dirt

4 outputs · tape: degraded · length: medium (45s)

Musical degradation — not just digital fracture. Tape loss (speed/age-dependent HF rolloff), cassette flutter, metallic reverb, and convolution smear. Chaos strongly prioritizes degradation, instability, and modulation.

OutputChain
rotted_room_loopDownsample → bitcrush → tape(degraded) → darkRoom → finish
cassette_collapseDownsample → tape(destroyed) → dirtyMetallic → finish(degraded)
speaker_cone_tearSoft clip → bandpass → noise → dirtyMetallic → finish(limited)
bitcrushed_tailBitcrush → downsample → convolutionSmear → tape(degraded) → finish

Pitch Wreckage

4 outputs · tape: degraded · length: medium (45s)

Pitch mutation via linear resampling with tape processing and convolution wash. Octave shifts are ±12–24 semitones. Chaos controls mutation intensity and instability.

  • sub_beast_layer — –12 to –24 st → tape(sub_heavy) → convolutionSmear → finish(limited)
  • glass_octave_tail — +12 to +24 st → bandpass → darkRoom → finish(bright)
  • detuned_metal_pair — Dual ±18 st layers → dirtyMetallic → tape(degraded)
  • falling_pitch_smear — Resample down → tape(cinematic_dark) → convolutionSmear → finish(warm)

Loop Extractor

5 outputs · tape: subtle · length: medium (45s)

Heuristic energy analysis finds loop-worthy sections. Sliding-window RMS, boundary correlation, transient scoring, and crossfade smoothing. Each candidate gets multiple finishing treatments.

  • clean_loop — Crossfaded loop, finish(gentle)
  • dirty_room_loop — Tape(subtle) → darkRoom → finish(warm)
  • delayed_loop — Ping-pong delay with filtered feedback
  • ambient_loop — Modulated hall → tape(warm) → finish(warm)
  • one_shot_from_loop — Extracted one-shot with convolution tail

Impact / Riser Mutator

5 outputs · tape: cinematic_dark · length: long (90s)

Cinematic impact design. Filter sweeps, convolution reverb, reverse blooms, metallic hits, and sub-heavy collapses. Chaos amplifies tail length, space, and mutation. All outputs are peak-limited for safety.

  • doom_riser — Reverse → filterSweep → tape(cinematic_dark) → modulatedHall → finish(warm)
  • pressure_drop — –24 to –36 st → tape(sub_heavy) → convolutionSmear → finish(limited)
  • metal_impact — Reverse → dirtyMetallic → tape(degraded) → finish(gentle)
  • reverse_slam — Soft clip → reverseBloom → finish(limited)
  • sub_collapse — –30 st → tape(sub_heavy) → convolutionSmear (damped) → finish(warm, long tail)

Chaos Pack

7 outputs · tape: warm · length: long (90s)

Curated multi-preset mashup. Runs 7 sub-recipes with adjusted chaos, picking the most interesting output from each. Seven flavors of controlled entropy.

  1. cathedral_bed from Ambient Stretch
  2. haunted_room_tail from Ghost Reverse
  3. particle_cloud from Granular Shards
  4. granular_delay_swarm from Granular Shards
  5. dirty_room_loop from Loop Extractor
  6. doom_riser from Impact/Riser
  7. sub_beast_layer from Pitch Wreckage

Chaos Parameter

The chaos knob is a single 0–1 float that simultaneously modulates every parameter in the active preset. It's not a simple "more effect" knob — it shifts the behavior of each DSP transform along a spectrum from subtle to extreme.

LabelValueStretchReverbBitcrushDownsampleDrivePitch Range
Clean0.000.48 bit0.2±4 st
Weird0.3312×0.576 bit0.37±11 st
Broken0.6616×0.734 bit12×0.53±17 st
Illegal Texture1.0020×0.902 bit14×0.70±24 st

Values are approximate — actual parameter ranges depend on the preset. Chaos also affects less obvious parameters: reverb decay time, filter cutoff frequencies, tape wow depth/rate, feedback amounts, noise levels, loop durations, and stereo width.

Length Modes

A length mode controls how long each generated sample can be and how much silence extends the tail. Longer modes produce bigger files and take more processing time. Each preset has a default mode tuned to its character — you can override it for shorter snippets or maximal drone textures.

ModeMax DurationTail ExtendUse Case
Short15sNoneQuick one-shots, tight loops
Medium45s0.3sVersatile default for most material
Long90s0.5sExtended pads, ambient tails
Absurd120s1.0sMaximal drones, large file sizes

Note:"Absurd" mode can produce output files over 20 MB each, and ZIP downloads may be substantial. Chaos also slightly extends tails — higher chaos adds up to 0.5s of extra tail.

DSP Techniques

Every transform is implemented in pure TypeScript using Float32Array buffers. No WebAudio nodes, no WASM, no external DSP libraries. Deterministic, browser-safe, reviewable.

Processing Pipeline

Every preset output flows through a consistent architecture:

source → mutation → tape/tone → delay/reverb → finishing rack → output

1. Mutation    — WSOLA stretch, resample, reverse, bitcrush, etc.
2. Tape/Tone   — DC block, head bump, tape loss (speed/age HF rolloff),
                 tone tilt, wow/flutter, soft saturation
3. Delay/Reverb — Dark room, modulated hall, dirty metallic,
                 reverse bloom, convolution smear, ping-pong delay,
                 diffusion delay, reverse delay, multi-tap
4. Finishing   — Trim silence, DC block, EQ profile, stereo width,
                 soft clip, peak limiter, normalize, fades, tail extend

Finishing Rack

Shared post-processing applied to every output. Order: trim silence → DC block → EQ profile (gentle/warm/bright/ degraded) → stereo width → soft clip → peak limiter → normalize to –1 dBFS → fades (5ms in, 20ms out) → tail extend. The limiter uses tanh-based soft-knee saturation above 85% of ceiling.

Tape Emulation

Implemented from first principles — no GPL code, no CHOWTapeModel port. 6 profiles: subtle, warm, degraded, destroyed, cinematic_dark, sub_heavy. Each profile includes DC blocker (single-pole IIR, R≈0.997), input highpass/ lowpass, head bump (peaking EQ at 45–120 Hz with configurable Q), tape loss (speed/age-dependent HF rolloff via first-order lowpass), tone tilt (dark/neutral/bright/ sub_heavy), optional wow/flutter (delegates to sinusoidal fractional delay modulation), and optional soft saturation. Chaos pushes parameters toward more degradation: lower speed, older age, deeper wow, more drive.

Delay Architecture

5 delay types, all with bounded feedback (≤0.95) and full tail rendering. Mono delay with optional LPF/HPF in the feedback path. Stereo ping-pong with cross-channel feedback. Diffusion delay with cascaded allpass smearing (2–6 stages). Reverse delay that reads the buffer backwards for pre-echo effects. Multi-tap delay with per-tap gain and stereo pan. All delays normalize to 0.95 peak and guarantee finite output.

Reverb Architecture

5 reverb engines with damping, stereo spread, and rendered tails. Dark room: 4-comb + 2-allpass FDN with heavy LPF damping. Modulated hall: sinusoidal delay-read modulation for cloud/bloom effects, extended tail. Dirty metallic: 6 shorter combs with configurable brightness for ringy textures. Reverse bloom: input reversed → FDN → reversed back for swelling pre-effects. Convolution smear: O(n·k) with procedurally generated exponential-decay noise IR (LPF-damped). All reverbs guarantee peak ≤1.0 and finite output.

Granular Synthesis (Two Modes)

Shard mode: Slices at 4 window sizes (40–200ms), shuffles with seeded LCG PRNG, concatenates with per-grain processing (pitch shift, bitcrush, reverb, saturation). Cloud mode: Overlap-add with Hann/Tukey envelopes, random grain position, pitch (±semitone range), pan per grain, reverse probability, density (grains/sec), jitter. Freeze mode sustains a short window via dense overlapping grains. Delay swarm feeds grains into a feedback delay network. Reverb bloom applies exponential decay envelopes for grain-to-wash transitions. All modes are deterministic for a given seed.

WSOLA Time-Stretch

Waveform-Similarity Overlap-Add. 30ms frames with Hann window, hop ratio from stretch factor. Falls back to simple resample at extreme ratios (20×+). Source capped at 60s before stretching.

Biquad Filters

Direct Form II transposed. LP/HP at Q=0.707 (Butterworth); bandpass Q from bandwidth ratio. Cutoff clamped to [20Hz, Nyquist–1Hz]. Coefficients pre-computed, state per-instance.

Schroeder Reverb

Classic 4-comb (31/37/43/53ms) + cascaded allpass (5ms). Feedback gain set by decay. Output normalized to peak 1.0. Used as a lightweight option where more expensive reverbs aren't needed.

Convolution Reverb

O(n·k) direct convolution with exponential-decay noise IR. IR generated procedurally and LPF-damped. Input capped at 5s, IR typically 0.5–4s. Used for transient smear and cinematic wash effects.

Loop Detection

Heuristic sliding-window finder with adaptive step size. Scores on RMS energy, peak-to-RMS ratio, front-loaded energy, tail energy, and boundary correlation. Top non-overlapping candidates selected; fallback to middle section if none pass threshold.

Chaos Lane Mapping

The single 0–1 chaos knob maps into 8 per-preset lanes: mutation, degradation, space, modulation, instability, finish, stereo, tail. Each preset defines its own lane weights. Ambient Stretch prioritizes space and tail; Bitrot Dirt prioritizes degradation and instability; Impact/Riser prioritizes space, tail, and stereo. Chaos is applied as chaos × laneWeight for each lane.

Tape Wow & Flutter

Sinusoidal LFO modulating a fractional delay line. Linear interpolation between samples. Separate from the broader tape emulation module — this is the raw pitch modulation primitive.

Downsample + Bitcrush

Two-stage: lowpass at Nyquist/factor → zero-order-hold decimation, then uniform quantization to N bits (1–16). N ≥ 16 passes through.

Haas Effect

Per-channel random delay (1–12ms) for precedence-effect stereo widening. Randomized per-call. Combined with warm chain (HP20 + LP60 + soft clip + normalize) for the watchyourtemper® character chain.

Technical Specs

Input

  • Formats: WAV, AIFF, FLAC, MP3, M4A, OGG
  • Max files: 8 per pack
  • Max duration: 300 s (5 min) per file
  • Max upload: 50 MB per file

Output

  • Format: 16-bit WAV at source sample rate
  • Max duration: 15–120 s (selectable via Length mode)
  • Normalization: –1 dBFS peak (0.89)
  • Always stereo (mono sources are duplicated)

Processing

  • Thread: single Web Worker
  • Data: Float32Array only (no float64 audio)
  • Decoding: AudioContext.decodeAudioData()
  • All transforms return new arrays (no mutation)

Codebase

  • Framework: Next.js 16 (static export)
  • DSP: ~4,300 lines of pure TypeScript across 8 modules
  • 40+ transform functions
  • 5 new DSP modules: finish, tape, delay, reverb, granular
  • 92 automated DSP tests
  • Zero runtime dependencies for audio

FAQ

Does this upload my audio anywhere?

No. All processing happens in your browser tab. The app has no backend — it's a static site. Your files never leave your computer.

Can I use this offline?

Yes, after the first page load. The entire DSP engine is client-side JavaScript. No API calls are made at runtime.

Why is convolution reverb slow at high chaos?

Convolution uses direct O(n·k) convolution. The impulse response length scales with reverb time (up to 4s at high chaos = ~192k samples). Input is capped to bound the operation count. On a modern laptop this typically takes 2–5 seconds. Consider this a tradeoff for deterministic, dependency-free convolution.

Can I use my own sample rate?

Outputs use the sample rate of the source audio (decoded by the browser). Most browsers output 48 kHz, but the DSP handles any rate transparently.

Why are some outputs skipped?

Every output passes validation (RMS ≥ 10⁻⁷, length ≥ 20 samples, no NaN/Infinity) via validateOutput() before it's included. If the processed audio is silent or corrupted, it's skipped to avoid broken WAV files. The finishing rack also clamps NaN to 0 and limits peaks, so skipped outputs are rare with normal source material.

What is the "watchyourtemper" character chain?

A signature processing chain: Haas-effect stereo widening (random 1–12 ms per-channel delay) followed by the finishing rack (DC block, EQ profile, stereo width, soft clip saturation, limiter, peak normalize, fades). Every output passes through the finishing rack. The earlier warm chain (HP20 → LP60 → soft clip) is still available as finalWarm() but most presets now use the full finishing rack.

Can I contribute a preset?

Yes. See the contributing guide for the recipe API and conventions.

Is this AI?

No. Every sample is produced by explicit, deterministic DSP. No neural networks, no machine learning — just biquad filters, overlap-add, convolution, and other classic signal processing.

Developer Guide

Local Development

git clone https://github.com/achuthanmukundan00/Resample-Lab.git cd Resample-Lab/apps/web pnpm install pnpm dev # → http://localhost:3000

No backend, no database, no environment variables. The dev server starts in seconds.

Production Build

pnpm build # static export → out/ pnpm start # serve locally

Outputs a fully self-contained static site. Deploy to Cloudflare Pages, Vercel, Netlify, S3, or any web server.

Project Structure

apps/web/lib/dsp/    # DSP engine
├── transforms.ts   # 40+ atomic audio transforms
├── presets.ts      # 8 preset recipes + registry
├── finish.ts       # Finishing rack (DC block, EQ, limiter, fades)
├── tape.ts         # Tape emulation (6 profiles, loss, head bump)
├── delay.ts        # Delay effects (mono, ping-pong, diffusion, reverse)
├── reverb.ts       # Reverb engines (dark, hall, metallic, bloom, convolution)
├── granular.ts     # Granular synthesis (cloud, freeze, swarm, bloom)
├── packWorker.ts   # Web Worker entry point
├── wav.ts          # 16-bit WAV encoding
├── zip.ts          # ZIP builder (stored, no compression)
├── constants.ts    # Centralized limits
├── types.ts        # Shared types
└── __tests__/      # 92 DSP tests
    └── dsp.test.ts

Adding a Transform

All transforms live in transforms.ts and follow a consistent signature:

// Input:  Float32Array[] channels, sample rate, params
// Output: New Float32Array[] (never mutate inputs)
export function myEffect(
  channels: Float32Array[],
  sr: number,
  intensity: number,
): Float32Array[] {
  return channels.map((ch) => {
    const out = new Float32Array(ch.length);
    for (let i = 0; i < ch.length; i++) {
      out[i] = ch[i] * intensity; // your processing here
    }
    return out;
  });
}

Running Tests

npx tsx apps/web/lib/dsp/__tests__/dsp.test.ts

114+ tests covering transforms, finishing rack, tape emulation, delays, reverbs, granular engine, stereo/mono compatibility, and audio analysis utilities.

Render-Audit (Listen Test)

Generate every preset × every source file × 3 chaos values × 4 length modes and produce a structured report. Useful for evaluating output quality across source material.

npx tsx scripts/render-dsp-corpus.ts --input ./my-wavs

Output goes to .render-audit/ with a structured folder tree, report.json, and a human-readable report.md.

Recommended source material for listen-testing:

  • Dry drum loop (120 BPM, ~4 bars)
  • Vocal one-shot or short phrase
  • Synth one-shot (piano, pad, brass stab)
  • Melodic loop (bassline or chord progression)
  • Noisy field recording (street ambience, room tone)
  • Full mixed snippet (15–30 s of a mastered track)

Demo Recorder (Playwright)

Automated browser test that exercises the full UI: upload, preset selection, chaos slider, length mode, and generation. Can also record a screencast for the README.

# Install (one-time) pnpm add -D playwright npx playwright install # Start dev server (new terminal) pnpm dev # Smoke test (no video) node scripts/demo-recorder.mjs # Record demo video node scripts/demo-recorder.mjs --record

Requires a test.wav file at the repo root and the dev server running on port 3000. The recorded video is saved to docs/assets/demo.webm.

Architecture Notes

  • Audio decoding uses the browser's native AudioContext.decodeAudioData()
  • All DSP runs in a single Web Worker — no blocking the main thread
  • Progress is reported via worker.postMessage() — the UI renders a real-time progress bar
  • Each preset recipe declares its output count and categories in the registry at the bottom of presets.ts
  • The makeSample() helper handles stereo conversion, validation, sanitization, and peak normalization