The robots in Stray made me want to build a voice with a life of its own. SqBell is my answer in a Korg minilogue xd user oscillator: a movable Gaussian spectrum assembled with fast square-wave arithmetic.
The shape has a center, a width and a density. Pitch moves the center. SHAPE changes the width in hertz. Density changes how many frequency components fill it. I wanted those three decisions to remain separate, so I could play the sound instead of negotiating with its implementation.
By Brian Greenforest. Free to use and modify under the MIT license; KORG’s BSD notice remains with SDK-derived material. The compiled download is for minilogue xd.
Hear density change while width stays put
Four three-second sections: Density 16, 32, 64, 96. Pitch is A4 and Gaussian width is about 400 Hz throughout. Each section has two plucks from an external amplitude envelope. Levels are matched so the comparison emphasizes the character of the oscillator. This is a 48 kHz rendering of the compiled WASM implementation through the XD SDK callbacks, with no effects. Download the 24-bit WAV.
A robot voice, and a wonderfully simple Fourier page
Stray was the creative spark: robots that feel like characters, with electronic voices that suggest intention and emotion. I wanted to explore that territory with synthesis I could understand and control. The game’s sound designers discuss their work in this interview with Yann van der Cruyssen and Raphaël Monnin. SqBell is my own construction inspired by that world.
Michael J. Ruiz’s Fourier Synthesizer 16 was another important reference. It lets you set the amplitudes and phases of sixteen harmonics and hear their sum. That direct relationship between a few numbers, a visible shape and a sound is exactly what makes Fourier synthesis inviting. It led me toward a specific question: how cheaply could I build a useful bell-shaped spectrum inside an instrument?
Pulsar synthesis: Curtis Roads and Hieroglyphic Plume
Beyond Fourier synthesis, the only existing method I had discovered for the kind of pulse-based sound I was pursuing was pulsar synthesis. My reference is Curtis Roads’s Sound Composition with Pulsars, published in the Journal of the Audio Engineering Society, volume 49, number 3, pages 134–147, in March 2001. Roads developed the PulsarGenerator software with Alberto de Campo.
A pulsar combines a short waveform, the pulsaret, with a silent interval. Repetition sets the pulse train’s rhythm or pitch; the pulsaret’s duration and waveform shape its formant structure. An amplitude window shapes each pulsaret. Roads explicitly includes a Gaussian window in Figure 3(b), on page 136, and gives its mathematical form in Appendix A.6, on page 146. Gaussian pulses are one possibility within his broader method.
The Eurorack instrument connected to this paper is Plume by Hieroglyphic. Its manufacturer directly credits Roads’s paper. Plume brings the method to an 18 HP stereo module with formant and cluster controls, wavefolding, pulse masking and variable window shapes. Its user guide, page 5, lists Gaussian and triangular windows as distinct choices among seven amplitude contours. The method is Roads’s; this Eurorack implementation is Hieroglyphic’s.
What I disliked in the examples I encountered was the pointy, tiny-triangle character of the pulses. I wanted smooth Gaussian pulses with rounded peaks and smoothly diminishing tails. That preference led me toward building the shape directly through Fourier synthesis. SqBell puts the Gaussian into the component amplitudes; their coherent sum creates the corresponding periodic time structure. Square-wave arithmetic makes that construction practical inside the minilogue xd, while SHAPE keeps the Gaussian width directly playable.
I want Gaussians, not tiny triangles
A pointy little triangle is not a Gaussian just because both rise and fall. A triangle has straight sides, a sharp tip and a finite edge. A Gaussian has a smoothly rounded peak and exponentially diminishing tails. Compress either into a few pixels or a handful of prominent partials and the distinction becomes easy to lose. I want the shape to be a mathematical decision, not a flattering label on a tiny drawing.
A separate Eurorack comparison is Auza Wave Packets. Its manual describes a contour assembled from linear segments between time stages, then combined with an oscillator. That construction can make angular, triangular-sided packets. A wave packet is a broader idea than a Gaussian packet: those little straight-sided shapes are useful, but they are not the rounded Gaussian I wanted to synthesize. Auza documents its linear construction explicitly.
There are real Gaussian techniques already in synthesis. Paul Nasca’s PADsynth, documented in Csound’s GENpadsynth, spreads partials across spectral bins using a selected profile, including a Gaussian, then uses an inverse Fourier transform. SuperCollider’s LFGauss makes a Gaussian-shaped time-domain envelope. In Eurorack, the Expert Sleepers disting mk4 Pulsar VCO offers a Gaussian window for its pulsarets. Magnetic Freak’s GAUSSIAN uses the word for a random distribution.
Those are different objects: a spectral profile, a grain envelope and a probability distribution. For SqBell I wanted the bell in the amplitude spectrum, with its width under direct control. Each intended component receives the actual Gaussian weight:
a(k) = exp(−½ · (offset(k) / sigma)²)
Here sigma is the width of the amplitude spectrum. At an offset of one sigma the amplitude is about 0.607 of the peak; at two it is about 0.135; at four it is about 0.000335. That puts a rounded center and long, quiet shoulders into the coefficients themselves.

