Skip to content

Add Swing and Human Feel

Swing is measured on a grid you name; nudge and humanize are measured on each event’s own step. Notes keep their length when they move. How Patterns Work explains the model.

.swing(amount, grid) cuts the cycle into grid slots and delays every second one, like the swing on a drum machine. 16 swings 16ths, 8 swings 8ths. Whatever plays on the beat stays, so a whole kit swings as one groove:

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.set_instrument(Sampler(Kit("CR-78")));
drums << [bd*4, _ cp _ cp, hh*16].swing(0.1, 16);
PLAY;

The kicks and claps sit on the grid’s even slots and don’t move; the hats on the odd 16ths play late.

The amount is a fraction of one slot and runs from 0 to 0.5. Start low and raise it by ear; 0.33 puts the delayed slots on the triplet — How Patterns Work explains why. The grid is an even number of slots per cycle, from 2 to 1024.

.nudge(offsets) moves each event by its own amount: 0.0 is on the grid, positive is later, negative is earlier. The offsets are used in order and wrap when there are more events than offsets:

[bd sd bd sd].nudge([0.0, 0.25, 0.0, -0.1]); // snare 1 late, snare 2 early
[bd sd bd sd].nudge([0.0, 0.1]); // [0, 0.1, 0, 0.1]
Offsets: 0.0 +0.25 0.0 -0.1
Original: | bd | sd | bd | sd |
0 0.25 0.5 0.75 1.0
Nudged: | bd | sd | bd | sd |
0 0.3125 0.5 0.725 1.0

.humanize(amount) moves each event by an offset within ±amount of its step. The offsets differ from event to event but are the same on every playback:

[bd sd bd sd].humanize(0.1); // ±10% of a step

Start around 0.05 and raise it until the part loosens without sounding sloppy.

Each method accepts a pattern where it takes an amount, so the groove can alternate:

[hh*8].swing(<0.25 0.33>, 8); // moderate, then triplet
[bd sd bd sd].humanize(<0.05 0.2>); // tight, then loose
[bd sd bd sd].nudge(<[0 0.25 0 -0.1] [0 0 0 0]>); // nudged, then straight

Set the groove first, then add the jitter:

[hh*8].swing(0.25, 8).humanize(0.05);
[bd sd bd sd].nudge([0.0, 0.15, 0.0, -0.08]).humanize(0.05);

If you humanize first, the swing is measured from positions that have already moved, and the groove blurs.

Put the layers on separate tracks and time each one on its own — here straight kicks, swung and humanized hats, and a late snare:

use "std/instruments" { Sampler, Kit };
let kit = Sampler(Kit("CR-78"));
let kicks = AudioTrack("kicks");
kicks.set_instrument(kit);
kicks << [bd*4].humanize(0.05);
let hats = AudioTrack("hats");
hats.set_instrument(kit);
hats << [hh*8].swing(0.2, 8).humanize(0.05);
let snares = AudioTrack("snares");
snares.set_instrument(kit);
snares << [_ sd _ sd].nudge([0.0, 0.1]);
PLAY;