Echo Chamber

DSP runs in C++ compiled to WebAssembly. Click to load the module and grant microphone access.

Requires echo_chamber.js (built via Emscripten) in the same directory.

← Back to Portfolio

Echo Chamber

C++ WebAssembly · delay · convolution reverb · real-time

Waveform
Idle
-
Delay
Time 250 ms
Feedback 40%
Mix 35%
Reverb
Decay 2.4 s
Pre-delay 15 ms
Mix 30%
Output Gain 100%
Implementation note All DSP runs in echo_chamber.cpp compiled to WebAssembly via Emscripten. The delay line is a power-of-2 RingBuffer with linear interpolation for sub-sample accuracy, the same structure used in EchoPsychFX (JUCE). The reverb is a Schroeder network: 4 parallel comb filters (mutually prime lengths, RT60-derived gain) followed by 2 all-pass diffusors, with a Gardner pre-delay. All parameter changes are smoothed with a one-pole filter to prevent zipper noise. JS handles only audio I/O and UI; it writes samples into the WASM linear memory heap and reads results back per block. Source: github.com/Code4Al1z/echo-chamber

How to Use

  • ▸Initialise: Click the button on the overlay to load the WASM module and grant microphone access
  • ▸Record: Hit Record to capture a few seconds of live mic input through the effects chain
  • ▸Play back: Stop recording, then Play to hear the processed result. Loop keeps it running
  • ▸Adjust live: All sliders update the C++ DSP in real time while audio plays. No zipper noise
  • ▸Toggle sections: The switches on Delay and Reverb bypass each effect independently

Controls Reference

  • ▸Delay Time: 10–1000ms. How far behind the dry signal the echo sits
  • ▸Feedback: 0–95%. How much of the delayed signal feeds back into the delay line. Above ~80% gets dense fast
  • ▸Delay Mix: Wet/dry blend for the delay line only
  • ▸Reverb Decay: 0.1–8s. RT60 of the Schroeder network, how long the tail rings
  • ▸Pre-delay: 0–100ms. Gap before the reverb tail starts, adds sense of room size
  • ▸Reverb Mix: Wet/dry blend for the reverb network independently

Under the Hood

All DSP runs in echo_chamber.cpp compiled to WebAssembly via Emscripten, the JS layer handles only audio I/O and UI. The delay line is a power-of-2 RingBuffer with linear interpolation for sub-sample accuracy, identical in structure to the delay used in EchoPsychFX (JUCE). The reverb is a Schroeder network: 4 parallel comb filters with mutually prime lengths and RT60-derived gain, followed by 2 all-pass diffusors, with a Gardner pre-delay stage. All parameter changes go through a one-pole smoother to eliminate zipper noise on fast sweeps.

JS writes samples into the WASM linear memory heap via Module.HEAPF32, calls EchoChamber_process() in C++, and reads the output buffer back per 128-sample block. The Emscripten SINGLE_FILE build inlines the WASM binary as base64 into the JS glue file, no separate asset fetch, no CORS, no server required. Note: ScriptProcessorNode is deprecated in favour of AudioWorklet, a production build would move the WASM call off the main thread.

C++ / Emscripten
DSP core → WebAssembly
Web Audio API
Mic I/O, ScriptProcessor
MediaRecorder API
Capture processed audio
Schroeder Reverb
4 combs + 2 all-pass
Ring Buffer Delay
Linear interp, sub-sample
Canvas 2D
Live waveform display
Browser Requirements Works on Chrome, Firefox, Edge, and Safari (desktop). Requires HTTPS and microphone permission. The WASM module is embedded inline, no separate download needed beyond echo_chamber.js.