Tone.JS Step Sequencer
Simple Step Sequencer demo using Tone.js
by Paul Leech
HTML
<body>
<div id="sequencer" class="container sequencer">
</div>
<div class="toggle-play">
<button id="play-button" class="play-button">Play</button>
</div>
</body>
CSS
.sequencer {
border-radius: 5px;
width: fit-content;
display: grid;
grid-template-rows: repeat(6, 1fr);
grid-template-columns: fit-content;
margin: auto;
}
.note {
border-radius: 5px;
height: 4em;
width: 4em;
border-style: solid;
border-width: 1px;
border-color: lightgray;
margin: 2px;
outline: none;
}
.sequencer-row {
display: inline-block;
white-space: nowrap;
}
.note-is-active {
background-color: green;
border: 1px solid black;
}
.note-not-active {
background-color: solid grey;
}
.toggle-play {
display: flex;
}
.play-button {
margin: auto;
}
JavaScript
import classNames from "https://cdn.skypack.dev/classnames/bind";
import * as Tone from "https://cdn.skypack.dev/tone";
const makeSynths = (count) => {
// declare array to store synths
const synths = [];
// each synth can only play one note at a time.
// for simplicity, we'll create one synth for each note available
// this allows for easy polyphony (multiple notes playing at the same time)
// I'll be using a one octive F minor pentatonic scale
// so I'll need 6 synths
for (let i = 0; i < count; i++) {
// Documentation for Tone.Synth can be found here:
// https://tonejs.github.io/docs/r13/Synth
// I'm using an oscillator with a square wave and 8 partials
// because I like how it sounds.
//
// You could simply declare new Tone.Synth().toDestination()
//
// This would work just as well, but sound slightly different.
// Demo different oscillator settings here:
// https://tonejs.github.io/examples/oscillator
let synth = new Tone.Synth({
oscillator: {
type: "square8"
}
}).toDestination();
synths.push(synth);
}
return synths;
};
const makeGrid = (notes) => {
// our "notation" will consist of an array with 6 sub arrays
// each sub array corresponds to one row in our sequencer grid
// parent array to hold each row subarray
const rows = [];
for (const note of notes) {
// declare the subarray
const row = [];
// each subarray contains multiple objects that have an assigned note
// and a boolean to flag whether they are "activated"
// each element in the subarray corresponds to one eigth note
for (let i = 0; i < 8; i++) {
row.push({
note: note,
isActive: false
});
}
rows.push(row);
}
// we now have 6 rows each containing 16 eighth notes
return rows;
};
const synths = makeSynths(6);
// declaring the notes for each row
const notes = ["F4", "Eb4", "C4", "Bb3", "Ab3", "F3"];
let grid =...