lfo

lfo · modulation

Clockable LFO with four phase outputs (0deg / 90deg / 180deg / 270deg).

A low-frequency modulation source: one oscillator emits the same wave at four quadrature phase taps (0°/90°/180°/270°) so a single LFO can sweep several destinations in stereo or round-robin without re-tuning. The engine is three controls — Rate sets the cycle speed (0.01–100 Hz), Shape continuously morphs the waveform along one axis (sine → saw → square), and Depth scales the bipolar swing (0.5 = unity ±1). A rising edge on the clock input hard-resets phase to 0 — a true pulse-train lock when the LFO is free-running (solo on the public canvas, where it starts from phase 0 and each reset persists). In a shared/multiplayer rack the phase is instead anchored to the rack's shared clock (drift corrections smoothed over ~200 ms) so every client sees the same value at the same moment — and that anchor outranks the clock input between pulses: a clock edge still snaps phase to 0 at that instant, but the anchor then glides it back to the shared-clock-derived phase.

the faceplate

Numbered control face for lfo
#controlwhat it does
1RateHow fast the LFO cycles, from 0.01 Hz (one sweep per ~100 s) to 100 Hz (audio-rate for FM-style use) on a logarithmic dial. It is the faceplate's headline readout, and it SWITCHES UNITS at 1 Hz: at or above 1 Hz it reads as a frequency (1.00 Hz, 12.3 Hz, 100 Hz) and below it as the PERIOD (0.5 Hz reads 2.00 s, the 0.01 Hz floor reads 100 s), because the dial's whole bottom two thirds are sub-Hertz where a raw number says nothing you can act on. 1 Hz is the only rate where the two readings are the same number, which is what makes the switch invisible. Sets the one speed shared by all four phase outputs; the clock input overrides phase, not rate.
2ShapeContinuously morphs the waveform along the 0–2 axis: 0 = sine, 1 = saw, 2 = square, with smooth linear crossfades between adjacent anchors (e.g. 0.5 = halfway sine↔saw). The three anchors are marked on the dial as ticks and the readout names the NEAREST one, so you always know which region you are in — but the value between them is a real blend, not a snap: the dial does not detent and nothing quantizes. The face's live waveform glyph draws the resolved shape continuously as you move it, using the engine's own morph law rather than a stylized icon.
3DepthOutput amplitude / swing, applied as gain = depth × 2 and not clamped: 0 = still (flat at the 0 center, no modulation), 0.5 = unity ±1 (the default, matches legacy patches), 1 = ±2 (deliberately beyond the normal ±1 range). The readout prints the SWING rather than the knob's own position — ±1.00 at the default, STILL at zero — because 0.50 reads like 'half' when it is actually unity. Orthogonal to shape: it only scales the swing, never shifts the center. Note the face's waveform glyph tracks it only up to unity, where the drawn cycle fills the screen box and saturates; past that the picture stops changing while the real swing keeps growing.

inputs

idcablewhat it does
clockgateExternal clock — each rising edge (crossing above 0.5) hard-resets the oscillator to phase 0. The reset only re-zeros the phase (the period is NOT measured; Rate still sets the speed between pulses), and it is intentionally hard: it snaps at the edge sample and cancels any in-flight shared-clock correction ramp, so a click on the edge is expected. Locking to an incoming pulse train is really a free-running feature: with no shared clock anchored (e.g. solo on the public canvas) each reset persists until the next pulse. In a shared/multiplayer rack the shared-clock anchor takes precedence between pulses — after a reset, its next divergence check schedules a ~200 ms glide back to the shared-clock-derived phase (the check runs once the anchor's periodic resync is active, within ~5 s of the clock attaching) — so there a clock edge is a momentary re-zero, not a lasting re-lock.
gate / trigger; trigger — fires once per rising edge
ratecvCV that scales the Rate knob on a logarithmic axis — ±1 multiplies the rate by about 100× / one-hundredth (≈ ±6.6 octaves of speed). Sample-and-held once at the start of each ~128-sample audio block so multiple clients stay phase-aligned despite sub-block CV latency differences.
control voltage (CV); modulates rate (multiplicative ≈ octaves — ±1 CV spans the param’s log range)
shapecvCV that displaces the Shape control, sliding the waveform morph (sine → saw → square) up or down around the knob position. Read per-sample (a-rate) so the morph stays smooth.
control voltage (CV); modulates shape (additive offset — ±1 CV sweeps the full range, centered on the knob)
depth_cvcvCV that sums into the Depth control, modulating the swing amount the same way the rate/shape CV inputs offset their params. ±1 sweeps depth across half its 0–1 range around the knob position. Read per-sample (a-rate) like Shape — not block-held like Rate — so an audio-rate signal here amplitude-modulates the taps rather than drifting them.
control voltage (CV); modulates depth (additive offset — ±1 CV sweeps the full range, centered on the knob)

outputs

idcablewhat it does
phase0cvThe LFO at 0° (the reference phase). Bipolar wave centered on 0; its swing magnitude is set by Depth (0 = flat/still, 0.5 = unity ±1, 1 = ±2).
control voltage (CV)
phase90cvThe same LFO advanced a quarter cycle (phase + 0.25) — the 90° tap, useful as the offset partner for quadrature/stereo modulation.
control voltage (CV)
phase180cvThe same LFO advanced half a cycle (phase + 0.5) — the 180° tap; for the sine and square shapes this is a polarity inversion (moves opposite phase0), useful for ping-pong / push-pull modulation (the saw instead restarts its ramp half a cycle early).
control voltage (CV)
phase270cvThe same LFO advanced three-quarters of a cycle (phase + 0.75) — the 270° tap, completing the 0/90/180/270 quadrature set off one shared oscillator.
control voltage (CV)

params

idlabelrangedefaultcurve
rateRate0.01..100Hz1log
shapeShape0..20linear
depthDepth0..1linear

source

lfo.ts on GitHub.

Generated from packages/web/src/lib/{audio,video}/module-registry.ts · repo