To be honest, this post is largely an excuse to experiment with having multiple canvases on the same page, as well as loading WASM code from inside an AudioWorkletProcessor.
In drawsynth, I presented the ability to allow a user to draw the waveform for a synthesizer. It worked by storing an output buffer in a C array which was copied every 128-sample frame in an AudioWorkletProcessor.
I like this approach for its speed and flexibility. Using a raw array buffer from C allows literally any digital signals processing technique to be used at a performance about as high as is possible on the web.
The solution so far has one important drawback: it cannot be played at a different pitch. This is because the output buffer is a fixed 128-sample frame. It is impossible to play a tone at a different fundamental frequency1 because the same waveform is always played at the same rate.
Consider the following sine wave:
It is easy to imagine the wave oscillating on into the future beyond the right hand side of the box, but this is not what is happening. If we draw two samples, we see that the sample is just duplicated:
That strange tooth in the middle is the problem. It's even easier to see the effects of this problem when we adjust the frequency (hover to animate).
The solution to this problem is for the oscillator defined in C to offset the phase with the appropriate amount every frame.
1: Technically higher f0 is possible, but only at whole multiples of (sample_rate) / (frame_size)