Building a sound engine from scratch is a rite of passage. You want to understand the clock. The timing. The raw logic that turns silence into rhythm. A sequencer is that logic. It’s a grid. You place notes. You move forward.
Most tutorials skip the hard part. They show you a library. They show you a GUI. They don’t show you how to handle the drift. The timing errors that creep in when you’re manually tracking time.
Let’s fix that.
We are building a basic step sequencer. It will run on a simple loop. It will trigger sounds. It will keep time without looking ahead. This approach prevents the “choppy” feel you get when using setTimeout for long loops.
The Core Concept: Step-Based Timing
A sequencer works on steps. Not seconds. Steps.
If your tempo is 120 BPM, each beat is 0.5 seconds. If you have 16 steps, each step is 0.125 seconds. The magic happens when you lock to these intervals.
“Time is not a continuous flow in a sequencer. It is a series of discrete events. Syncing to these events is everything.”
We need a way to calculate when each step happens. Not by guessing. By math.
Setting Up the Clock
You need a reference point. The start time.
In JavaScript, Date.now() or performance.now() gives you milliseconds. You use this to calculate drift.
- Define the tempo.
- Calculate step duration.
- Start a loop.
Here is the skeleton. It’s not pretty. It works.
This code is basic. It uses setTimeout. It’s not perfect. But it’s a start.
Why This Matters
Why build it yourself?
Libraries like Tone.js or Web Audio API wrappers are great. They handle the complex scheduling. They compensate for latency.
But you don’t learn how they work by using them. You learn by seeing where they fail.
When setTimeout drifts, your beat wobbles. It sounds human. It sounds wrong.
Handling the Drift
The if (nextStepTime < Date.now()) check is critical.
It























