# Arpeggiator

> An [arpeggiator](https://en.wikipedia.org/wiki/Synthesizer#Arpeggiator) (arp) is a feature available on several synthesizers that automatically steps through a sequence of notes based on an input chord, thus creating an arpeggio.

Let's go over one of the ways we can achieve a traditional arpeggiator inside Sattern.&#x20;

As we've discussed in the [Patterns](/tutorial/patterns.md) section, Patterns are polyphonic. This means that with every note held the `Pattern.onSequence` callback will be called. For our arpeggiator program we only want to have one "audible" pattern playing a list of held notes at any given time. Let's begin by adding some code to our existing project to keep track of currently held notes.

```javascript
// main.js
// Arpeggiator
//
// Created by Jacob Sologub on 15 Jun 2020.
// Copyright © 2020 Jacob Sologub. All rights reserved.

const sampler = new sattern.Sampler();
sattern.graph.add (sampler);

const sound = new sattern.Sound ("./mcg_mp_064.wav", {
    lowKey: 0, highKey: 127, rootKey: 64
});
sampler.add (sound);

const reverb = new sattern.SOULPatch ("./Reverb.soulpatch");
sattern.graph.add (reverb);
sattern.graph.connect (sampler.output, reverb.input);

const pattern = new sattern.Pattern();
sattern.add (pattern);
pattern.onSequence = function (context) {
    // Intentionally empty.
};

const triggerKey = new sattern.Note ("C4").key;
const currentlyHeldKeys = new Array (127);

sattern.onNoteOn = function (note) {
    if (note.key != triggerKey) {
        currentlyHeldKeys [note.key] = true;
    }
};

sattern.onNoteOff = function (note) {
    currentlyHeldKeys [note.key] = false;
};
```

Sattern has a few global callbacks for monitoring/responding to incoming Controller, PitchWheel, Aftertouch, ChannelPressure and Note on/off messages. As you can see on lines `#28 - 36` I've assigned two callbacks, `sattern.onNoteOn` and `sattern.onNoteOff` to keep track of the currently held keys. When a note on is received, we mark the key index of our `currentlyHeldKeys` array with `true`. When we receive a note off message, we mark the key index of our `currentlyHeldKeys` array with `false`. We've also defined a "trigger key" constant on line `#25` that will be used to determine whether or not the arp should be active. I've chosen "C4" or midi note 72 as my trigger key to enable/disable pattern playback but you can use whatever makes sense for your current setup. The lowest key on your MIDI controller would probably be a better choice, but I'm using (12) "Midi Keyboard" inside the Sattern UI to trigger notes with my computer keyboard.

{% hint style="info" %}
(12) "Midi Keyboard" maps the top two rows of a standard qwerty keyboard to MIDI notes and can be triggered with your keyboard when (12) "Midi Keyboard" has keyboard focus (click on the keyboard component once to grab the keyboard focus). "C4" is the "A" key on the keyboard.
{% endhint %}

Now let's add the actual code to generate the `Step` objects inside our `pattern.onSequence` callback.

```javascript
// main.js
// Arpeggiator
//
// Created by Jacob Sologub on 15 Jun 2020.
// Copyright © 2020 Jacob Sologub. All rights reserved.

const sampler = new sattern.Sampler();
sattern.graph.add (sampler);

const sound = new sattern.Sound ("./mcg_mp_064.wav", {
    lowKey: 0, highKey: 127, rootKey: 64
});
sampler.add (sound);

const reverb = new sattern.SOULPatch ("./Reverb.soulpatch");
sattern.graph.add (reverb);
sattern.graph.connect (sampler.output, reverb.input);

const pattern = new sattern.Pattern();
sattern.add (pattern);
pattern.onSequence = function (context) {
    if (context.note.key == triggerKey) {
        const steps = [];

        for (let i = 0; i < currentlyHeldKeys.length; ++i) {
            if (currentlyHeldKeys [i] == true) {
                const step = new sattern.Step ([sampler, new sattern.Note (i)], 3, 2);
                steps.push (step);
            }
        }

        context.clockMultiplier = 2.0;
        
        if (steps.length) {
            return steps;
        }

        return new sattern.Step ([undefined, new sattern.Note()], 1, 1);
    }
};

const triggerKey = new sattern.Note ("C4").key;
const currentlyHeldKeys = new Array (127);

sattern.onNoteOn = function (note) {
    if (note.key != triggerKey) {
        currentlyHeldKeys [note.key] = true;
    }
};

sattern.onNoteOff = function (note) {
    currentlyHeldKeys [note.key] = false;
};
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.sattern.dev/tutorial/arpeggiator.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
