I built a waveform explorer, switch to dark mode and check it out!. It works ok if you're not in dark mode.
It's been a little while since I've thought about Oscillators, and reading the Web Audio API for parts I hadn't used, I saw the PeriodicWaveNode, and decided to build a little tool to give it a try. It involved implementing each of the four basic waveforms using the equations in the specification, and then an averager to let the user blend smoothly between waveforms and hear what the intermediary waveforms sound like. I then added a SUPER naive filter - since these waveforms are without enharmonic overtones, I was able to filter down to just the overtones below some nth overtone specified by the user. In my case, this shows me that you can remove quite a lot of harmonic content from a waveform before it becomes very noticeable that you've done so, but the waveform looks quite different. I was pretty pleased with the result, but disappointed I couldn't figure out how to apply frequency modulation in the frequency domain, which would've been cool.
I've been inspired to go back to my university textbooks!
Things I learned
In my mind, the real and imaginary components of a complex spectrum corresponded directly to the amplitude and phase components of each sine wave in a signal. It seems now to be more complicated than that. I haven't quite figured out how yet, but I'm reading through Musimathics bit by bit to try and get a handle on it.
The first clue that I was wrong about the imaginary component of a complex spectrum equating to sine phase was that the basic waveforms in the Web Audio API specification all specify functions of the imaginary component, and that the real component is 0 in all cases "because the waveforms are odd functions". I'm not honestly sure what an odd function is, but if my assumption about the real component of the complex spectrum containing the amplitude information, I'd be wrong right there, because I'd have expected the resulting waveform to have 0 amplitude.
I suspected that maybe imaginary was amplitude and real was phase - so I added a constant offset to the entire real component - rather than rotating the phase of the resulting waveform as I expected, it added a weird DC offset. Evidently I have more reading to do.
Frequency Domain Filter
I was able to add a panel to let the user directly cut off any harmonics of the resulting waveform after the nth harmonic. This seems not to have caused any phase issues, and it made for a really cool way to see how adding lots of tiny sine waves together produces the angular waveforms that synthesizer fans are familiar with. I've seen the animation before, but it's extra cool to see it happening in my own code.
I implemented several reusable React UI components for synths in my personal website (maybe I'll extract a component library, but for now it's fine). I have a knob, a switch, a transport playback toggle, and a few supporting components. The idea is that where possible they'll use html elements with custom UI, and where that's impossible, will use one of the ARIA specs to be properly accessible. I still have to do that in the knob, but the switch and toggle are both already checkbox elements reskinned. The dropdown is just a CSS wrapped React Select. I found the docs for that to be pretty inscrutible, and because I was using typescript and styled-components simultaneously there was a little weirdness, but it was ok.
It was rather cool to have a knob component in the web. I wasn't able to find an existing open source component I was happy with, so I hope to get around to making sure this is accessible and mobile friendly, at which point I might try to package it up and publish it. It accepts a WebAudio AudioParam directly, which is a nice DX treat.
The tricky bit that I can't seem to figure out is the design. I employ tips like only using predefined spacing, but then I end up not getting proportions right. I haven't done anything for light mode (I still need to build a light mode toggle for this site, or get rid of light mode altogether), and that just feels like designing it twice.
That's what I learned!
Thanks for reading. If you found this post interesting, do let me know!