Week 3 — Additive Oscillator


The Evergreen State College · Summer 2026 · 12 credits
Student: Travis Inskeep · Faculty Sponsor: Jessica Carey

Eidolon · Independent Learning ContractWK·03

Goal: build the second synthesis mode, the additive oscillator, which builds a tone up from a sum of sine partials rather than filtering a rich shape down. Done: an AdditiveOscillator sits on the same Oscillator base as Week 2 and sums up to thirty-two partials from a shared sine table. Their amplitudes are set by a movable window, its position and reach controlled by center and width, its slope by tilt, and the spacing of the partials by stretch. The series is band-limited at the top so it does not alias. The evidence below is rendered straight from that oscillator.

The shared base, revisited

Week 2 built the abstract Oscillator so that every mode could share one clock and one note-handling path, and fill in a single method, processSample, to turn the current phase into one output sample. The additive mode is the promised second subclass. The AdditiveOscillator inherits the base phase accumulator and note handling unchanged and supplies only its own processSample, so it drops into the audio-thread lifecycle already in place from Week 1 with nothing else to wire.

The two modes then diverge in method. The subtractive oscillator starts from one harmonically rich shape and leans on band-limiting to tame it. The additive oscillator starts from silence and adds one sine at a time, so its spectrum is whatever set of partials it chooses to sum. That difference is the reason to build both: they reach a similar range of tones from opposite directions, and the tradeoffs, in cost and in control, are the study of the week.

Summing the partials

Additive synthesis rests on a plain idea from Fourier: any periodic tone is a sum of harmonics, sine waves at integer multiples of the fundamental. Build the sum directly and you build the tone. The AdditiveOscillator carries thirty-two partials, each with its own phase, and reads every one from a single shared quarter-wave SineTable rather than calling a trigonometric function per partial per sample.

Summing many sines raises a level problem: a wide tone stacks far more energy than a single partial, so its peak is much higher. Each block the oscillator computes the running sum of the squared partial amplitudes and divides the output by its square root, a normalization that holds the loudness roughly even as the window opens and closes. The result is soft-clamped to the valid range as a final guard, so no setting can overrun the output.

The window: center and width

The thirty-two partial amplitudes are not thirty-two independent sliders. They are shaped by one movable window, an amplitude envelope drawn across the partial series, so a few controls set the whole spectrum at once. The window is a Gaussian curve. center places its peak on a chosen partial, from the fundamental up to the thirty-second, and width sets how many neighbouring partials it reaches before it falls away. A narrow window near the fundamental gives a nearly sinusoidal tone; a wide window higher up gives a formant-like cluster.

The curve is deliberately asymmetric. Its lower side, toward the fundamental, falls a little more gradually than its upper side, toward the top of the series (the code widens the lower half of the bell and narrows the upper half). The asymmetry keeps a shifted center from sounding thin, since a real resonant body tends to trail energy downward more than upward. It is a small shaping choice, but it is audible when the window sweeps.

Tilt and stretch

Two further controls act on the series as a whole. tilt is a spectral slope, applied as a gain in decibels per octave measured across the partials, so a negative tilt darkens the tone by rolling off the upper partials and a positive one brightens it. It is the additive form of the rolloff curve a subtractive filter would draw, set here at the source instead of after it.

Where tilt changes how loud the partials are, stretch changes where they sit. It nudges each partial's frequency away from an exact integer multiple of the fundamental, so partial n lands slightly above or below n times the pitch. Perfect integer spacing gives a fused, organ-like pitch; a small stretch pulls the partials into an inharmonic relationship and gives the tone the metallic character of a struck string, whose stiffness stretches its overtones the same way.

Band-limiting the series

Additive synthesis has one advantage over the subtractive mode when it comes to aliasing: it never generates a partial it did not ask for, so it can simply decline to sum any partial that would sit above the Nyquist frequency. As the pitch rises, the upper partials cross that line one by one and are dropped from the sum. To keep a partial from appearing abruptly as it crosses, the ones just below the limit are faded down a cosine taper rather than switched off, so the top of the spectrum thins smoothly instead of clicking.

One subtlety matters for the loudness. The oscillator runs oversampled, four times the host rate, and the normalization sum is taken from the full window before any high partial is dropped, not after. Removing a partial that would alias therefore lowers the output level, as it should, without changing the balance of the partials that remain in band. The tone gets quieter toward the top of the keyboard, not brighter or thinner, which is the physically correct behavior.

Partial drift and jitter

Thirty-two mathematically exact sines, locked in phase and pitch, sound static. The oscillator keeps them in motion. Each carries a slow, independent drift in phase and a small, faster jitter in amplitude, both a fraction of a decibel, scaled so the higher partials move a little more than the low ones. This motion is always on. It is not a parameter to be dialled up, but a property of the oscillator, the additive equivalent of the per-voice seeding the noise source used in Week 2.

The motion has an audible consequence at the start of a note. At note-on the partials begin aligned in phase, and the first fraction of a second, before the drift has pulled them apart, carries more high-frequency energy than the settled tone that follows. It resolves on its own as the partials decorrelate. This is documented here as a known behavior of the current build, and refining that opening transient is scheduled work, not a finished result.

Evidence

The assets below are rendered headless from the plugin's own additive oscillator (one voice, no analogue coloration, 48 kHz), so they show the real code path, not a model of it. The spectra are drawn from the rendered audio, not computed from the window formula.

Three magnitude spectra of the additive oscillator on one note, showing the partial series under three window settings: a narrow window at the fundamental, a wide window shifted up to a mid partial, and the same wide window with a negative tilt rolling off the top
One note at 220 Hz, three window settings, rendered from the oscillator. A narrow window at the fundamental (top) leaves a fundamental-dominant tone with only a faint trail of upper partials. A wide window shifted up to the eighth partial (middle) draws a broad, formant-like cluster across the series. Adding a negative tilt (bottom) holds that cluster in place but pulls the upper partials down, the additive rolloff. The low-level detail between the partials is the always-on drift and jitter at work.
Magnitude spectrum of a high 1000 Hz additive note with a wide window: the partials climb the harmonic series and taper off in a shaded band just below 24 kHz, with no inharmonic tones folded into the band below
The band-limiting on a high note (1000 Hz), with a window wide enough to reach toward the top of the range. The partials rise through the harmonic series, and the ones entering the shaded band just below the Nyquist frequency are faded down the cosine taper; none is summed above it. Every peak sits on an exact harmonic, with no inharmonic tones folded into the band below, so the tone does not alias.
The three settings in order: narrow at the fundamental, wide and shifted up, then the same with the top rolled off. Note: this file measures +0.36 dBFS true peak from MP3 codec inter-sample reconstruction; sample-level peak is 0 dBFS and no hard clipping is present (flat factor 0.000000).

Reflection

The synthesis itself was straightforward. Summing sines from a shared table onto the base class from Week 2 was almost mechanical, and the result was immediate: a spectrum I could see and control. The difficulty concentrated in two places. The first was the normalization, which I underestimated. My early tones jumped in loudness every time the window moved, and getting the level to hold even meant thinking carefully about which partials to count in the sum and when, especially at the top of the range where partials are dropping out. The second was restraint. Thirty-two independent amplitudes were a temptation, and my first instinct was to expose all of them. The window, with its handful of controls, is the better instrument, and choosing fewer, more musical controls over raw generality was where the design effort of the week went. The surprise was how much the always-on partial motion mattered. With it switched off the tone is cleaner but duller, and that tradeoff sets up the questions the filter and modulation weeks have to answer.

References

Eidolon · Week 3 of 8 · The Evergreen State College