Original post date: January 2, 2026

About this post

This post goes through the process of converting an input file (a .wav file) to wavetables, and finally playing them back.

I used some older code I had lying around (for example the wavetable interpolation and WebAudio playback logic). With AI-assisted coding (the sequencer was built mostly from prompts), I could get most of the interactive pieces working quickly.

Overall, it has become noticeably easier to prototype something like this compared to just a few years ago.

Wavetable synthesis

MusicAcousticsProgramming

You can try playing around with the steps, or you can instantly try an example where I'm saying "Kai Saksela" by pre-filling it with some example data .

Load a WAV file

A WAV file is basically a header plus PCM sample data. We decode it into a normalized floating-point signal.

Step 1

Load a WAV file

We load a .wav file into memory. Nothing is sent to a server; all processing is done in the browser.

Implementation

Visualize the waveform

This view is mostly for sanity-checking: it helps confirm the file decoded correctly and whether it looks OK.

Step 2

Visualize the waveform

After simple preprocessing (convert the signal to mono, remove silence), plot what the waveform looks like.

Implementation

Waveform

Load a WAV file to see its waveform.

Compute YIN / CMNDF heatmap

There are many ways to assess periodicity of a signal varying in time. The visualization below shows where the likely fundamentals live using the YIN algorithm.

Detecting periodicity

A common way to detect periodicity is the YIN algorithm. We look at the minimum of the squared difference with different delays:

d(τ)=j(xjxj+τ)2d(\tau)=\sum_{j} (x_j-x_{j+\tau})^2

We could just look at the minimum squared difference, but it turns out that doesn't work that well. With the YIN algorithm, we calculate the CMNDF (Cumulative Mean Normalized Difference Function), which divides each value by the running average of all smaller delays (with delay 0 having value 1 by default). This stops short delays from being unfairly favored just because fewer samples contribute to them:

d(τ)=d(τ)1τj=1τd(j)d'(\tau)=\frac{d(\tau)}{\frac{1}{\tau}\sum_{j=1}^{\tau} d(j)}

Step 3

Compute YIN / CMNDF heatmap

We analyze the waveform to estimate the fundamental frequency using the YIN algorithm, computed over short windows of the audio.

Implementation

Potential fundamentals / CMND

YIN window: 0 samples · hop: 0 samples

Load a WAV file to visualize the YIN window.

Cursor slice

Hover the heatmap to update

No slice selected.

This is the YIN cumulative mean normalized difference (CMND) function over time. Lower CMND (more periodic) is shown as yellow; higher CMND is blue.

Pick fundamental + harmonic tracks

For a single time window, you can think of CMNDF as a 1D curve over lag τ. The fundamental period is picked by finding a deep local minimum, and then converting that lag to frequency.

The colored lines are candidate fundamentals; click one to select it. The thick black line is the selected (estimated) fundamental over time.

Step 4

Pick fundamental + harmonic tracks

We extract the fundamental frequency track and other prominent harmonic tracks from the heatmap.

Implementation

Chosen fundamental

YIN window: 0 samples

Load a WAV file to see YIN valley tracks.

First local strong minima are selected. Then the fundamentals expand left/right over time following local "valleys" and merge when they connect. Tracks stop when the valley is no longer clear. Longer and less varying tracks are preferred.

Slice into wavetables

The signal is split into N segments. For each segment, estimate what frequencies matter (fundamental + harmonics), then reconstruct a single-cycle waveform that represents that segment. Resampling the cycle to a fixed length (2048) makes later playback fast and consistent. For the reconstruction step, the fundamental and harmonics are picked from the segment FFT, guided by the average fundamental track.

Step 5

Slice into wavetables

We slice the waveform into individual wavetables using the fundamental track. The signal is divided into as many segments as selected in the dropdown.

Implementation

Wavetables

Load a WAV file to build wavetables.

Each segment’s FFT values are taken at harmonics of the segment's fundamental, then a 2048-sample one-period wavetable is synthesized from the harmonic magnitudes and phases.

Smooth + play back

The main artifact to avoid is a sudden jump when switching tables (a discontinuity creates wideband energy and a click). Smoothing does a gentle crossfade / interpolation so the waveform evolves continuously as playback moves from segment to segment. Instead of interpolating in real time, we precompute intermediate tables between each consecutive wavetable and keep them in memory.

Frequency-domain interpolation

To achieve smooth interpolation between wavetables, the interpolation is done per bin in the frequency domain: for each FFT bin we adjust magnitude and phase linearly, moving phase in the direction of the smallest change.

Frequency-domain interpolation between consecutive wavetables

Step 6

Smooth + play back

We interpolate between wavetables smoothly to avoid discontinuities, then play the result back using WebAudio.

Implementation

Wavetable player

Audio playback is available in the browser.

Playback uses a shared AudioContext and an AudioWorklet for real-time playback.

Presets + sequencer

We define three sound presets (each with its own color), then use a simple 4-beat piano-roll sequencer (16th-note grid) to draw patterns and play them back in a loop.

Implementation

Step 7

Presets + sequencer

Sequencer playback is available in the browser.

Playback uses an AudioWorklet with 5-voice polyphony at 120 BPM.