Skip to content

Sequencer

The Sequencer component outputs a stepped CV sequence and optional gate/accent pulses. It advances on an external clock input, making it phase-locked to the rest of the patch.

Props

PropTypeDefaultDescription
outputModStreamRefRequiredCV output for step values
gateOutputModStreamRef-Optional gate output per step
accentOutputModStreamRef-Optional accent CV output per step
clockModStreamRef-Clock input (required to advance steps)
resetModStreamRef-Optional reset pulse input
labelstring'sequencer'Label for metadata
numStepsnumber8Default number of steps
stepsStep[]Array(numSteps)Controlled step data
onStepsChange(steps: Step[]) => void-Callback when steps change
divisionnumber4Clock division (1/4, 1/8, 1/16, etc.)
onDivisionChange(division: number) => void-Callback when division changes
lengthnumbernumStepsActive sequence length (1-32)
onLengthChange(length: number) => void-Callback when length changes
swingnumber0Swing amount (-50 to 50)
onSwingChange(swing: number) => void-Callback when swing changes
onCurrentStepChange(currentStep: number) => void-Callback when step changes
childrenfunction-Render prop function

Step Model

Each step is an object:

ts
interface Step {
  active: boolean;      // Whether this step triggers
  value: number;        // CV output value
  lengthPct: number;    // Gate length as percentage (10-100)
  slide: boolean;       // Portamento into this step
  accent: boolean;      // Accent flag for this step
}

Render Props

PropertyTypeDescription
stepsStep[]Current step data
setSteps(steps: Step[]) => voidReplace all step data
currentStepnumberCurrent step index
divisionnumberClock division
setDivision(value: number) => voidUpdate division
lengthnumberSequence length
setLength(value: number) => voidUpdate length
swingnumberSwing amount
setSwing(value: number) => voidUpdate swing
reset() => voidReset to step 0

Usage

Clock -> Sequencer (Required)

The sequencer advances only when it receives clock pulses. Connect a Clock output to the Sequencer clock input.

tsx
import { Clock, Sequencer, Monitor } from '@mode-7/mod';
import { useRef } from 'react';

function App() {
  const clockOut = useRef(null);
  const seqOut = useRef(null);
  const seqGate = useRef(null);

  return (
    <>
      <Clock output={clockOut}>
        {({ start }) => <button onClick={start}>Start</button>}
      </Clock>
      <Sequencer output={seqOut} gateOutput={seqGate} clock={clockOut} />
      <Monitor input={seqOut} />
    </>
  );
}

Per-Step Features (Length, Slide, Accent)

tsx
import { Sequencer } from '@mode-7/mod';
import { useRef } from 'react';

function App() {
  const seqOut = useRef(null);
  const gateOut = useRef(null);
  const accentOut = useRef(null);

  return (
    <Sequencer output={seqOut} gateOutput={gateOut} accentOutput={accentOut}>
      {({ steps, setSteps }) => (
        <button
          onClick={() => {
            const next = [...steps];
            next[1] = { ...next[1], active: true, lengthPct: 50, slide: true, accent: true };
            setSteps(next);
          }}
        >
          Configure Step 2
        </button>
      )}
    </Sequencer>
  );
}

TB-303 Style Acid Sequence

tsx
import { Clock, Sequencer, ToneGenerator, Filter, ADSR, Monitor } from '@mode-7/mod';
import { useRef } from 'react';

function AcidBass() {
  const clockOut = useRef(null);
  const seqOut = useRef(null);
  const seqGate = useRef(null);
  const seqAccent = useRef(null);
  const toneOut = useRef(null);
  const filterOut = useRef(null);
  const adsrOut = useRef(null);

  return (
    <>
      <Clock output={clockOut} bpm={140}>
        {({ start, stop, isRunning }) => (
          <button onClick={isRunning ? stop : start}>
            {isRunning ? 'Stop' : 'Start'}
          </button>
        )}
      </Clock>

      <Sequencer
        output={seqOut}
        gateOutput={seqGate}
        accentOutput={seqAccent}
        clock={clockOut}
        division={4}
        swing={15}
      >
        {({ steps, setSteps, currentStep }) => (
          <div>
            <span>Step: {currentStep}</span>
            {/* Step editor UI would go here */}
          </div>
        )}
      </Sequencer>

      <ToneGenerator output={toneOut} frequency={110} cv={seqOut} cvAmount={200} />
      <Filter input={toneOut} output={filterOut} type="lowpass" frequency={800} cv={seqAccent} cvAmount={2000} />
      <ADSR input={filterOut} output={adsrOut} gate={seqGate} attack={0.01} decay={0.2} sustain={0.3} release={0.1} />
      <Monitor input={adsrOut} />
    </>
  );
}

Implementation Notes

Demo Input Configuration (v0.1.12+)

The demo/playground shows three inputs: Clock, Reset, and Stop. However, the Stop input is not yet implemented in the component and has no effect. Only Clock and Reset are functional.

This is planned for a future update to allow stopping the sequencer independently from the clock.

Behavior Notes

Clock Division

The sequencer uses the incoming clock pulses as a base and advances steps according to division:

DivisionNote Value
11/4 notes
21/8 notes
41/16 notes (default)
81/32 notes
161/64 notes

Gate Length

Each step has lengthPct (10-100). Gate and accent outputs stay high for that percentage of the step duration.

Slide

  • Slide is applied into a step when step[i].slide is true and both the previous and current steps are active.
  • The previous step's gate is held at 100% across the boundary (legato).
  • CV glides using a 65ms linear ramp after the step boundary.

Accent

Accent raises the accentOutput CV high for the step duration (lengthPct). Connect this to a filter cutoff or VCA for classic accent behavior.

Swing

Swing delays every other step to create a shuffle feel:

  • Positive values (1-50) delay odd steps
  • Negative values (-50 to -1) delay even steps
  • The delay is a percentage of the step duration

Reset

Sending a pulse to the reset input resets the sequence to step 0 on the next clock tick.

  • Clock - Clock source for step advance
  • ADSR - Use gate output to trigger envelopes
  • ToneGenerator - CV pitch source

Released under the MIT License.