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.