← Back to projects

Audio

Arpeggiator

A real-time arpeggiator and synthesizer in the browser, built only with the Web Audio API, with no libraries and no audio files.

Audio off

Choose the notes

Every key previews its note and adds it to or removes it from the arpeggio.

Keyboard octave

C4–C5

Now playing

—

Selected notes: C4 · D♯4 · G4

Generated sequence · 6 steps

  1. C4
  2. D♯4
  3. G4
  4. C5
  5. D♯5
  6. G5
Keyboard shortcuts (while the instrument is on screen)
  • A–K add or remove notes
  • Z X keyboard octave
  • C V transpose (−12, −7, 0, +7, +12 semitones)
  • Space play or stop (anywhere in the instrument)
  • B build-up

Live signal

Waveform (time domain)
Synthesizer output
Normalized spectrum (logarithmic frequency axis)
Spectrum Filter cutoff: 2.8 kHz

Turn on audio and play to see the signal.

Arpeggiator

120 BPM

120 BPM
68%
12%

Build-up

Automation that speeds up the notes, opens the filter and adds saturation. Press again at the drop to reset.

Synthesizer

Oscillator
2.8 kHz
4.0
18%
0.010 s
0.18 s
24%
35%
8 cents

How an arpeggiator works

An arpeggiator doesn't invent harmony. It takes the notes you choose, arranges them into a pattern and schedules each sound at the right moment. The path has four stages:

  1. Note input: clicks on the on-screen keyboard or computer keys.
  2. Pattern builder: up, down, random, across several octaves.
  3. Audio scheduler: notes are booked ahead of time on the audio clock.
  4. Synth voice: an oscillator, an envelope and a filter produce each note.

1. Notes become numbers

Each piano key is represented by a MIDI note number. Middle C is 60, C sharp is 61, D is 62, and so on. That turns transposition and octave copies into simple arithmetic: one octave up means adding 12.

C4=60,C5=72

2. The MIDI number becomes a frequency

The oscillator needs a frequency in hertz, not a note name. In equal temperament, each semitone multiplies the frequency by 21/12. Using A4 (MIDI 69) at 440 Hz as the reference:

f=440⋅2(n−69)/12

For example, middle C (n=60) sits at 440⋅2−9/12≈261.6 Hz.

3. The chord becomes a sequence

In the up pattern, the notes are sorted from lowest to highest, and extra octaves are created by adding 12 semitones. The sequence plays exactly the notes you picked, with nothing added: a one-octave Cmaj7 plays C, E, G, B. The other patterns reverse, mirror or shuffle that list.

The random pattern is shuffled only when the notes, the pattern, the range or the transposition change. Moving the tempo or the sound controls never reshuffles it.

4. The tempo sets the clock

Tempo in BPM says how many beats fit in a minute, and the rate says what fraction of a beat each step takes:

step=60BPM⋅rate

At 120 BPM, a beat lasts half a second. A sixteenth note (1/16) is a quarter of that, so a new step starts every 125 ms.

Swing lengthens the first step of each pair and shortens the second by the same amount, so every second note lands slightly late. With swing s (from 0 to 0.6):

stepeven=step⋅(1+s2)stepodd=step⋅(1−s2)

Pairs are counted from the total number of steps since playback started, not from the position in the sequence. That keeps the groove alternating even when the sequence has an odd number of notes.

5. Why not play the notes with setInterval?

JavaScript timers can be delayed by rendering, scrolling or other work on the page. A 20 ms delay is already audible in a sixteenth-note rhythm. So the project separates deciding from playing:

  • a short loop wakes up every 25 ms and books the notes that fall within the next 100 ms;
  • each note is scheduled on the Web Audio clock, which runs outside the page and plays at the exact time;
  • the screen follows the same clock: the highlighted note changes in an animation loop that reads the queue of scheduled notes.
js
function scheduler() {
  while (nextNoteTime < audioContext.currentTime + 0.1) {
    const midi = sequence[step % sequence.length]
    playVoice(midi, nextNoteTime, gateTime)
    nextNoteTime += stepDuration(step)
    step++
  }
}

