Implementing Random Probability in Tone.js Note Triggers
Adding randomness to your web audio projects creates more dynamic, generative, and organic musical patterns. This article explains how to implement a probability factor for note triggers in Tone.js using JavaScript’s conditional logic within scheduling blocks, allowing you to control the exact likelihood of a note playing.
The Core Concept of Probability
To implement random chance in Tone.js, you compare a threshold value
(the probability) against a randomly generated decimal between 0 and 1
using Math.random().
If the generated number is lower than your target probability, the note triggers. Otherwise, the step is skipped.
- 1.0 Probability: 100% chance (always plays)
- 0.5 Probability: 50% chance (plays half the time)
- 0.1 Probability: 10% chance (rarely plays)
- 0.0 Probability: 0% chance (never plays)
Method 1: Global Probability in a Sequence
If you want a uniform probability applied to every note in a
sequence, you can place a simple conditional check inside the
Tone.Sequence callback function.
// Initialize a synth and connect it to the main output
const synth = new Tone.Synth().toDestination();
// Set a global probability (e.g., 70% chance)
const playProbability = 0.7;
// Create a sequence of notes
const sequence = new Tone.Sequence((time, note) => {
// Generate a random number and compare it to the probability
if (Math.random() < playProbability) {
synth.triggerAttackRelease(note, "8n", time);
}
}, ["C4", "E4", "G4", "B4"], "8n");
// Start the sequence and transport
sequence.start(0);
Tone.Transport.start();Method 2: Per-Note Probability
For more complex compositions, you might want certain notes to trigger more often than others (such as a strong downbeat vs. a weak syncopated note). You can achieve this by passing objects containing both the pitch and the specific probability value into the sequence.
const synth = new Tone.Synth().toDestination();
// Define steps with individual note names and playback probabilities
const pattern = [
{ note: "C4", probability: 1.0 }, // Always plays on the downbeat
{ note: "E4", probability: 0.4 }, // Occasional accent
{ note: "G4", probability: 0.8 }, // Highly likely to play
{ note: "B4", probability: 0.2 } // Rare variation
];
const sequence = new Tone.Sequence((time, step) => {
if (step && Math.random() < step.probability) {
synth.triggerAttackRelease(step.note, "8n", time);
}
}, pattern, "8n");
sequence.start(0);
Tone.Transport.start();Method 3: Randomizing Note Selection (Weighted Random)
Instead of choosing whether or not to play a single note, you can use probability to choose which note plays from an array of options.
const synth = new Tone.Synth().toDestination();
const chords = ["C4", "E4", "G4"];
const occasionalNote = "B4";
const loop = new Tone.Loop((time) => {
// 80% chance to play a standard chord note, 20% chance to play the accent note
const chosenNote = Math.random() < 0.8
? chords[Math.floor(Math.random() * chords.length)]
: occasionalNote;
synth.triggerAttackRelease(chosenNote, "8n", time);
}, "4n");
loop.start(0);
Tone.Transport.start();