diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs
index f988f0a7..6cac6e54 100644
--- a/packages/core/controls.mjs
+++ b/packages/core/controls.mjs
@@ -640,6 +640,36 @@ const generic_params = [
*/
// currently an alias of 'hcutoff' https://github.com/tidalcycles/strudel/issues/496
// ['hpf'],
+ /**
+ * Applies a vibrato to the frequency of the oscillator.
+ *
+ * @name vib
+ * @synonyms vibrato, v
+ * @param {number | Pattern} frequency of the vibrato in hertz
+ * @example
+ * note("a")
+ * .vib("<.5 1 2 4 8 16>")
+ * @example
+ * // change the modulation depth with ":"
+ * note("a")
+ * .vib("<.5 1 2 4 8 16>:12")
+ */
+ [['vib', 'vibmod'], 'vibrato', 'v'],
+ /**
+ * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set
+ *
+ * @name vibmod
+ * @synonyms vmod
+ * @param {number | Pattern} depth of vibrato (in semitones)
+ * @example
+ * note("a").vib(4)
+ * .vibmod("<.25 .5 1 2 12>")
+ * @example
+ * // change the vibrato frequency with ":"
+ * note("a")
+ * .vibmod("<.25 .5 1 2 12>:8")
+ */
+ [['vibmod', 'vib'], 'vmod'],
[['hcutoff', 'hresonance'], 'hpf', 'hp'],
/**
* Controls the **h**igh-**p**ass **q**-value.
diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs
index a409d990..633d0113 100644
--- a/packages/superdough/synth.mjs
+++ b/packages/superdough/synth.mjs
@@ -40,6 +40,8 @@ export function registerSynthSounds() {
fmrelease: fmRelease,
fmvelocity: fmVelocity,
fmwave: fmWaveform = 'sine',
+ vib = 0,
+ vibmod = 0.5,
} = value;
let { n, note, freq } = value;
// with synths, n and note are the same thing
@@ -53,7 +55,14 @@ export function registerSynthSounds() {
}
// maybe pull out the above frequency resolution?? (there is also getFrequency but it has no default)
// make oscillator
- const { node: o, stop } = getOscillator({ t, s: wave, freq, partials: n });
+ const { node: o, stop } = getOscillator({
+ t,
+ s: wave,
+ freq,
+ vib,
+ vibmod,
+ partials: n,
+ });
// FM + FM envelope
let stopFm, fmEnvelope;
@@ -137,8 +146,8 @@ export function waveformN(partials, type) {
return osc;
}
-export function getOscillator({ s, freq, t, partials }) {
- // make oscillator
+export function getOscillator({ s, freq, t, vib, vibmod, partials }) {
+ // Make oscillator with partial count
let o;
if (!partials || s === 'sine') {
o = getAudioContext().createOscillator();
@@ -148,7 +157,25 @@ export function getOscillator({ s, freq, t, partials }) {
}
o.frequency.value = Number(freq);
o.start(t);
- //o.stop(t + duration + release);
- const stop = (time) => o.stop(time);
- return { node: o, stop };
+
+ // Additional oscillator for vibrato effect
+ let vibrato_oscillator;
+ if (vib > 0) {
+ vibrato_oscillator = getAudioContext().createOscillator();
+ vibrato_oscillator.frequency.value = vib;
+ const gain = getAudioContext().createGain();
+ // Vibmod is the amount of vibrato, in semitones
+ gain.gain.value = vibmod * 100;
+ vibrato_oscillator.connect(gain);
+ gain.connect(o.detune);
+ vibrato_oscillator.start(t);
+ }
+
+ return {
+ node: o,
+ stop: (time) => {
+ vibrato_oscillator?.stop(time);
+ o.stop(time);
+ },
+ };
}
diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap
index 48835a48..e026f9c4 100644
--- a/test/__snapshots__/examples.test.mjs.snap
+++ b/test/__snapshots__/examples.test.mjs.snap
@@ -4758,6 +4758,42 @@ exports[`runs examples > example "velocity" example index 0 1`] = `
]
`;
+exports[`runs examples > example "vib" example index 0 1`] = `
+[
+ "[ 0/1 → 1/1 | note:a vib:0.5 ]",
+ "[ 1/1 → 2/1 | note:a vib:1 ]",
+ "[ 2/1 → 3/1 | note:a vib:2 ]",
+ "[ 3/1 → 4/1 | note:a vib:4 ]",
+]
+`;
+
+exports[`runs examples > example "vib" example index 1 1`] = `
+[
+ "[ 0/1 → 1/1 | note:a vib:0.5 vibmod:12 ]",
+ "[ 1/1 → 2/1 | note:a vib:1 vibmod:12 ]",
+ "[ 2/1 → 3/1 | note:a vib:2 vibmod:12 ]",
+ "[ 3/1 → 4/1 | note:a vib:4 vibmod:12 ]",
+]
+`;
+
+exports[`runs examples > example "vibmod" example index 0 1`] = `
+[
+ "[ 0/1 → 1/1 | note:a vib:4 vibmod:0.25 ]",
+ "[ 1/1 → 2/1 | note:a vib:4 vibmod:0.5 ]",
+ "[ 2/1 → 3/1 | note:a vib:4 vibmod:1 ]",
+ "[ 3/1 → 4/1 | note:a vib:4 vibmod:2 ]",
+]
+`;
+
+exports[`runs examples > example "vibmod" example index 1 1`] = `
+[
+ "[ 0/1 → 1/1 | note:a vibmod:0.25 vib:8 ]",
+ "[ 1/1 → 2/1 | note:a vibmod:0.5 vib:8 ]",
+ "[ 2/1 → 3/1 | note:a vibmod:1 vib:8 ]",
+ "[ 3/1 → 4/1 | note:a vibmod:2 vib:8 ]",
+]
+`;
+
exports[`runs examples > example "voicing" example index 0 1`] = `
[
"[ 0/1 → 1/1 | note:E4 ]",
diff --git a/website/src/pages/learn/synths.mdx b/website/src/pages/learn/synths.mdx
index 26e94467..9f21204f 100644
--- a/website/src/pages/learn/synths.mdx
+++ b/website/src/pages/learn/synths.mdx
@@ -48,6 +48,16 @@ You can also set `n` directly in mini notation with `sound`:
Note for tidal users: `n` in tidal is synonymous to `note` for synths only.
In strudel, this is not the case, where `n` will always change timbre, be it though different samples or different waveforms.
+## Vibrato
+
+### vib
+
+
+
+### vibmod
+
+
+
## FM Synthesis
FM Synthesis is a technique that changes the frequency of a basic waveform rapidly to alter the timbre.