diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs
index 7cfa6a6d..4762ab85 100644
--- a/packages/core/pattern.mjs
+++ b/packages/core/pattern.mjs
@@ -1976,6 +1976,24 @@ export const segment = register('segment', function (rate, pat) {
return pat.struct(pure(true)._fast(rate)).setTactus(rate);
});
+/**
+ * The function `swingBy x n` breaks each cycle into `n` slices, and then delays events in the second half of each slice by the amount `x`, which is relative to the size of the (half) slice. So if `x` is 0 it does nothing, `0.5` delays for half the note duration, and 1 will wrap around to doing nothing again. The end result is a shuffle or swing-like rhythm
+ * @param {number} subdivision
+ * @param {number} offset
+ * @example
+ * s("hh*8").swingBy(1/3, 4)
+ */
+export const swingBy = register('swingBy', (swing, n, pat) => pat.inside(n, late(seq(0, swing / 2))));
+
+/**
+ * Shorthand for swingBy with 1/3:
+ * @param {number} subdivision
+ * @example
+ * s("hh*8").swing(4)
+ * // s("hh*8").swingBy(1/3, 4)
+ */
+export const swing = register('swing', (n, pat) => pat.swingBy(1 / 3, n));
+
/**
* Swaps 1s and 0s in a binary pattern.
* @name invert
diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap
index 27074590..1d2af9de 100644
--- a/test/__snapshots__/examples.test.mjs.snap
+++ b/test/__snapshots__/examples.test.mjs.snap
@@ -7298,6 +7298,112 @@ exports[`runs examples > example "sustain" example index 0 1`] = `
]
`;
+exports[`runs examples > example "swing" example index 0 1`] = `
+[
+ "[ 0/1 → 1/8 | s:hh ]",
+ "[ 1/24 ⇜ (1/8 → 1/6) | s:hh ]",
+ "[ (1/6 → 1/4) ⇝ 7/24 | s:hh ]",
+ "[ 1/4 → 3/8 | s:hh ]",
+ "[ 7/24 ⇜ (3/8 → 5/12) | s:hh ]",
+ "[ (5/12 → 1/2) ⇝ 13/24 | s:hh ]",
+ "[ 1/2 → 5/8 | s:hh ]",
+ "[ 13/24 ⇜ (5/8 → 2/3) | s:hh ]",
+ "[ (2/3 → 3/4) ⇝ 19/24 | s:hh ]",
+ "[ 3/4 → 7/8 | s:hh ]",
+ "[ 19/24 ⇜ (7/8 → 11/12) | s:hh ]",
+ "[ (11/12 → 1/1) ⇝ 25/24 | s:hh ]",
+ "[ 1/1 → 9/8 | s:hh ]",
+ "[ 25/24 ⇜ (9/8 → 7/6) | s:hh ]",
+ "[ (7/6 → 5/4) ⇝ 31/24 | s:hh ]",
+ "[ 5/4 → 11/8 | s:hh ]",
+ "[ 31/24 ⇜ (11/8 → 17/12) | s:hh ]",
+ "[ (17/12 → 3/2) ⇝ 37/24 | s:hh ]",
+ "[ 3/2 → 13/8 | s:hh ]",
+ "[ 37/24 ⇜ (13/8 → 5/3) | s:hh ]",
+ "[ (5/3 → 7/4) ⇝ 43/24 | s:hh ]",
+ "[ 7/4 → 15/8 | s:hh ]",
+ "[ 43/24 ⇜ (15/8 → 23/12) | s:hh ]",
+ "[ (23/12 → 2/1) ⇝ 49/24 | s:hh ]",
+ "[ 2/1 → 17/8 | s:hh ]",
+ "[ 49/24 ⇜ (17/8 → 13/6) | s:hh ]",
+ "[ (13/6 → 9/4) ⇝ 55/24 | s:hh ]",
+ "[ 9/4 → 19/8 | s:hh ]",
+ "[ 55/24 ⇜ (19/8 → 29/12) | s:hh ]",
+ "[ (29/12 → 5/2) ⇝ 61/24 | s:hh ]",
+ "[ 5/2 → 21/8 | s:hh ]",
+ "[ 61/24 ⇜ (21/8 → 8/3) | s:hh ]",
+ "[ (8/3 → 11/4) ⇝ 67/24 | s:hh ]",
+ "[ 11/4 → 23/8 | s:hh ]",
+ "[ 67/24 ⇜ (23/8 → 35/12) | s:hh ]",
+ "[ (35/12 → 3/1) ⇝ 73/24 | s:hh ]",
+ "[ 3/1 → 25/8 | s:hh ]",
+ "[ 73/24 ⇜ (25/8 → 19/6) | s:hh ]",
+ "[ (19/6 → 13/4) ⇝ 79/24 | s:hh ]",
+ "[ 13/4 → 27/8 | s:hh ]",
+ "[ 79/24 ⇜ (27/8 → 41/12) | s:hh ]",
+ "[ (41/12 → 7/2) ⇝ 85/24 | s:hh ]",
+ "[ 7/2 → 29/8 | s:hh ]",
+ "[ 85/24 ⇜ (29/8 → 11/3) | s:hh ]",
+ "[ (11/3 → 15/4) ⇝ 91/24 | s:hh ]",
+ "[ 15/4 → 31/8 | s:hh ]",
+ "[ 91/24 ⇜ (31/8 → 47/12) | s:hh ]",
+ "[ (47/12 → 4/1) ⇝ 97/24 | s:hh ]",
+]
+`;
+
+exports[`runs examples > example "swingBy" example index 0 1`] = `
+[
+ "[ 0/1 → 1/8 | s:hh ]",
+ "[ 1/24 ⇜ (1/8 → 1/6) | s:hh ]",
+ "[ (1/6 → 1/4) ⇝ 7/24 | s:hh ]",
+ "[ 1/4 → 3/8 | s:hh ]",
+ "[ 7/24 ⇜ (3/8 → 5/12) | s:hh ]",
+ "[ (5/12 → 1/2) ⇝ 13/24 | s:hh ]",
+ "[ 1/2 → 5/8 | s:hh ]",
+ "[ 13/24 ⇜ (5/8 → 2/3) | s:hh ]",
+ "[ (2/3 → 3/4) ⇝ 19/24 | s:hh ]",
+ "[ 3/4 → 7/8 | s:hh ]",
+ "[ 19/24 ⇜ (7/8 → 11/12) | s:hh ]",
+ "[ (11/12 → 1/1) ⇝ 25/24 | s:hh ]",
+ "[ 1/1 → 9/8 | s:hh ]",
+ "[ 25/24 ⇜ (9/8 → 7/6) | s:hh ]",
+ "[ (7/6 → 5/4) ⇝ 31/24 | s:hh ]",
+ "[ 5/4 → 11/8 | s:hh ]",
+ "[ 31/24 ⇜ (11/8 → 17/12) | s:hh ]",
+ "[ (17/12 → 3/2) ⇝ 37/24 | s:hh ]",
+ "[ 3/2 → 13/8 | s:hh ]",
+ "[ 37/24 ⇜ (13/8 → 5/3) | s:hh ]",
+ "[ (5/3 → 7/4) ⇝ 43/24 | s:hh ]",
+ "[ 7/4 → 15/8 | s:hh ]",
+ "[ 43/24 ⇜ (15/8 → 23/12) | s:hh ]",
+ "[ (23/12 → 2/1) ⇝ 49/24 | s:hh ]",
+ "[ 2/1 → 17/8 | s:hh ]",
+ "[ 49/24 ⇜ (17/8 → 13/6) | s:hh ]",
+ "[ (13/6 → 9/4) ⇝ 55/24 | s:hh ]",
+ "[ 9/4 → 19/8 | s:hh ]",
+ "[ 55/24 ⇜ (19/8 → 29/12) | s:hh ]",
+ "[ (29/12 → 5/2) ⇝ 61/24 | s:hh ]",
+ "[ 5/2 → 21/8 | s:hh ]",
+ "[ 61/24 ⇜ (21/8 → 8/3) | s:hh ]",
+ "[ (8/3 → 11/4) ⇝ 67/24 | s:hh ]",
+ "[ 11/4 → 23/8 | s:hh ]",
+ "[ 67/24 ⇜ (23/8 → 35/12) | s:hh ]",
+ "[ (35/12 → 3/1) ⇝ 73/24 | s:hh ]",
+ "[ 3/1 → 25/8 | s:hh ]",
+ "[ 73/24 ⇜ (25/8 → 19/6) | s:hh ]",
+ "[ (19/6 → 13/4) ⇝ 79/24 | s:hh ]",
+ "[ 13/4 → 27/8 | s:hh ]",
+ "[ 79/24 ⇜ (27/8 → 41/12) | s:hh ]",
+ "[ (41/12 → 7/2) ⇝ 85/24 | s:hh ]",
+ "[ 7/2 → 29/8 | s:hh ]",
+ "[ 85/24 ⇜ (29/8 → 11/3) | s:hh ]",
+ "[ (11/3 → 15/4) ⇝ 91/24 | s:hh ]",
+ "[ 15/4 → 31/8 | s:hh ]",
+ "[ 91/24 ⇜ (31/8 → 47/12) | s:hh ]",
+ "[ (47/12 → 4/1) ⇝ 97/24 | s:hh ]",
+]
+`;
+
exports[`runs examples > example "timecat" example index 0 1`] = `
[
"[ 0/1 → 3/4 | note:e3 ]",
diff --git a/website/src/pages/learn/time-modifiers.mdx b/website/src/pages/learn/time-modifiers.mdx
index 1baef9ea..7419bcfa 100644
--- a/website/src/pages/learn/time-modifiers.mdx
+++ b/website/src/pages/learn/time-modifiers.mdx
@@ -106,4 +106,12 @@ Some of these have equivalent operators in the Mini Notation:
+## swingBy
+
+
+
+## swing
+
+
+
Apart from modifying time, there are ways to [Control Parameters](/functions/value-modifiers/).