How to Add Swing to Tone.js Transport

Adding a natural groove or swing to your digital audio projects can make programmed beats sound much more human and musical. This article explains how to configure and control the swing feel using Tone.Transport in Tone.js, covering key properties like swing and swingSubdivision, and providing a practical code example to get your sequences grooving.

Understanding Swing in Tone.js

In music production, “swing” delays the timing of every second note (the off-beat) in a specific grid. This creates a skipping or shuffling rhythm instead of a rigid, straight beat.

In Tone.js, swing is managed globally via Tone.Transport. It affects all scheduled events that rely on the Transport’s timeline—such as Tone.Sequence, Tone.Loop, and Tone.Part—provided they are scheduled using subdivisions that match or divide the swing grid.

Key Transport Properties

To implement swing, you need to configure two primary properties on Tone.Transport:

  1. Tone.Transport.swing: This property takes a normalized value between 0 (no swing, completely straight) and 1 (maximum swing). A value of 0.5 is a common starting point for a classic half-triplet swing feel.
  2. Tone.Transport.swingSubdivision: This defines the grid size that will be swung. It is represented as a subdivision string, such as '8n' (eighth notes) or '16n' (sixteenth notes). The default value is '16n'.

Step-by-Step Implementation

To apply swing to your playback, set up your audio source, configure the Transport’s swing settings, schedule your notes, and start the Transport.

import * as Tone from 'tone';

// 1. Create an instrument
const synth = new Tone.MembraneSynth().toDestination();

// 2. Configure the swing settings on the Transport
Tone.Transport.swing = 0.6;            // Swing amount (0 to 1)
Tone.Transport.swingSubdivision = '8n'; // Apply swing to eighth notes
Tone.Transport.bpm.value = 120;         // Set tempo

// 3. Create a sequence of eighth notes
// The sequence callback receives the precise 'time' parameter
const seq = new Tone.Sequence((time, note) => {
    synth.triggerAttackRelease(note, '8n', time);
}, ['C2', 'E2', 'G2', 'B2'], '8n');

// Start the sequence and the transport
seq.start(0);
Tone.Transport.start();

Crucial Rules for Swing to Work

For the swing effect to be audible in your application, you must follow these rules: