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 =...