@y11i-3d/step-sequencer
v1.0.0
Published
A lightweight Jotai-based step sequencer library.
Readme
@y11i-3d/step-sequencer
A lightweight Jotai-based step sequencer library.
Demo
https://y11i-3d.github.io/step-sequencer/
Installation
npm install @y11i-3d/step-sequencerUsage
Vanilla
import { createSequencer } from "@y11i-3d/step-sequencer";
import { getDefaultStore } from "jotai/vanilla";
const store = getDefaultStore();
const sequence = createSequencer(
{
bpm: 120,
stepsPerMeasure: 8,
tracks: [
[true, false, true, false, true, false, true, false],
[false, false, true, false, false, false, true, false],
],
},
store,
);
// Subscribe to track triggers
const unsub = store.sub(sequence.atoms.tracks[0].trigger, () => {
console.log(`track 0 triggered at step ${store.get(sequence.atoms.step)}`);
});
// Start the ticker
let lastTime: number;
const tick = (time: number) => {
if (lastTime !== undefined) {
store.set(sequence.atoms.tick, (time - lastTime) / 1000);
}
lastTime = time;
requestAnimationFrame(tick);
};
const rafId = requestAnimationFrame(tick);
// Toggle a note
store.set(sequence.atoms.tracks[0].notes[2].active, false);
// Change BPM
store.set(sequence.atoms.bpm, 140);
// Cleanup
cancelAnimationFrame(rafId);
sequence.dispose();
unsub();React
import { useSequencer } from "@y11i-3d/step-sequencer/react";
import { useEffect } from "react";
import { useStore } from "jotai";
const MyComponent = () => {
const store = useStore();
const sequence = useSequencer({
bpm: 120,
stepsPerMeasure: 8,
tracks: [
[true, false, true, false, true, false, true, false],
[false, false, true, false, false, false, true, false],
],
});
useEffect(() => {
let lastTime: number;
let rafId: number;
const tick = (time: number) => {
if (lastTime !== undefined) {
store.set(sequence.tick, (time - lastTime) / 1000);
}
lastTime = time;
rafId = requestAnimationFrame(tick);
};
rafId = requestAnimationFrame(tick);
const unsub = store.sub(sequence.tracks[0].trigger, () => {
console.log(`track 0 triggered at step ${store.get(sequence.step)}`);
});
return () => {
cancelAnimationFrame(rafId);
unsub();
};
}, [store, sequence]);
// ...
};