Click to toggle beats on/off. SPACE to start/stop. C to clear. R and I to randomize beats and instruments
A fork of Drum sequencer by Rubén Medellín <chubas>
xxxxxxxxxx
// Drum sequencer
// For the #WCCChallenge << Scrolling OR Rhythm >> (join the discord! https://discord.gg/S8c7qcjw2b)
// You know I had to do both
// No much time this week, but managed to put together a simple drum sequencer using Tone.js
// Controls:
// - P or spacebar: Play/Pause
// - C: Clear the grid
// - R: Randomize the grid
// - I: Randomize the instruments
//
// Use the OPC sliders to select the instruments
let cw, ch
let synth
let grid
let numCols = 16, numRows = 5
let padSize = 50
let padSpacing = 10
let padX = 10
let padY = 10
let drumKit
let started
let samples = {
'Hi Hat - Open': './808_HH__OP.wav',
'Hi Hat - Closed': './808_HH__CL.wav',
'Kick': './808_KICK.wav',
'Long Kick': './808_LNG_KICK.wav',
'Snare': './808_SNARE.wav',
'Crash Cymbal': './CRASH_CYM.wav',
'Crash Cymbal 1': './CRASH__1.wav',
'Effect Cymbal': './EFEX_CY02_SA.wav',
'Clap 1': './F_CLAP_1.wav',
'Clap 2': './F_CLAP_2.wav',
'Thin Hi Hat': './HH_THIN.wav',
'Thin Hi Hat - Open': './HH_THIN__OP.wav',
'Hip Hi Hat': './HIP_HH_1.wav',
'Hip Kick': './HIP_KICK.wav',
'Hip Low Hi Hat': './HIP_LHH.wav',
'Hip Snare 7': './HIP_SN_7.wav',
'Hip Snare': './HIP_S_SN.wav',
'House Tom': './HOUC_TOM__SA.wav',
'Kick F': './KICK_F.wav',
'Kick Off 1B': './KICK_OF_1B.wav',
'16 Ride': './M16_RIDE.wav',
'MHBB Snare': './MHBB_SN.wav',
'New FX1 Tom': './NEW_FX1TOM.wav',
'Nori Snare 0': './NORI_SN_0.wav',
'Nori Crash A': './NR_CRS_A.wav',
'Nori Hi Hat C A1': './NR_HH_C_A1.wav',
'Nori Hi Hat L A5': './NR_HH_L_A5.wav',
'Nori Splash': './NR_SPLASH.wav',
'Nori Tom F': './NR_TOM_F.wav',
'Nori Tom H': './NR_TOM_H.wav',
'Nori Tom L': './NR_TOM_L.wav',
'Nori Tom M': './NR_TOM_M.wav',
'PW Mix SD02S': './PW_MIX_SD02S.wav',
'P Snare Rim': './P_SN_RIM.wav',
'Reso Cymbal 1': './RESO_CYN_1.wav',
'Reverse Slap': './REV_SLAP.wav',
'St Amb Sn7': './ST_AMBSN7.wav',
'Synth Tom 1': './SY_TOM_1.wav',
'Thin Crash 1': './THIN_CRASH1.wav',
'Thin Hi Hat Foot': './THIN_HH_FT.wav',
'Thin Ride': './THIN_RIDE.wav',
'TT Hi Hat 12 F8': './TT_HH12_F8.wav',
}
OPC.slider('instrument1', 0, 0, Object.values(samples).length - 1, 1)
OPC.slider('instrument2', 1, 0, Object.values(samples).length - 1, 1)
OPC.slider('instrument3', 2, 0, Object.values(samples).length - 1, 1)
OPC.slider('instrument4', 3, 0, Object.values(samples).length - 1, 1)
OPC.slider('instrument5', 4, 0, Object.values(samples).length - 1, 1)
function preload () {
for (let [name, path] of Object.entries(samples)) {
samples[name] = new Tone.Player(path).toDestination()
samples[name].name = name
}
}
let selectedDrums
let playing
function setup() {
cw = (padSize + padSpacing) * numCols + padX
ch = (padSize + padSpacing) * numRows + padY
createCanvas(cw + 250, ch)
textSize(20)
textAlign(LEFT, CENTER)
// Create the grid
grid = []
for (let i = 0; i < numRows; i++) {
let r = []
for (let j = 0; j < numCols; j++) {
r.push({ value: "off" })
}
grid.push(r)
}
}
function draw() {
// Apply change on samples
selectedDrums = [
samples[Object.keys(samples)[floor(instrument1)]],
samples[Object.keys(samples)[floor(instrument2)]],
samples[Object.keys(samples)[floor(instrument3)]],
samples[Object.keys(samples)[floor(instrument4)]],
samples[Object.keys(samples)[floor(instrument5)]],
]
background('#f5f2e3')
// Draw the grid and instrument names
strokeWeight(2)
for (let j = 0; j < numRows; j++) {
for (let i = 0; i < numCols; i++) {
let x = padX + i * (padSize + padSpacing)
let y = padY + j * (padSize + padSpacing)
let pad = grid[j][i]
fill(pad.value === "on" ? "#3caf65" : "white")
stroke(i % 4 === 0 ? "#6C5E45" : '#c7ae82')
rect(x, y, padSize, padSize, 5)
}
fill(0)
noStroke()
text(selectedDrums[j].name, cw + 10, padY + j * (padSize + padSpacing) + padSize / 2)
}
// Parse the beat and draw the line indicator
let [bars, beats, sixteenths] = Tone.Transport.position.split(":").map(parseFloat)
let percentage = (beats * 4 + sixteenths) / 16
stroke("#fe4830")
strokeWeight(4)
line(padX + percentage * (cw - padX), padY, padX + percentage * (cw - padX), ch)
}
// Press P to play or pause
function keyPressed() {
if (key === 'p' || key === ' ') {
if (!playing) {
Tone.Transport.start()
playing = true
} else {
Tone.Transport.stop()
playing = false
}
}
if (key === 'c') {
for (let r of grid) {
for (let pad of r) {
pad.value = "off"
}
}
}
// Randomize values with R
if (key === 'r') {
let threshold = random(0.2, 0.4)
for (let r of grid) {
for (let pad of r) {
pad.value = random() < threshold ? "on" : "off"
}
}
}
// Randomize instrument with I
if (key === 'i') {
instrument1 = floor(random(0, Object.values(samples).length - 1))
instrument2 = floor(random(0, Object.values(samples).length - 1))
instrument3 = floor(random(0, Object.values(samples).length - 1))
instrument4 = floor(random(0, Object.values(samples).length - 1))
instrument5 = floor(random(0, Object.values(samples).length - 1))
}
}
function mousePressed() {
if (!started) {
Tone.context.lookAhead = 0
let repeat = (time) => {
// Obtain the current beat based on the position
let [bars, beats, sixteenths] = Tone.Transport.position.split(":").map(parseFloat)
beat = floor(beats * 4 + sixteenths) % numCols
// for (let row of grid) {
grid.forEach((r, i) => {
let instrument = selectedDrums[i]
let pad = r[beat]
if (pad.value === "on") {
instrument.start()
}
})
}
Tone.Transport.bpm.value = 120
Tone.Transport.scheduleRepeat(repeat, "16n")
started = true
}
// Detect which pad was clicked
let i = floor((mouseX - padX) / (
padSize + padSpacing
))
let j = floor((mouseY - padY) / (
padSize + padSpacing
))
if (i >= 0 && i < numCols && j >= 0 && j < numRows) {
let pad = grid[j][i]
pad.value = pad.value === "on" ? "off" : "on"
}
}