setInterval(scheduler, 25)

Schedule first, play later. JavaScript only needs to wake up before the 100 ms window runs out. Even if the loop is a few milliseconds late, the notes are already booked on the audio clock and come out on time.

6. The synthesizer creates the sound

For every step, the browser creates a new voice and discards it when the sound ends:

  1. Two oscillators generate the wave (sine, triangle, square or sawtooth). The second one is slightly detuned, which widens the sound.
  2. The envelope shapes the volume over the note: it rises during the attack, dips a little, holds while the note lasts and fades during the release.
  3. The low-pass filter removes frequencies above the cutoff, and resonance boosts the region around it.
  4. Saturation and echo add color and space before the compressor and the speakers.

With a very short gate, a note can end before its attack is over. In that case each envelope stage is clamped to the end of the note, so the volume automation never goes back in time.

7. A build-up is parameter automation

A build-up (also called a riser) gradually shortens the time between notes. At the same time, it opens the filter and adds saturation. With progress p going from 0 to 1 and a final speed v:

step(p)=stepvp

Because it divides by a power, the acceleration is exponential: with v=4, halfway through the steps are already twice as short. The filter cutoff rises the same way, up to about 12 kHz.

8. Saturation bends the waveform

A WaveShaperNode runs each sample through a nonlinear curve. Here the curve is the hyperbolic tangent, normalized so that x=±1 still maps to ±1:

y=tanh⁡(ax)tanh⁡(a),a=1+drive8

The more drive, the more the curve flattens the peaks of the wave, and that flattening creates new harmonics. The mix control blends the clean and saturated paths (with cosine and sine gains, so the perceived loudness stays the same), so the sound gains density without having to turn harsh.

9. The spectrum shows the filter at work

An AnalyserNode applies the fast Fourier transform (FFT) to the final output. The horizontal axis is logarithmic, like the spacing of musical octaves: each frequency f is drawn at

x(f)=log⁡(f/fmin⁡)log⁡(fmax⁡/fmin⁡)

Each frame is normalized so the strongest component sits at 0 dB. That makes it easy to compare the spectral shape and see the filter cutoff: move the cutoff and the harmonics to the right of the marker disappear.

What each control changes

Change one parameter at a time and listen to the difference.

Timing and movement

  • Pattern: the order of the notes (up, down, up and down, chosen order or random).
  • Rate: the rhythmic length of each step.
  • Tempo: the overall speed, in BPM.
  • Gate: the fraction of the step during which the note keeps sounding.
  • Swing: the delay applied to every second step.

Notes and pitch

  • Keyboard octave: chooses the notes assigned to the keys A to K.
  • Range: copies the selected notes one or two octaves above.
  • Transpose: shifts the whole sequence while keeping the chosen intervals.
  • Chord presets: select the notes of a chord at once; the arpeggiator still plays them one at a time.

Envelope and filter

  • Attack: how quickly the sound reaches its peak after a note starts.
  • Release: how long the sound takes to fade after the note ends.
  • Filter cutoff: the highest frequency the low-pass filter lets through.
  • Resonance: a boost around the cutoff frequency.

Tone, space and automation

  • Oscillator: the waveform and harmonic character of the sound.
  • Echo: tempo-synced repeats mixed behind the note.
  • Saturation drive: how hard the signal is pushed into the nonlinear curve.
  • Saturation mix: the balance between the clean and saturated paths.
  • Analog detune: the small pitch difference between the oscillators that adds width and movement.
  • Build-up: the ramp that raises speed, cutoff and saturation until you reset it.

Everything happens in the browser

  • No audio files: every sound is generated in real time by oscillators.
  • No audio libraries: the engine uses only the Web Audio API.
  • No data upload: your notes and audio stay on your device.

© 2026 Giuliano Geiss Arnhold. All rights reserved.

Privacy

giuliano.codes