Metlivi Blog

How to Pause a Modulation Position and Resume It Backward on the Next Trigger in SuperCollider

To pause a modulation position and resume it backward, keep its phase generator running with an increment of zero, then switch that increment to a negative value on the next trigger. Leave the phase-reset input disconnected from those triggers so the movement continues from the held position.

This tutorial builds that behavior in SuperCollider: a rising pitch sweep pauses on the first trigger and retraces its path on the second. It is intended for a sound designer who wants a repeatable modulation gesture controlled by successive events. The same phase signal can later drive another parameter.

October 29, 20266 min readBy Metlivi Editorial Team
Section 1

What Must Be Preserved During the Pause?

Treat modulation as two connected operations: a position moves through a cycle, and a mapping converts that position into a useful parameter value. Here, position runs from zero toward one, and the mapping turns it into oscillator frequency.

SuperCollider’s Phasor provides the position. Its rate argument specifies the increment per sample; its trigger input instead jumps to resetPos. These are separate controls, so a trigger used to change direction should not also reset the position. Phasor documentation

The implementation retains an internal level and adds the rate to it. From that behavior, zero rate holds the level, while negative rate moves it downward. The patch below applies this directly, preserving the generator’s state throughout the pause. SuperCollider 3.14.0 TriggerUGens.cpp implementation

Section 2

What Should Each Successive Trigger Do?

Define the event sequence before connecting the sound. For this patch, movement starts forward immediately. Each subsequent trigger advances one state:

Triggers received State Direction multiplier
0 Move forward +1
1 Hold current position 0
2 Resume backward −1
3 Hold current position 0
4 Resume forward +1

This four-state cycle separates the two held states: one leads into backward motion, and the other leads into forward motion. That makes repeated gestures predictable without requiring another direction button.

PulseCount increments when its input changes from non-positive to positive. Taking the count modulo four produces the repeating indices 0–3. Select then chooses the corresponding multiplier from [1, 0, -1, 0]. These are the documented roles of PulseCount and Select.

Section 3

How Do You Build the Complete Patch?

Use a local SuperCollider installation with a working audio output; the example follows the 3.14.0 documentation. It uses built-in unit generators and generates its own sound, so no sample files or extensions are required. Evaluate the entire block once and wait for the ready message.

waitForBoot starts the server if needed. The definition is sent before the synth is created, with s.sync waiting for outstanding server operations. See the Server documentation and SynthDef documentation.

(
s = Server.default;
s.waitForBoot({
    SynthDef(\pauseReverseMod, {
        |out = 0, t_step = 0, cycleSeconds = 8, amp = 0.08|
        var state, direction, increment, phase, frequency, sound;

        state = PulseCount.kr(t_step) % 4;
        direction = Select.kr(state, [1, 0, -1, 0]);

        // A full 0..1 traversal takes cycleSeconds seconds.
        increment = direction * SampleDur.ir
            / cycleSeconds.max(0.01);

        // State-changing triggers never reset this phase.
        phase = Phasor.ar(
            trig: 0,
            rate: increment,
            start: 0,
            end: 1,
            resetPos: 0
        );

        frequency = LinExp.ar(phase, 0, 1, 220, 880);
        sound = SinOsc.ar(frequency, 0, amp);
        Out.ar(out, sound ! 2);

        Poll.kr(Impulse.kr(2), state, "state");
        Poll.kr(Impulse.kr(2), A2K.kr(phase), "phase");
    }).add;

    s.sync;
    ~pauseReverseSynth = Synth(\pauseReverseMod);
    "Ready: evaluate ~pauseReverseSynth.set(\\t_step, 1);".postln;
});
)

The increment is direction multiplied by sample duration and divided by cycle duration. SampleDur returns the reciprocal of the server sample rate, so the eight-second setting does not assume a particular sample rate. SampleDur documentation

The pitch mapping spans 220–880 Hz. LinExp converts a linear input range into an exponential output range; its destination endpoints must be nonzero and share the same sign. Both endpoints here satisfy that requirement. LinExp documentation

I inspected the SuperCollider example against the retrieved documentation. The SuperCollider runtime was unavailable in the research environment, so I did not execute or audition this patch. A separate deterministic arithmetic check verified the direction sequence and the zero-increment hold behavior.

Section 4

How Do You Pause and Resume Backward?

After the ready message, allow the pitch to rise for roughly two seconds. Then evaluate this interaction command once:

~pauseReverseSynth.set(\t_step, 1);

The state should become 1, and the printed phase should settle at one value. Wait a moment, then evaluate the same command again. The state should become 2, and the phase should decrease from the held position. A third evaluation holds the descending sweep; a fourth starts it forward again.

The t_ prefix makes t_step a trigger control. Setting it produces a control-rate impulse, allowing the same command to be used repeatedly without manually sending a separate zero between ordinary, spaced evaluations. SynthDef trigger-control documentation

For an illustrative timing calculation, an eight-second cycle advances by 0.125 of its range per second. Pausing after two seconds therefore holds approximately 0.25. After any pause duration, one second of backward movement takes it to approximately 0.125. These are calculated expectations; manual command timing determines the actual stopping position.

To finish, evaluate the cleanup command:

~pauseReverseSynth.free;
Section 5

How Can You Verify That the Position Really Stayed Put?

Use the printed phase as the main diagnostic. During forward motion it should increase; during either held state it should remain constant; during backward motion it should decrease until it reaches the cycle boundary.

The patch prints twice per second, so the display samples the behavior rather than showing every sample. Observe several consecutive readings during the pause, then compare the first descending readings after resuming. Poll is intended for debugging unit-generator values; remove the two polling lines when adapting the definition for regular use. Poll documentation

This test is more informative than listening alone. A steady sound establishes that the destination stopped changing, while a stable phase reading confirms that the underlying position also stopped. Keeping the mapping fixed throughout the test makes the result easier to interpret.

Section 6

Why Might the Patch Restart, Jump, or Miss a Trigger?

It restarts when you resume. Check whether the event also reaches Phasor’s trigger input. That input explicitly resets position. In this patch it stays at zero, while events feed only the state counter. Phasor documentation

It jumps after reaching an endpoint. This example deliberately loops. The implementation wraps the phase into its configured range, including during negative travel. Once backward movement passes zero, the phase returns near the upper boundary, producing a pitch jump with this ramp mapping. Test the pause and reversal away from that boundary first. Phasor implementation

Several rapid commands do not produce several states. The counter needs distinct rising transitions. Trigger controls operate at control rate, so do not assume multiple commands arriving within one control block will become separate events. Start with deliberate, spaced commands; for tightly timed sequences, design the event source around distinct pulses. PulseCount documentation, SynthDef documentation

The sound changes while the phase is held. Check whether you are also changing the destination mapping or another sound parameter. For this test, leave the pitch endpoints fixed and use the printed phase to isolate the modulation transport.

The completed gesture is forward, hold, backward, hold, then forward again. Once that sequence behaves correctly, retain the state counter and phase generator as a reusable modulation source, and change the destination mapping to suit the sound you are building.

Section 7

Sources

Related reading

Keep exploring this topic