Skip to main content
Lightning Jar - Web Studio Lightning Jar Wordmark

How Do You Unit-Test Swing?

A watercolor illustration of handwritten sheet music

Swing is the oldest trick in the drum machine: push every other sixteenth note a little late and a stiff grid starts to groove. In Stem Shovel's free drum machine, the rule lives in one small function. On the sixteenth-note grid, the odd steps land late by up to a third of a step; on the eighth-note grid, the off-beat eighths land late by up to a third of an eighth. Full swing is a triplet feel, the top of an MPC's range. The function is pure, it takes a step number and returns a delay in seconds, and it has ordinary unit tests: nothing moves at zero swing, half swing is half the delay, the eighths never move on the sixteenth grid.

So the question in the title sounds answered. It is not, and the reason is the most important thing we learned building audio in the browser.

Correct Math, Silent Speakers

A pure function test proves the rule. It proves nothing about whether the rule reaches the audio. Between drumSwingDelay and your ears sit a scheduler, a voice player, a chain of effect nodes, and the Web Audio clock, and any of them can drop the delay on the floor without raising an error. We wrote about the worst version of this in our Web Audio field report: a wiring comparison once concluded that an empty effects chain needed no wiring at all, every track played silence, and every test passed, because no test asserted on rendered sound.

Swing is especially good at hiding this way. A drum machine with broken swing still plays. It plays confidently, on the grid, sounding exactly like a drum machine, and unless the listener is paying attention to feel, nobody files a bug. The failure mode is not silence; it is stiffness.

Render the Beat, Measure the Hits

So the swing check does not read the code. It plays the instrument and listens.

The script (bun run check:swing) drives a headless Chromium at the dev server, imports the drum machine's own offline renderer, the same step player the live engine uses, and renders a bar of rim shots at 120 bpm: a pattern of straight sixteenths and a pattern of eighths, each rendered with swing off and with swing all the way up, on both swing grids. Then it decodes the WAV it just made and finds the onsets the blunt way: walk the samples, and every time the signal crosses a small threshold after at least a couple thousand samples of quiet, record the time in milliseconds.

What comes back is not a pass/fail boolean from deep in a mock. It is a list of timestamps, the actual moments the drum hit in the rendered audio. The assertions read like a musician checking the chart. At 120 bpm a sixteenth is 125 milliseconds, so with full swing every odd sixteenth should land about 42 milliseconds late, within a 6 millisecond tolerance. The even sixteenths must not move. The eighth notes must not move at all on the sixteenth grid, which is the musically honest detail: a beat with nothing on the odd sixteenths sounds the same at any swing setting, and the check pins that down. Switch to the eighth-note grid and the off-beat eighths should land about 83 milliseconds late while their neighbors stay put. The passing message says it plainly: 1/16 moves the odd sixteenths a third of a step, 1/8 moves the off-beat eighths a third of an eighth, nothing else moves.

The Rule we Keep

Both layers of testing stay. The unit tests pin the arithmetic and run in milliseconds; the render check pins the sound and runs in seconds. What we gave up is the belief that the first layer implies the second. In most domains, code that computes the right value and tests that confirm it are the end of the story. In audio, the value has to survive the trip to the speaker, and the trip is where the bugs live.

The general form of the rule costs one sentence: the only trustworthy claim about audio code is a measurement of its output. Render it, decode it, and check where the hits landed. If that sounds like a lot of machinery for a groove knob on a free drum machine, it is cheaper than the alternative, which is shipping a drum machine that plays confidently, on the grid, and does not groove.

headshot of Kevin Peckham
Kevin Peckham
Principal, Lightning Jar

Ask Eljay

Ask about the studio's work, research, packages, or writing. A few starters:

Answers come from this site's own content and link their sources. For anything that matters, email hello@lightningjar.com.