Clock
The Clock component generates precise timing pulses (gate signals) that can trigger envelopes, sync sequencers, or create rhythmic patterns.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
output | ModStreamRef | Required | Reference to output the clock pulses |
startOutput | ModStreamRef | - | Optional reference for a start trigger signal (outputs 1 while running, 0 when stopped) |
stopOutput | ModStreamRef | - | Optional reference for a stop trigger signal (outputs 1 on stop event, 0 otherwise) |
label | string | 'clock' | Label for the component in metadata |
bpm | number | 120 | Tempo in beats per minute (controlled or initial value) |
onBpmChange | (bpm: number) => void | - | Callback when BPM changes |
onRunningChange | (isRunning: boolean) => void | - | Callback when running state changes |
children | function | - | Render prop function receiving control props |
Render Props
When using the children render prop, the following controls are provided:
| Property | Type | Description |
|---|---|---|
bpm | number | Current tempo in beats per minute |
setBpm | (value: number) => void | Update the tempo |
isRunning | boolean | Whether the clock is running |
start | () => void | Start the clock |
stop | () => void | Stop the clock |
reset | () => void | Stop the clock and reset |
Usage
Basic Usage
import { Clock, ADSR, ToneGenerator, Monitor } from '@mode-7/mod';
import { useRef } from 'react';
function App() {
const clockOut = useRef(null);
const adsrOut = useRef(null);
const toneOut = useRef(null);
return (
<>
<Clock output={clockOut}>
{({ start, stop, isRunning }) => (
<button onClick={isRunning ? stop : start}>
{isRunning ? 'Stop' : 'Start'}
</button>
)}
</Clock>
<ADSR gate={clockOut} output={adsrOut} />
<ToneGenerator output={toneOut} cv={adsrOut} cvAmount={1.0} />
<Monitor input={toneOut} />
</>
);
}With Tempo Control
import { Clock } from '@mode-7/mod';
import { useRef } from 'react';
function App() {
const clockOut = useRef(null);
return (
<Clock output={clockOut}>
{({ bpm, setBpm, isRunning, start, stop, reset }) => (
<div>
<div>
<button onClick={start} disabled={isRunning}>Start</button>
<button onClick={stop} disabled={!isRunning}>Stop</button>
<button onClick={reset}>Reset</button>
</div>
<div>
<label>Tempo: {bpm} BPM</label>
<input
type="range"
min="40"
max="240"
value={bpm}
onChange={(e) => setBpm(Number(e.target.value))}
/>
</div>
<div>
<button onClick={() => setBpm(60)}>60</button>
<button onClick={() => setBpm(90)}>90</button>
<button onClick={() => setBpm(120)}>120</button>
<button onClick={() => setBpm(140)}>140</button>
<button onClick={() => setBpm(180)}>180</button>
</div>
</div>
)}
</Clock>
);
}Triggering Multiple Envelopes
import { Clock, ADSR, ToneGenerator, Filter, Monitor } from '@mode-7/mod';
import { useRef } from 'react';
function App() {
const clockOut = useRef(null);
const ampEnvOut = useRef(null);
const filterEnvOut = useRef(null);
const toneOut = useRef(null);
const filterOut = useRef(null);
return (
<>
<Clock output={clockOut} bpm={128}>
{({ start }) => {
React.useEffect(() => { start(); }, []);
return null;
}}
</Clock>
{/* Amplitude envelope */}
<ADSR
gate={clockOut}
output={ampEnvOut}
/>
{/* Filter envelope with different settings */}
<ADSR
gate={clockOut}
output={filterEnvOut}
>
{({ setAttack, setDecay, setSustain, setRelease }) => {
React.useEffect(() => {
setAttack(0.001);
setDecay(0.3);
setSustain(0.2);
setRelease(0.2);
}, []);
return null;
}}
</ADSR>
<ToneGenerator output={toneOut} cv={ampEnvOut} cvAmount={1.0} />
<Filter
input={toneOut}
output={filterOut}
cv={filterEnvOut}
cvAmount={2000}
/>
<Monitor input={filterOut} />
</>
);
}Rhythmic Pattern with Sequencer
import { Clock, Sequencer, ToneGenerator, Monitor } from '@mode-7/mod';
import { useRef } from 'react';
function App() {
const clockOut = useRef(null);
const seqOut = useRef(null);
const toneOut = useRef(null);
return (
<>
<Clock output={clockOut} bpm={120}>
{({ start, isRunning }) => {
React.useEffect(() => {
if (!isRunning) start();
}, []);
return null;
}}
</Clock>
<Sequencer output={seqOut} numSteps={16}>
{({ setSteps }) => {
React.useEffect(() => {
// Create a rhythmic pattern
setSteps([
1.0, 0.0, 0.5, 0.0, 0.8, 0.0, 0.3, 0.0,
1.0, 0.0, 0.5, 0.0, 0.6, 0.4, 0.2, 0.0,
]);
}, []);
return null;
}}
</Sequencer>
<ToneGenerator
output={toneOut}
frequency={100}
cv={seqOut}
cvAmount={200}
/>
<Monitor input={toneOut} />
</>
);
}Controlled Props
You can control the Clock from external state using controlled props:
import { Clock, ADSR, ToneGenerator, Monitor } from '@mode-7/mod';
import { useState, useRef } from 'react';
function App() {
const clockOut = useRef(null);
const adsrOut = useRef(null);
const toneOut = useRef(null);
const [bpm, setBpm] = useState(120);
const [isRunning, setRunning] = useState(false);
return (
<>
<Clock
output={clockOut}
bpm={bpm}
onBpmChange={setBpm}
onRunningChange={setRunning}
>
{({ start, stop }) => (
<button onClick={isRunning ? stop : start}>
{isRunning ? 'Stop' : 'Start'}
</button>
)}
</Clock>
<div>
<label>Tempo: {bpm} BPM</label>
<input
type="range"
min="40"
max="240"
value={bpm}
onChange={(e) => setBpm(Number(e.target.value))}
/>
</div>
<ADSR gate={clockOut} output={adsrOut} />
<ToneGenerator output={toneOut} cv={adsrOut} cvAmount={1.0} />
<Monitor input={toneOut} />
</>
);
}Imperative Refs
For programmatic access to state and transport control, you can use refs:
import { Clock, ClockHandle, ADSR, ToneGenerator, Monitor } from '@mode-7/mod';
import { useRef, useEffect } from 'react';
function App() {
const clockRef = useRef<ClockHandle>(null);
const clockOut = useRef(null);
const adsrOut = useRef(null);
const toneOut = useRef(null);
useEffect(() => {
// Access current state
if (clockRef.current) {
const state = clockRef.current.getState();
console.log('BPM:', state.bpm);
console.log('Is running:', state.isRunning);
}
}, []);
const handleTransport = () => {
if (!clockRef.current) return;
// Use imperative transport methods
clockRef.current.start();
// clockRef.current.stop();
// clockRef.current.reset();
};
return (
<>
<Clock ref={clockRef} output={clockOut} />
<ADSR gate={clockOut} output={adsrOut} />
<ToneGenerator output={toneOut} cv={adsrOut} cvAmount={1.0} />
<button onClick={handleTransport}>Start</button>
<Monitor input={toneOut} />
</>
);
}Note: The imperative handle provides start(), stop(), and reset() for transport control, plus getState() for read-only access. To control BPM programmatically, use the controlled props pattern shown above.
Important Notes
Clock Pulses
- Each pulse is a short 10ms trigger at 16th note intervals (16 pulses per beat)
- Pulses go from 0 to 1 and back to 0
- Pulse timing is sample-accurate using an AudioWorklet for precise timing
AudioWorklet Implementation
The Clock uses an AudioWorklet processor for sample-accurate timing. This provides:
- Precise pulse generation on the audio thread
- No timing jitter from JavaScript's main thread
- Smooth BPM changes without timing glitches
BPM Range
- Supported range: 1-999 BPM
- Typical range: 40-240 BPM
- Lower values create slower rhythms
- Higher values create faster rhythms
- Can be changed while the clock is running
Gate Outputs
- The clock output is compatible with any component accepting gate inputs
- Most commonly used with ADSR envelopes
- Can also trigger sequencers or other CV modules
Start Output
The optional startOutput prop provides a trigger pulse when the clock starts:
- Outputs a short pulse (10ms at 1.0) when the clock starts
- Outputs 0 otherwise
- Useful for triggering envelopes or resetting components at transport start
Stop Output
The optional stopOutput prop provides a trigger pulse when the clock stops:
- Outputs a short pulse (10ms at 1.0) when the clock stops
- Outputs 0 otherwise
- Useful for triggering events or resetting components when transport stops
Sync Considerations
- The clock starts immediately when
start()is called - Changing BPM while running adjusts timing smoothly
reset()stops the clock and can be used to resync- Phase resets to 0 when clock starts or restarts
Shared Transport Architecture
Important: All Clock components within the same AudioContext share a single underlying transport worklet. This design ensures:
- Perfect synchronization - Multiple clocks always stay in phase
- Shared BPM - All clocks share the same tempo
- Efficient resource usage - Single worklet handles all clock timing
Implications:
- If you create multiple Clock components, changing the BPM on any one clock will affect all clocks in the same AudioContext
- All clocks share the same start/stop state
- This is by design to enable tight synchronization in modular patches
- Each Clock component still has its own independent outputs and can be routed differently
Related
- ADSR - Trigger envelopes with clock pulses
- Sequencer - Can be synced to clock tempo
- ToneGenerator - Create rhythmic patterns