What the Fourier sum is doing
The ideal reference is a sum of frequency components around the played center frequency:
s(t) = Σ a(k) · cos(2π · (fc + offset(k)) · t + phase(k))
For N components, set M = floor(N/2) and spacing to 4·sigma/M. Even counts use offsets from −M through M−1; odd counts use −M through M. Multiplying those indices by the spacing places the components across approximately eight standard deviations.
With coherent phase, a Gaussian frequency profile and a Gaussian time envelope are Fourier partners. A finite, evenly spaced set of components gives a periodic version of that idea. Widening the frequency profile concentrates its time structure; closer frequency spacing changes its repetition. The played carrier moves that structure into the desired pitch region.
The real-valued output also has the mirrored negative-frequency spectrum. A very broad profile at a low pitch can overlap that mirror. SqBell keeps the requested width instead of silently narrowing it to fit the note. Density remains a count of intended components; it does not turn a finite line spectrum into a continuous spectrum.
The fast part: weighted square waves
I made the inner loop cheap by replacing a bank of per-component sine evaluations with square-wave sign decisions. A square wave is straightforward to generate from a phase accumulator: choose a sign from the phase bits. Weight and phase-shift several square waves, and their unwanted harmonics cancel each other.
Each quadrature in v0.5 comes from seven weighted square branches, shifted in steps of π/8. The ideal mixer can be written:
m(θ) = sin(π/16) · Σ cos(kπ/8) · sign(sin(θ + kπ/8))
k = −3 … 3
The sin inside sign describes a square wave mathematically; the runtime obtains that sign from phase bits. The branch weights are computed ahead of time. Their sum has sixteen phase regions and only four distinct positive levels, with the other levels supplied by symmetry.
I reduce those seven branches algebraically into four combined levels and pack them into two coefficient words per component. At runtime, bit folding selects a level and its sign. Gaussian amplitude weighting is already present in those coefficients. There is no Gaussian filter after the sum and no dense sampled sine-wave table hiding behind it.
Conjugate pairs share work. The component phases are integer multiples of one shared DDS phase accumulator. One recursive complex carrier rotation per output sample translates the result to the played note; its trigonometric coefficients are approximated when the pitch changes. This is where the speed comes from: simple phase arithmetic and coefficient selection do the repeated work.
In the ideal coefficient limit, the sixteen-phase mixer cancels odd harmonics 3 through 13. Its first residual harmonic families are 15 and 17. The real implementation also has coefficient rounding and discrete-time aliasing, which the included spectral comparisons make visible.
Three controls with separate jobs
| Control | What it changes |
|---|---|
| Played note | The intended spectral center, fc. |
| SHAPE | Gaussian amplitude-spectrum width: sigma = 2 + 2398·SHAPE³ Hz, with SHAPE normalized to 0–1. |
| Density | The actual intended component count, from 16 through 96. |
At sigma = 100 Hz, Density 16 gives 50 Hz spacing; 32 gives 25 Hz; 64 gives 12.5 Hz; 96 gives about 8.33 Hz. The width stays fixed. More components fill the same bell more densely, and more of those components sit near its audible center.
Density changes use a brief fade down, a coefficient-bank switch and a fade back up, roughly 2.7–4 ms in total. That avoids an abrupt switch without paying to run two banks at once.
What v0.5 delivers
The ARM ELF occupies 12,292 bytes of text, data and BSS combined. Measured executed instruction counts per output sample are approximately 239 at Density 16, 351 at 32, 575 at 64 and 799 at 96. These are emulator instruction counts, not processor cycles or a benchmark against a sine implementation.
At A5 and sigma about 100 Hz, the error power relative to an independent ideal Gaussian sum improved from −14.22 dB in SqBell16 v0.4 to −20.32 dB in v0.5 at the same sixteen-component count: about 6.1 dB less error. Higher Density means closer lines; it is a separate choice from harmonic rejection.
The earlier build plays for three seconds, followed by one second of silence and three seconds of v0.5. The sixteen-component count, pitch, width and external pluck envelope match; RMS is matched. 24-bit WAV.

Checks cover all 81 Density settings, changes during held notes, pitch/width independence, retriggers, block sizes and modulation. Five ARM/WASM comparisons agree within 7.2 × 10−7 full scale. The v0.5 listening examples and checks use software and ARM emulation; start at Density 32 when trying the instrument, since high-density polyphonic timing still needs an on-instrument check.
Load it into your minilogue xd
- Download sqbell-v0.5.mnlgxdunit. Use the Korg minilogue xd Sound Librarian to transfer it into an available USER oscillator slot.
- Select the USER oscillator and choose SqBell with TYPE. Turn SHAPE to explore the spectral width.
- Enter PROGRAM EDIT, press button 10 (MULTI ENGINE), and select parameter 1, Density. Choose 32 to begin. The full displayed range is 16–96.
- Save the program with WRITE to preserve the setting. Saved programs can restore their own Density value, so check the display when comparing sounds.
The Korg owner’s manual documents the user-oscillator parameter menu. This download is an oscillator unit; the instrument’s envelope, filter and effects are available for shaping your own programs around it.
The MIT source release includes the C++ oscillator, coefficient generator, build scripts, compiled unit, WASM, ELF, measurement scripts, reports and WAVs. Its build instructions identify the SDK layout, dependencies and pinned commit. Build hashes identify the exact published v0.5 source and binaries.
If you enjoy getting a large behavior from a handful of operations, try Chaos 01 next. Its ten-module feedback core uses stock VCV mixers and VCAs to make a drone evolve. SqBell uses phase bits and weighted signs to make a spectral shape playable. Both are open invitations to take the mechanism apart and make it your own.
Build on the source, play the oscillator, and send me what you make.