From df544047332a9ea5f71e744b12b33bbbb393721b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Wed, 1 Oct 2025 22:21:03 +0200 Subject: [PATCH 001/124] Group by category --- jsdoc/jsdoc-synonyms.js | 8 +++ packages/motion/motion.mjs | 15 +++++ .../src/repl/components/panel/Reference.jsx | 60 ++++++++++++------- 3 files changed, 63 insertions(+), 20 deletions(-) diff --git a/jsdoc/jsdoc-synonyms.js b/jsdoc/jsdoc-synonyms.js index d59c8dac4..aed0dcc06 100644 --- a/jsdoc/jsdoc-synonyms.js +++ b/jsdoc/jsdoc-synonyms.js @@ -12,6 +12,14 @@ function defineTags(dictionary) { doclet.synonyms = doclet.synonyms_text.split(/[ ,]+/); }, }); + + dictionary.defineTag('group', { + mustHaveValue: true, + onTagged: function (doclet, tag) { + doclet.group = tag.value; + console.log('TAGGED GROUP:', doclet.group); + }, + }); } module.exports = { defineTags: defineTags }; diff --git a/packages/motion/motion.mjs b/packages/motion/motion.mjs index 875704eae..cba814a84 100644 --- a/packages/motion/motion.mjs +++ b/packages/motion/motion.mjs @@ -7,6 +7,7 @@ import { signal } from '../core/signal.mjs'; * @name accelerationX * @return {Pattern} * @synonyms accX + * @group external_io * @example * n(accelerationX.segment(4).range(0,7)).scale("C:minor") * @@ -17,6 +18,7 @@ import { signal } from '../core/signal.mjs'; * @name accelerationY * @return {Pattern} * @synonyms accY + * @group external_io * @example * n(accelerationY.segment(4).range(0,7)).scale("C:minor") * @@ -27,6 +29,7 @@ import { signal } from '../core/signal.mjs'; * @name accelerationZ * @return {Pattern} * @synonyms accZ + * @group external_io * @example * n(accelerationZ.segment(4).range(0,7)).scale("C:minor") * @@ -37,6 +40,7 @@ import { signal } from '../core/signal.mjs'; * @name gravityX * @return {Pattern} * @synonyms gravX + * @group external_io * @example * n(gravityX.segment(4).range(0,7)).scale("C:minor") * @@ -47,6 +51,7 @@ import { signal } from '../core/signal.mjs'; * @name gravityY * @return {Pattern} * @synonyms gravY + * @group external_io * @example * n(gravityY.segment(4).range(0,7)).scale("C:minor") * @@ -57,6 +62,7 @@ import { signal } from '../core/signal.mjs'; * @name gravityZ * @return {Pattern} * @synonyms gravZ + * @group external_io * @example * n(gravityZ.segment(4).range(0,7)).scale("C:minor") * @@ -67,6 +73,7 @@ import { signal } from '../core/signal.mjs'; * @name rotationAlpha * @return {Pattern} * @synonyms rotA, rotZ, rotationZ + * @group external_io * @example * n(rotationAlpha.segment(4).range(0,7)).scale("C:minor") * @@ -77,6 +84,7 @@ import { signal } from '../core/signal.mjs'; * @name rotationBeta * @return {Pattern} * @synonyms rotB, rotX, rotationX + * @group external_io * @example * n(rotationBeta.segment(4).range(0,7)).scale("C:minor") * @@ -87,6 +95,7 @@ import { signal } from '../core/signal.mjs'; * @name rotationGamma * @return {Pattern} * @synonyms rotG, rotY, rotationY + * @group external_io * @example * n(rotationGamma.segment(4).range(0,7)).scale("C:minor") * @@ -97,6 +106,7 @@ import { signal } from '../core/signal.mjs'; * @name orientationAlpha * @return {Pattern} * @synonyms oriA, oriZ, orientationZ + * @group external_io * @example * n(orientationAlpha.segment(4).range(0,7)).scale("C:minor") * @@ -107,6 +117,7 @@ import { signal } from '../core/signal.mjs'; * @name orientationBeta * @return {Pattern} * @synonyms oriB, oriX, orientationX + * @group external_io * @example * n(orientationBeta.segment(4).range(0,7)).scale("C:minor") * @@ -117,6 +128,7 @@ import { signal } from '../core/signal.mjs'; * @name orientationGamma * @return {Pattern} * @synonyms oriG, oriY, orientationY + * @group external_io * @example * n(orientationGamma.segment(4).range(0,7)).scale("C:minor") * @@ -127,6 +139,7 @@ import { signal } from '../core/signal.mjs'; * @name absoluteOrientationAlpha * @return {Pattern} * @synonyms absOriA, absOriZ, absoluteOrientationZ + * @group external_io * @example * n(absoluteOrientationAlpha.segment(4).range(0,7)).scale("C:minor") * @@ -137,6 +150,7 @@ import { signal } from '../core/signal.mjs'; * @name absoluteOrientationBeta * @return {Pattern} * @synonyms absOriB, absOriX, absoluteOrientationX + * @group external_io * @example * n(absoluteOrientationBeta.segment(4).range(0,7)).scale("C:minor") * @@ -147,6 +161,7 @@ import { signal } from '../core/signal.mjs'; * @name absoluteOrientationGamma * @return {Pattern} * @synonyms absOriG, absOriY, absoluteOrientationY + * @group external_io * @example * n(absoluteOrientationGamma.segment(4).range(0,7)).scale("C:minor") * diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index 6007667fa..a7b4702c4 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -17,13 +17,13 @@ const availableFunctions = (() => { seen.add(s); // Swap `doc.name` in for `s` in the list of synonyms const synonymsWithDoc = [doc.name, ...synonyms].filter((x) => x && x !== s); - functions.push({ - ...doc, - name: s, // update names for the synonym - longname: s, - synonyms: synonymsWithDoc, - synonyms_text: synonymsWithDoc.join(', '), - }); + // functions.push({ + // ...doc, + // name: s, // update names for the synonym + // longname: s, + // synonyms: synonymsWithDoc, + // synonyms_text: synonymsWithDoc.join(', '), + // }); } } return functions.sort((a, b) => /* a.meta.filename.localeCompare(b.meta.filename) + */ a.name.localeCompare(b.name)); @@ -52,6 +52,19 @@ export function Reference() { }); }, [search]); + const visibleFunctionsByGroup = (() => { + const groups = {}; + for (const doc of visibleFunctions) { + const group = doc.group || 'Ungrouped'; + if (!groups[group]) { + groups[group] = []; + } + groups[group].push(doc); + } + return groups; + })(); + console.log(visibleFunctionsByGroup); + return (
@@ -59,18 +72,25 @@ export function Reference() {
- {visibleFunctions.map((entry, i) => ( - { - const el = document.getElementById(`doc-${i}`); - const container = document.getElementById('reference-container'); - container.scrollTo(0, el.offsetTop); - }} - > - {entry.name} {/* {entry.meta.filename} */} - + {Object.entries(visibleFunctionsByGroup).map(([groupName, groupEntries]) => ( + <> +

+ {groupName} +

+ {groupEntries.map((entry, i) => ( + { + const el = document.getElementById(`doc-${entry.name}`); + const container = document.getElementById('reference-container'); + container.scrollTo(0, el.offsetTop); + }} + > + {entry.name} {/* {entry.meta.filename} */} + + ))} + ))}
@@ -86,7 +106,7 @@ export function Reference() {

{visibleFunctions.map((entry, i) => (
-

{entry.name}

+

{entry.name}

{!!entry.synonyms_text && (

Synonyms: {entry.synonyms_text} From c754dab1ef257b8873c59380eb9e5e4b5e7b5d9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Wed, 1 Oct 2025 22:31:03 +0200 Subject: [PATCH 002/124] Order groups --- packages/supradough/dough.mjs | 1 + .../src/repl/components/panel/Reference.jsx | 30 +++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/supradough/dough.mjs b/packages/supradough/dough.mjs index 1cb9241ad..7eeb7a507 100644 --- a/packages/supradough/dough.mjs +++ b/packages/supradough/dough.mjs @@ -18,6 +18,7 @@ function applyGainCurve(val) { * @param {number} a - Signal A (can be a single value or an array value in buffer processing). * @param {number} b - Signal B (can be a single value or an array value in buffer processing). * @param {number} m - Crossfade parameter (0.0 = all A, 1.0 = all B, 0.5 = equal mix). + * @group effects * @returns {number} Crossfaded output value. */ function crossfade(a, b, m) { diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index a7b4702c4..aa0453c3a 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -35,6 +35,14 @@ const getInnerText = (html) => { return div.textContent || div.innerText || ''; }; +const GROUP_DISPLAY_NAMES = { + external_io: 'External I/O', + effects: 'Effects', + ungrouped: 'Ungrouped', +}; + +const GROUP_ORDER = ['effects', 'external_io', 'ungrouped']; + export function Reference() { const [search, setSearch] = useState(''); @@ -55,7 +63,7 @@ export function Reference() { const visibleFunctionsByGroup = (() => { const groups = {}; for (const doc of visibleFunctions) { - const group = doc.group || 'Ungrouped'; + const group = doc.group || 'ungrouped'; if (!groups[group]) { groups[group] = []; } @@ -63,7 +71,17 @@ export function Reference() { } return groups; })(); - console.log(visibleFunctionsByGroup); + // console.log(visibleFunctionsByGroup); + + // Sort and map group entries + const sortedGroups = Object.entries(visibleFunctionsByGroup).sort(([a], [b]) => { + const ai = GROUP_ORDER.indexOf(a); + const bi = GROUP_ORDER.indexOf(b); + if (ai === -1 && bi === -1) return a.localeCompare(b); + if (ai === -1) return 1; + if (bi === -1) return -1; + return ai - bi; + }); return (

@@ -72,14 +90,14 @@ export function Reference() {
- {Object.entries(visibleFunctionsByGroup).map(([groupName, groupEntries]) => ( + {sortedGroups.map(([groupId, groupEntries]) => ( <> -

- {groupName} +

+ {GROUP_DISPLAY_NAMES[groupId] || groupId}

{groupEntries.map((entry, i) => ( { const el = document.getElementById(`doc-${entry.name}`); From 4a395f372dc60d77ae77d63b1608f0c63157f5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Wed, 1 Oct 2025 23:45:48 +0200 Subject: [PATCH 003/124] Annote pattern.mjs and signal.mjs --- jsdoc/jsdoc-synonyms.js | 1 - packages/core/pattern.mjs | 146 +++++++++++++++++- packages/core/signal.mjs | 52 +++++++ .../src/repl/components/panel/Reference.jsx | 6 +- 4 files changed, 198 insertions(+), 7 deletions(-) diff --git a/jsdoc/jsdoc-synonyms.js b/jsdoc/jsdoc-synonyms.js index aed0dcc06..423c7ad6e 100644 --- a/jsdoc/jsdoc-synonyms.js +++ b/jsdoc/jsdoc-synonyms.js @@ -17,7 +17,6 @@ function defineTags(dictionary) { mustHaveValue: true, onTagged: function (doclet, tag) { doclet.group = tag.value; - console.log('TAGGED GROUP:', doclet.group); }, }); } diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index d0be3f599..e3d4f5f9c 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -84,6 +84,7 @@ export class Pattern { /** * Returns a new pattern, with the function applied to the value of * each hap. It has the alias `fmap`. + * @group functional * @synonyms fmap * @param {Function} func to to apply to the value * @returns Pattern @@ -116,6 +117,7 @@ export class Pattern { * Assumes 'this' is a pattern of functions, and given a function to * resolve wholes, applies a given pattern of values to that * pattern of functions. + * @group functional * @param {Function} whole_func * @param {Function} func * @noAutocomplete @@ -153,6 +155,7 @@ export class Pattern { * In this `_appBoth` variant, where timespans of the function and value haps * are not the same but do intersect, the resulting hap has a timespan of the * intersection. This applies to both the part and the whole timespan. + * @group functional * @param {Pattern} pat_val * @noAutocomplete * @returns Pattern @@ -180,6 +183,7 @@ export class Pattern { * on. In practice, this means that the pattern structure, including onsets, * are preserved from the pattern of functions (often referred to as the left * hand or inner pattern). + * @group functional * @param {Pattern} pat_val * @noAutocomplete * @returns Pattern @@ -213,6 +217,7 @@ export class Pattern { * As with `appLeft`, but `whole` timespans are instead taken from the * pattern of values, i.e. structure is preserved from the right hand/outer * pattern. + * @group functional * @param {Pattern} pat_val * @noAutocomplete * @returns Pattern @@ -403,6 +408,7 @@ export class Pattern { /** * Query haps inside the given time span. * + * @group internals * @param {Fraction | number} begin from time * @param {Fraction | number} end to time * @returns Hap[] @@ -426,6 +432,7 @@ export class Pattern { * Returns a new pattern, with queries split at cycle boundaries. This makes * some calculations easier to express, as all haps are then constrained to * happen within a cycle. + * @group internals * @returns Pattern * @noAutocomplete */ @@ -440,6 +447,7 @@ export class Pattern { /** * Returns a new pattern, where the given function is applied to the query * timespan before passing it to the original pattern. + * @group internals * @param {Function} func the function to apply * @returns Pattern * @noAutocomplete @@ -462,6 +470,7 @@ export class Pattern { /** * As with `withQuerySpan`, but the function is applied to both the * begin and end time of the query timespan. + * @group internals * @param {Function} func the function to apply * @returns Pattern * @noAutocomplete @@ -474,6 +483,7 @@ export class Pattern { * Similar to `withQuerySpan`, but the function is applied to the timespans * of all haps returned by pattern queries (both `part` timespans, and where * present, `whole` timespans). + * @group internals * @param {Function} func * @returns Pattern * @noAutocomplete @@ -485,6 +495,7 @@ export class Pattern { /** * As with `withHapSpan`, but the function is applied to both the * begin and end time of the hap timespans. + * @group internals * @param {Function} func the function to apply * @returns Pattern * @noAutocomplete @@ -495,6 +506,7 @@ export class Pattern { /** * Returns a new pattern with the given function applied to the list of haps returned by every query. + * @group internals * @param {Function} func * @returns Pattern * @noAutocomplete @@ -507,6 +519,7 @@ export class Pattern { /** * As with `withHaps`, but applies the function to every hap, rather than every list of haps. + * @group internals * @param {Function} func * @returns Pattern * @noAutocomplete @@ -517,6 +530,7 @@ export class Pattern { /** * Returns a new pattern with the context field set to every hap set to the given value. + * @group internals * @param {*} context * @returns Pattern * @noAutocomplete @@ -527,6 +541,7 @@ export class Pattern { /** * Returns a new pattern with the given function applied to the context field of every hap. + * @group internals * @param {Function} func * @returns Pattern * @noAutocomplete @@ -542,6 +557,7 @@ export class Pattern { /** * Returns a new pattern with the context field of every hap set to an empty object. + * @group internals * @returns Pattern * @noAutocomplete */ @@ -552,6 +568,7 @@ export class Pattern { /** * Returns a new pattern with the given location information added to the * context of every hap. + * @group internals * @param {Number} start start offset * @param {Number} end end offset * @returns Pattern @@ -575,6 +592,7 @@ export class Pattern { /** * Returns a new Pattern, which only returns haps that meet the given test. + * @group internals * @param {Function} hap_test - a function which returns false for haps to be removed from the pattern * @returns Pattern * @noAutocomplete @@ -586,6 +604,7 @@ export class Pattern { /** * As with `filterHaps`, but the function is applied to values * inside haps. + * @group internals * @param {Function} value_test * @returns Pattern * @noAutocomplete @@ -597,6 +616,7 @@ export class Pattern { /** * Returns a new pattern, with haps containing undefined values removed from * query results. + * @group internals * @returns Pattern * @noAutocomplete */ @@ -608,6 +628,7 @@ export class Pattern { * Returns a new pattern, with all haps without onsets filtered out. A hap * with an onset is one with a `whole` timespan that begins at the same time * as its `part` timespan. + * @group internals * @returns Pattern * @noAutocomplete */ @@ -621,6 +642,7 @@ export class Pattern { /** * Returns a new pattern, with 'continuous' haps (those without 'whole' * timespans) removed from query results. + * @group internals * @returns Pattern * @noAutocomplete */ @@ -632,6 +654,7 @@ export class Pattern { /** * Combines adjacent haps with the same value and whole. Only * intended for use in tests. + * @group internals * @noAutocomplete */ defragmentHaps() { @@ -684,6 +707,7 @@ export class Pattern { /** * Queries the pattern for the first cycle, returning Haps. Mainly of use when * debugging a pattern. + * @group internals * @param {Boolean} with_context - set to true, otherwise the context field * will be stripped from the resulting haps. * @returns [Hap] @@ -699,6 +723,7 @@ export class Pattern { /** * Accessor for a list of values returned by querying the first cycle. + * @group internals * @noAutocomplete */ get firstCycleValues() { @@ -707,6 +732,7 @@ export class Pattern { /** * More human-readable version of the `firstCycleValues` accessor. + * @group internals * @noAutocomplete */ get showFirstCycle() { @@ -718,6 +744,7 @@ export class Pattern { /** * Returns a new pattern, which returns haps sorted in temporal order. Mainly * of use when comparing two patterns for equality, in tests. + * @group internals * @returns Pattern * @noAutocomplete */ @@ -732,6 +759,10 @@ export class Pattern { ); } + /** + * Returns a new pattern with all values parsed as numerals. + * @group internals + */ asNumber() { return this.fmap(parseNumeral); } @@ -782,6 +813,7 @@ export class Pattern { /** * Layers the result of the given function(s). Like `superimpose`, but without the original pattern: * @name layer + * @group combiners * @memberof Pattern * @synonyms apply * @returns Pattern @@ -797,6 +829,7 @@ export class Pattern { /** * Superimposes the result of the given function(s) on top of the original pattern: * @name superimpose + * @group combiners * @memberof Pattern * @returns Pattern * @example @@ -856,6 +889,7 @@ export class Pattern { /** * Writes the content of the current event to the console (visible in the side menu). + * @group visualizers * @name log * @memberof Pattern * @example @@ -870,6 +904,7 @@ export class Pattern { /** * A simplified version of `log` which writes all "values" (various configurable parameters) * within the event to the console (visible in the side menu). + * @group visualizers * @name logValues * @memberof Pattern * @example @@ -903,6 +938,7 @@ export class Pattern { * source pattern to be looped, and for an (optional) given function to be * applied. False values result in the corresponding part of the source pattern * to be played unchanged. + * @group structure * @name into * @memberof Pattern * @example @@ -942,6 +978,7 @@ Pattern.prototype.collect = function () { /** * Selects indices in in stacked notes. + * @group transforms * @example * note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>") * .arpWith(haps => haps[2]) @@ -956,6 +993,7 @@ export const arpWith = register('arpWith', (func, pat) => { /** * Selects indices in in stacked notes. + * @group transforms * @example * note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>") * .arp("0 [0,2] 1 [0,2]") @@ -1038,6 +1076,7 @@ function _composeOp(a, b, func) { * note("c3 e3 g3".add("<0 5 7 0>")) * // Behind the scenes, the notes are converted to midi numbers: * // note("48 52 55".add("<0 5 7 0>")) + * @group transforms */ add: [numeralArgs((a, b) => a + b)], // support string concatenation /** @@ -1150,6 +1189,7 @@ function _composeOp(a, b, func) { /** * Applies the given structure to the pattern: * + * @group structure * @example * note("c,eb,g") * .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~") @@ -1164,6 +1204,7 @@ function _composeOp(a, b, func) { /** * Returns silence when mask is 0 or "~" * + * @group structure * @example * note("c [eb,g] d [eb,g]").mask("<1 [0 1]>") */ @@ -1176,6 +1217,7 @@ function _composeOp(a, b, func) { /** * Resets the pattern to the start of the cycle for each onset of the reset pattern. * + * @group structure * @example * s("[ sd]*2, hh*8").reset("") */ @@ -1189,6 +1231,7 @@ function _composeOp(a, b, func) { * Restarts the pattern for each onset of the restart pattern. * While reset will only reset the current cycle, restart will start from cycle 0. * + * @group structure * @example * s("[ sd]*2, hh*8").restart("") */ @@ -1229,6 +1272,7 @@ export const pm = polymeter; /** * Does absolutely nothing, but with a given metrical 'steps' * @name gap + * @group generators * @param {number} steps * @example * gap(3) // "~@3" @@ -1238,6 +1282,7 @@ export const gap = (steps) => new Pattern(() => [], steps); /** * Does absolutely nothing.. * @name silence + * @group generators * @example * silence // "~" */ @@ -1249,6 +1294,7 @@ export const nothing = gap(0); /** * A discrete value that repeats once per cycle. * + * @group generators * @returns {Pattern} * @example * pure('e4') // "e4" @@ -1290,7 +1336,10 @@ export function reify(thing) { return pure(thing); } -/** Takes a list of patterns, and returns a pattern of lists. +/** + * Takes a list of patterns, and returns a pattern of lists. + * + * @group transforms */ export function sequenceP(pats) { let result = pure([]); @@ -1303,6 +1352,7 @@ export function sequenceP(pats) { /** * The given items are played at the same time at the same length. * + * @group structure * @return {Pattern} * @synonyms polyrhythm, pr * @example @@ -1387,6 +1437,7 @@ export function stackBy(by, ...pats) { /** * Concatenation: combines a list of patterns, switching between them successively, one per cycle. * + * @group combiners * @return {Pattern} * @synonyms cat * @example @@ -1420,6 +1471,7 @@ export function slowcat(...pats) { } /** Concatenation: combines a list of patterns, switching between them successively, one per cycle. Unlike slowcat, this version will skip cycles. + * @group combiners * @param {...any} items - The items to concatenate * @return {Pattern} */ @@ -1435,6 +1487,7 @@ export function slowcatPrime(...pats) { /** The given items are con**cat**enated, where each one takes one cycle. * + * @group combiners * @param {...any} items - The items to concatenate * @synonyms slowcat * @return {Pattern} @@ -1456,6 +1509,7 @@ export function cat(...pats) { * Allows to arrange multiple patterns together over multiple cycles. * Takes a variable number of arrays with two elements specifying the number of cycles and the pattern to use. * + * @group combiners * @return {Pattern} * @example * arrange( @@ -1473,6 +1527,7 @@ export function arrange(...sections) { * Similarly to `arrange`, allows you to arrange multiple patterns together over multiple cycles. * Unlike `arrange`, you specify a start and stop time for each pattern rather than duration, which * means that patterns can overlap. + * @group combiners * @return {Pattern} * @example seqPLoop([0, 2, "bd(3,8)"], @@ -1517,6 +1572,7 @@ export function sequence(...pats) { } /** Like **cat**, but the items are crammed into one cycle. + * @group combiners * @synonyms sequence, fastcat * @example * seq("e5", "b4", ["d5", "c5"]).note() @@ -1690,6 +1746,7 @@ function stepRegister(name, func, patternify = true, preserveSteps = false, join * Assumes a numerical pattern. Returns a new pattern with all values rounded * to the nearest integer. * @name round + * @group math * @memberof Pattern * @returns Pattern * @example @@ -1698,13 +1755,13 @@ function stepRegister(name, func, patternify = true, preserveSteps = false, join export const round = register('round', function (pat) { return pat.asNumber().fmap((v) => Math.round(v)); }); - /** * Assumes a numerical pattern. Returns a new pattern with all values set to * their mathematical floor. E.g. `3.7` replaced with to `3`, and `-4.2` * replaced with `-5`. * @name floor * @memberof Pattern + * @group math * @returns Pattern * @example * note("42 42.1 42.5 43".floor()) @@ -1719,6 +1776,7 @@ export const floor = register('floor', function (pat) { * replaced with `-4`. * @name ceil * @memberof Pattern + * @group math * @returns Pattern * @example * note("42 42.1 42.5 43".ceil()) @@ -1729,6 +1787,7 @@ export const ceil = register('ceil', function (pat) { /** * Assumes a numerical pattern, containing unipolar values in the range 0 .. * 1. Returns a new pattern with values scaled to the bipolar range -1 .. 1 + * @group math * @returns Pattern * @noAutocomplete */ @@ -1739,6 +1798,7 @@ export const toBipolar = register('toBipolar', function (pat) { /** * Assumes a numerical pattern, containing bipolar values in the range -1 .. 1 * Returns a new pattern with values scaled to the unipolar range 0 .. 1 + * @group math * @returns Pattern * @noAutocomplete */ @@ -1752,6 +1812,7 @@ export const fromBipolar = register('fromBipolar', function (pat) { * Most useful in combination with continuous patterns. * @name range * @memberof Pattern + * @group math * @returns Pattern * @example * s("[bd sd]*2,hh*8") @@ -1767,6 +1828,7 @@ export const range = register('range', function (min, max, pat) { * following an exponential curve. * @name rangex * @memberof Pattern + * @group math * @returns Pattern * @example * s("[bd sd]*2,hh*8") @@ -1781,6 +1843,7 @@ export const rangex = register('rangex', function (min, max, pat) { * Returns a new pattern with values scaled to the given min/max range. * @name range2 * @memberof Pattern + * @group math * @returns Pattern * @example * s("[bd sd]*2,hh*8") @@ -1795,6 +1858,7 @@ export const range2 = register('range2', function (min, max, pat) { * Returns a new pattern with just numbers. * @name ratio * @memberof Pattern + * @group math * @returns Pattern * @example * ratio("1, 5:4, 3:2").mul(110) @@ -1813,6 +1877,7 @@ export const ratio = register('ratio', (pat) => // Structural and temporal transformations /** Compress each cycle into the given timespan, leaving a gap + * @group structure * @example * cat( * s("bd sd").compress(.25,.75), @@ -1834,6 +1899,7 @@ export const { compressSpan, compressspan } = register(['compressSpan', 'compres /** * speeds up a pattern like fast, but rather than it playing multiple times as fast would it instead leaves a gap in the remaining space of the cycle. For example, the following will play the sound pattern "bd sn" only once but compressed into the first half of the cycle, i.e. twice as fast. + * @group structure * @name fastGap * @synonyms fastgap * @example @@ -1871,6 +1937,7 @@ export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (f /** * Similar to `compress`, but doesn't leave gaps, and the 'focus' can be bigger than a cycle + * @group structure * @example * s("bd hh sd hh").focus(1/4, 3/4) */ @@ -1888,6 +1955,7 @@ export const { focusSpan, focusspan } = register(['focusSpan', 'focusspan'], fun }); /** The ply function repeats each event the given number of times. + * @group structure * @example * s("bd ~ sd cp").ply("<1 2 3>") */ @@ -1902,6 +1970,7 @@ export const ply = register('ply', function (factor, pat) { /** * Speed up a pattern by the given factor. Used by "*" in mini notation. * + * @group structure * @name fast * @synonyms density * @memberof Pattern @@ -1926,6 +1995,7 @@ export const { fast, density } = register( /** * Both speeds up the pattern (like 'fast') and the sample playback (like 'speed'). + * @group structure * @example * s("bd sd:2").hurry("<1 2 4 3>").slow(1.5) */ @@ -1936,6 +2006,7 @@ export const hurry = register('hurry', function (r, pat) { /** * Slow down a pattern over the given number of cycles. Like the "/" operator in mini notation. * + * @group structure * @name slow * @synonyms sparsity * @memberof Pattern @@ -1953,6 +2024,7 @@ export const { slow, sparsity } = register(['slow', 'sparsity'], function (facto /** * Carries out an operation 'inside' a cycle. + * @group structure * @example * "0 1 2 3 4 3 2 1".inside(4, rev).scale('C major').note() * // "0 1 2 3 4 3 2 1".slow(4).rev().fast(4).scale('C major').note() @@ -1963,6 +2035,7 @@ export const inside = register('inside', function (factor, f, pat) { /** * Carries out an operation 'outside' a cycle. + * @group structure * @example * "<[0 1] 2 [3 4] 5>".outside(4, rev).scale('C major').note() * // "<[0 1] 2 [3 4] 5>".fast(4).rev().slow(4).scale('C major').note() @@ -1973,6 +2046,7 @@ export const outside = register('outside', function (factor, f, pat) { /** * Applies the given function every n cycles, starting from the last cycle. + * @group structure * @name lastOf * @memberof Pattern * @param {number} n how many cycles @@ -1989,6 +2063,7 @@ export const lastOf = register('lastOf', function (n, func, pat) { /** * Applies the given function every n cycles, starting from the first cycle. + * @group structure * @name firstOf * @memberof Pattern * @param {number} n how many cycles @@ -2000,6 +2075,7 @@ export const lastOf = register('lastOf', function (n, func, pat) { /** * An alias for `firstOf` + * @group structure * @name every * @memberof Pattern * @param {number} n how many cycles @@ -2016,8 +2092,8 @@ export const { firstOf, every } = register(['firstOf', 'every'], function (n, fu /** * Like layer, but with a single function: + * @group structure * @name apply - * @memberof Pattern * @example * "".scale('C minor').apply(scaleTranspose("0,2,4")).note() */ @@ -2028,6 +2104,7 @@ export const apply = register('apply', function (func, pat) { /** * Plays the pattern at the given cycles per minute. + * @group structure * @deprecated * @example * s(",hh*2").cpm(90) // = 90 bpm @@ -2040,6 +2117,7 @@ export const cpm = register('cpm', function (cpm, pat) { /** * Nudge a pattern to start earlier in time. Equivalent of Tidal's <~ operator * + * @group structure * @name early * @memberof Pattern * @param {number | Pattern} cycles number of cycles to nudge left @@ -2060,6 +2138,7 @@ export const early = register( /** * Nudge a pattern to start later in time. Equivalent of Tidal's ~> operator * + * @group structure * @name late * @memberof Pattern * @param {number | Pattern} cycles number of cycles to nudge right @@ -2080,6 +2159,7 @@ export const late = register( /** * Plays a portion of a pattern, specified by the beginning and end of a time span. The new resulting pattern is played over the time period of the original pattern: * + * @group structure * @example * s("bd*2 hh*3 [sd bd]*2 perc").zoom(0.25, 0.75) * // s("hh*3 [sd bd]*2") // equivalent @@ -2106,6 +2186,7 @@ export const { zoomArc, zoomarc } = register(['zoomArc', 'zoomarc'], function (a /** * Splits a pattern into the given number of slices, and plays them according to a pattern of slice numbers. * Similar to `slice`, but slices up patterns rather than sound samples. + * @group structure * @param {number} number of slices * @param {number} slices to play * @example @@ -2133,6 +2214,7 @@ export const bite = register( /** * Selects the given fraction of the pattern and repeats that part to fill the remainder of the cycle. + * @group structure * @param {number} fraction fraction to select * @example * s("lt ht mt cp, [hh oh]*2").linger("<1 .5 .25 .125>") @@ -2153,6 +2235,7 @@ export const linger = register( /** * Samples the pattern at a rate of n events per cycle. Useful for turning a continuous pattern into a discrete one. + * @group structure * @name segment * @synonyms seg * @param {number} segments number of segments per cycle @@ -2165,6 +2248,7 @@ export const { segment, seg } = register(['segment', 'seg'], function (rate, pat /** * 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 + * @group structure * @param {number} subdivision * @param {number} offset * @example @@ -2174,6 +2258,7 @@ export const swingBy = register('swingBy', (swing, n, pat) => pat.inside(n, late /** * Shorthand for swingBy with 1/3: + * @group structure * @param {number} subdivision * @example * s("hh*8").swing(4) @@ -2183,6 +2268,7 @@ export const swing = register('swing', (n, pat) => pat.swingBy(1 / 3, n)); /** * Swaps 1s and 0s in a binary pattern. + * @group structure * @name invert * @synonyms inv * @example @@ -2200,6 +2286,7 @@ export const { invert, inv } = register( /** * Applies the given function whenever the given pattern is in a true state. + * @group structure * @name when * @memberof Pattern * @param {Pattern} binary_pat @@ -2214,6 +2301,7 @@ export const when = register('when', function (on, func, pat) { /** * Superimposes the function result on top of the original pattern, delayed by the given time. + * @group structure * @name off * @memberof Pattern * @param {Pattern | number} time offset time @@ -2230,6 +2318,7 @@ export const off = register('off', function (time_pat, func, pat) { * Returns a new pattern where every other cycle is played once, twice as * fast, and offset in time by one quarter of a cycle. Creates a kind of * breakbeat feel. + * @group structure * @returns Pattern */ export const brak = register('brak', function (pat) { @@ -2239,6 +2328,7 @@ export const brak = register('brak', function (pat) { /** * Reverse all haps in a pattern * + * @group structure * @name rev * @memberof Pattern * @returns Pattern @@ -2272,6 +2362,7 @@ export const rev = register( /** Like press, but allows you to specify the amount by which each * event is shifted. pressBy(0.5) is the same as press, while * pressBy(1/3) shifts each event by a third of its timespan. + * @group structure * @example * stack(s("hh*4"), * s("bd mt sd ht").pressBy("<0 0.5 0.25>") @@ -2283,6 +2374,7 @@ export const pressBy = register('pressBy', function (r, pat) { /** * Syncopates a rhythm, by shifting each event halfway into its timespan. + * @group structure * @example * stack(s("hh*4"), * s("bd mt sd ht").every(4, press) @@ -2294,6 +2386,7 @@ export const press = register('press', function (pat) { /** * Silences a pattern. + * @group structure * @example * stack( * s("bd").hush(), @@ -2306,6 +2399,7 @@ Pattern.prototype.hush = function () { /** * Applies `rev` to a pattern every other cycle, so that the pattern alternates between forwards and backwards. + * @group structure * @example * note("c d e g").palindrome() */ @@ -2320,6 +2414,7 @@ export const palindrome = register( /** * Jux with adjustable stereo width. 0 = mono, 1 = full stereo. + * @group structure * @name juxBy * @synonyms juxby * @example @@ -2341,6 +2436,7 @@ export const { juxBy, juxby } = register(['juxBy', 'juxby'], function (by, func, /** * The jux function creates strange stereo effects, by applying a function to a pattern, but only in the right-hand channel. + * @group structure * @example * s("bd lt [~ ht] mt cp ~ bd hh").jux(rev) * @example @@ -2354,6 +2450,7 @@ export const jux = register('jux', function (func, pat) { /** * Superimpose and offset multiple times, applying the given function each time. + * @group structure * @name echoWith * @synonyms echowith, stutWith, stutwith * @param {number} times how many times to repeat @@ -2373,6 +2470,7 @@ export const { echoWith, echowith, stutWith, stutwith } = register( /** * Superimpose and offset multiple times, gradually decreasing the velocity + * @group structure * @name echo * @memberof Pattern * @returns Pattern @@ -2388,6 +2486,7 @@ export const echo = register('echo', function (times, time, feedback, pat) { /** * Deprecated. Like echo, but the last 2 parameters are flipped. + * @group structure * @name stut * @param {number} times how many times to repeat * @param {number} feedback velocity multiplicator for each iteration @@ -2409,6 +2508,7 @@ export const applyN = register('applyN', function (n, func, p) { /** * The plyWith function repeats each event the given number of times, applying the given function to each event.\n + * @group structure * @name plyWith * @synonyms plywith * @param {number} factor how many times to repeat @@ -2431,6 +2531,7 @@ export const plyWith = register(['plyWith', 'plywith'], function (factor, func, /** * The plyForEach function repeats each event the given number of times, applying the given function to each event. * This version of ply uses the iteration index as an argument to the function, similar to echoWith. + * @group structure * @name plyForEach * @synonyms plyforeach * @param {number} factor how many times to repeat @@ -2452,6 +2553,7 @@ export const plyForEach = register(['plyForEach', 'plyforeach'], function (facto /** * Divides a pattern into a given number of subdivisions, plays the subdivisions in order, but increments the starting subdivision each cycle. The pattern wraps to the first subdivision after the last subdivision is played. + * @group structure * @name iter * @memberof Pattern * @returns Pattern @@ -2479,6 +2581,7 @@ export const iter = register( /** * Like `iter`, but plays the subdivisions in reverse order. Known as iter' in tidalcycles + * @group structure * @name iterBack * @synonyms iterback * @memberof Pattern @@ -2497,6 +2600,7 @@ export const { iterBack, iterback } = register( /** * Repeats each cycle the given number of times. + * @group structure * @name repeatCycles * @memberof Pattern * @returns Pattern @@ -2520,6 +2624,7 @@ export const { repeatCycles } = register( /** * Divides a pattern into a given number of parts, then cycles through those parts in turn, applying the given function to each part in turn (one part per cycle). + * @group structure * @name chunk * @synonyms slowChunk, slowchunk * @memberof Pattern @@ -2551,6 +2656,7 @@ export const { chunk, slowchunk, slowChunk } = register( /** * Like `chunk`, but cycles through the parts in reverse order. Known as chunk' in tidalcycles + * @group structure * @name chunkBack * @synonyms chunkback * @memberof Pattern @@ -2571,6 +2677,7 @@ export const { chunkBack, chunkback } = register( /** * Like `chunk`, but the cycles of the source pattern aren't repeated * for each set of chunks. + * @group structure * @name fastChunk * @synonyms fastchunk * @memberof Pattern @@ -2591,6 +2698,7 @@ export const { fastchunk, fastChunk } = register( /** * Like `chunk`, but the function is applied to a looped subcycle of the source pattern. + * @group structure * @name chunkInto * @synonyms chunkinto * @memberof Pattern @@ -2604,6 +2712,7 @@ export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], fun /** * Like `chunkInto`, but moves backwards through the chunks. + * @group structure * @name chunkBackInto * @synonyms chunkbackinto * @memberof Pattern @@ -2634,6 +2743,7 @@ export const bypass = register( /** * Loops the pattern inside an `offset` for `cycles`. * If you think of the entire span of time in cycles as a ribbon, you can cut a single piece and loop it. + * @group structure * @name ribbon * @synonyms rib * @param {number} offset start point of loop in cycles @@ -2662,6 +2772,7 @@ export const hsl = register('hsl', (h, s, l, pat) => { /** * Tags each Hap with an identifier. Good for filtering. The function populates Hap.context.tags (Array). * @name tag + * @group structure * @noAutocomplete * @param {string} tag anything unique */ @@ -2672,6 +2783,7 @@ Pattern.prototype.tag = function (tag) { /** * Filters haps using the given function * @name filter + * @group structure * @param {Function} test function to test Hap * @example * s("hh!7 oh").filter(hap => hap.value.s==='hh') @@ -2681,6 +2793,7 @@ export const filter = register('filter', (test, pat) => pat.withHaps((haps) => h /** * Filters haps by their begin time * @name filterWhen + * @group structure * @noAutocomplete * @param {Function} test function to test Hap.whole.begin */ @@ -2689,6 +2802,7 @@ export const filterWhen = register('filterWhen', (test, pat) => pat.filter((h) = /** * Use within to apply a function to only a part of a pattern. * @name within + * @group structure * @param {number} start start within cycle (0 - 1) * @param {number} end end within cycle (0 - 1). Must be > start * @param {Function} func function to be applied to the sub-pattern @@ -2763,6 +2877,7 @@ export function _match(span, hap_p) { * *Experimental* * * Speeds a pattern up or down, to fit to the given number of steps per cycle. + * @group structure * @example * sound("bd sd cp").pace(4) * // The same as sound("{bd sd cp}%4") or sound("*4") @@ -2804,6 +2919,7 @@ export function _polymeterListSteps(steps, ...args) { * *Experimental* * * Aligns the steps of the patterns, creating polymeters. The patterns are repeated until they all fit the cycle. For example, in the below the first pattern is repeated twice, and the second is repeated three times, to fit the lowest common multiple of six steps. + * @group structure * @synonyms pm * @example * // The same as note("{c eb g, c2 g2}%6") @@ -2836,6 +2952,7 @@ export function polymeter(...args) { * The steps can either be inferred from the pattern, or provided as a [length, pattern] pair. * Has the alias `timecat`. * @name stepcat + * @group combiners * @synonyms timeCat, timecat * @return {Pattern} * @example @@ -2893,6 +3010,7 @@ export function stepcat(...timepats) { * Concatenates patterns stepwise, according to an inferred 'steps per cycle'. * Similar to `stepcat`, but if an argument is a list, the whole pattern will alternate between the elements in the list. * + * @group combiners * @return {Pattern} * @example * stepalt(["bd cp", "mt"], "bd").sound() @@ -2919,6 +3037,7 @@ export function stepalt(...groups) { * * Takes the given number of steps from a pattern (dropping the rest). * A positive number will take steps from the start of a pattern, and a negative number from the end. + * @group structure * @return {Pattern} * @example * "bd cp ht mt".take("2").sound() @@ -2963,6 +3082,7 @@ export const take = stepRegister('take', function (i, pat) { * * Drops the given number of steps from a pattern. * A positive number will drop steps from the start of a pattern, and a negative number from the end. + * @group structure * @return {Pattern} * @example * "tha dhi thom nam".drop("1").sound().bank("mridangam") @@ -2991,6 +3111,7 @@ export const drop = stepRegister('drop', function (i, pat) { * `extend` is similar to `fast` in that it increases its density, but it also increases the step count * accordingly. So `stepcat("a b".extend(2), "c d")` would be the same as `"a b a b c d"`, whereas * `stepcat("a b".fast(2), "c d")` would be the same as `"[a b] [a b] c d"`. + * @group structure * @example * stepcat( * sound("bd bd - cp").extend(2), @@ -3005,6 +3126,7 @@ export const extend = stepRegister('extend', function (factor, pat) { * *Experimental* * * Expands the step size of the pattern by the given factor. + * @group structure * @example * sound("tha dhi thom nam").bank("mridangam").expand("3 2 1 1 2 3").pace(8) */ @@ -3016,6 +3138,7 @@ export const expand = stepRegister('expand', function (factor, pat) { * *Experimental* * * Contracts the step size of the pattern by the given factor. See also `expand`. + * @group structure * @example * sound("tha dhi thom nam").bank("mridangam").contract("3 2 1 1 2 3").pace(8) */ @@ -3070,6 +3193,7 @@ export const shrinklist = (amount, pat) => pat.shrinklist(amount); * Progressively shrinks the pattern by 'n' steps until there's nothing left, or if a second value is given (using mininotation list syntax with `:`), * that number of times. * A positive number will progressively drop steps from the start of a pattern, and a negative number from the end. + * @group structure * @return {Pattern} * @example * "tha dhi thom nam".shrink("1").sound() @@ -3109,6 +3233,7 @@ export const shrink = register( * Progressively grows the pattern by 'n' steps until the full pattern is played, or if a second value is given (using mininotation list syntax with `:`), * that number of times. * A positive number will progressively grow steps from the start of a pattern, and a negative number from the end. + * @group structure * @return {Pattern} * @example * "tha dhi thom nam".grow("1").sound() @@ -3148,7 +3273,8 @@ export const grow = register( * Inserts a pattern into a list of patterns. On the first repetition it will be inserted at the end of the list, then moved backwards through the list * on successive repetitions. The patterns are added together stepwise, with all repetitions taking place over a single cycle. Using `pace` to set the * number of steps per cycle is therefore usually recommended. - * + * + * @group combiners * @return {Pattern} * @example * "[c g]".tour("e f", "e f g", "g f e c").note() @@ -3175,6 +3301,7 @@ Pattern.prototype.tour = function (...many) { * 'zips' together the steps of the provided patterns. This can create a long repetition, taking place over a single, dense cycle. * Using `pace` to set the number of steps per cycle is therefore usually recommended. * + * @group combiners * @returns {Pattern} * @example * zip("e f", "e f g", "g [f e] a f4 c").note() @@ -3226,6 +3353,7 @@ Pattern.prototype.steps = Pattern.prototype.pace; * Cuts each sample into the given number of parts, allowing you to explore a technique known as 'granular synthesis'. * It turns a pattern of samples into a pattern of parts of samples. * @name chop + * @group transforms * @memberof Pattern * @returns Pattern * @example @@ -3256,6 +3384,7 @@ export const chop = register('chop', function (n, pat) { /** * Cuts each sample into the given number of parts, triggering progressive portions of each sample at each loop. * @name striate + * @group transforms * @memberof Pattern * @returns Pattern * @example @@ -3274,6 +3403,7 @@ export const striate = register('striate', function (n, pat) { /** * Makes the sample fit the given number of cycles by changing the speed. * @name loopAt + * @group transforms * @memberof Pattern * @returns Pattern * @example @@ -3292,6 +3422,7 @@ const _loopAt = function (factor, pat, cps = 0.5) { * Chops samples into the given number of slices, triggering those slices with a given pattern of slice numbers. * Instead of a number, it also accepts a list of numbers from 0 to 1 to slice at specific points. * @name slice + * @group transforms * @memberof Pattern * @returns Pattern * @example @@ -3327,6 +3458,7 @@ export const slice = register( * make something happen on event time * uses browser timeout which is innacurate for audio tasks * @name onTriggerTime + * @group external_io * @memberof Pattern * @returns Pattern * @example @@ -3344,6 +3476,7 @@ Pattern.prototype.onTriggerTime = function (func) { /** * Works the same as slice, but changes the playback speed of each slice to match the duration of its step. * @name splice + * @group transforms * @example * samples('github:tidalcycles/dirt-samples') * s("breaks165") @@ -3381,6 +3514,7 @@ export const { loopAt, loopat } = register(['loopAt', 'loopat'], function (facto * Makes the sample fit its event duration. Good for rhythmical loops like drum breaks. * Similar to `loopAt`. * @name fit + * @group transforms * @example * samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' }) * s("rhodes/2").fit() @@ -3406,6 +3540,7 @@ export const fit = register('fit', (pat) => * given by a global clock and this function will be * deprecated/removed. * @name loopAtCps + * @group transforms * @memberof Pattern * @returns Pattern * @example @@ -3432,6 +3567,7 @@ let fadeGain = (p) => (p < 0.5 ? 1 : 1 - (p - 0.5) / 0.5); * - 1 = (no left, full right) * * @name xfade + * @group combiners * @example * xfade(s("bd*2"), "<0 .25 .5 .75 1>", s("hh*8")) */ @@ -3453,6 +3589,7 @@ Pattern.prototype.xfade = function (pos, b) { * creates a structure pattern from divisions of a cycle * especially useful for creating rhythms * @name beat + * @group structure * @example * s("bd").beat("0,7,10", 16) * @example @@ -3529,6 +3666,7 @@ export const _morph = (from, to, by) => { * sine.slow(8) // slowly morph between the rhythms * ) * ) + * @group structure */ export const morph = (frompat, topat, bypat) => { frompat = reify(frompat); diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 6bd6fde67..9f6636c84 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -24,6 +24,7 @@ export const signal = (func) => { * A sawtooth signal between 0 and 1. * * @return {Pattern} + * @group generators * @example * note("*8") * .clip(saw.slow(2)) @@ -38,6 +39,7 @@ export const saw = signal((t) => t % 1); * A sawtooth signal between -1 and 1 (like `saw`, but bipolar). * * @return {Pattern} + * @group generators */ export const saw2 = saw.toBipolar(); @@ -45,6 +47,7 @@ export const saw2 = saw.toBipolar(); * A sawtooth signal between 1 and 0 (like `saw`, but flipped). * * @return {Pattern} + * @group generators * @example * note("*8") * .clip(isaw.slow(2)) @@ -59,6 +62,7 @@ export const isaw = signal((t) => 1 - (t % 1)); * A sawtooth signal between 1 and -1 (like `saw2`, but flipped). * * @return {Pattern} + * @group generators */ export const isaw2 = isaw.toBipolar(); @@ -66,12 +70,14 @@ export const isaw2 = isaw.toBipolar(); * A sine signal between -1 and 1 (like `sine`, but bipolar). * * @return {Pattern} + * @group generators */ export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t)); /** * A sine signal between 0 and 1. * @return {Pattern} + * @group generators * @example * n(sine.segment(16).range(0,15)) * .scale("C:minor") @@ -83,6 +89,7 @@ export const sine = sine2.fromBipolar(); * A cosine signal between 0 and 1. * * @return {Pattern} + * @group generators * @example * n(stack(sine,cosine).segment(16).range(0,15)) * .scale("C:minor") @@ -94,12 +101,14 @@ export const cosine = sine._early(Fraction(1).div(4)); * A cosine signal between -1 and 1 (like `cosine`, but bipolar). * * @return {Pattern} + * @group generators */ export const cosine2 = sine2._early(Fraction(1).div(4)); /** * A square signal between 0 and 1. * @return {Pattern} + * @group generators * @example * n(square.segment(4).range(0,7)).scale("C:minor") * @@ -110,6 +119,7 @@ export const square = signal((t) => Math.floor((t * 2) % 2)); * A square signal between -1 and 1 (like `square`, but bipolar). * * @return {Pattern} + * @group generators */ export const square2 = square.toBipolar(); @@ -117,6 +127,7 @@ export const square2 = square.toBipolar(); * A triangle signal between 0 and 1. * * @return {Pattern} + * @group generators * @example * n(tri.segment(8).range(0,7)).scale("C:minor") * @@ -127,6 +138,7 @@ export const tri = fastcat(saw, isaw); * A triangle signal between -1 and 1 (like `tri`, but bipolar). * * @return {Pattern} + * @group generators */ export const tri2 = fastcat(saw2, isaw2); @@ -134,6 +146,7 @@ export const tri2 = fastcat(saw2, isaw2); * An inverted triangle signal between 1 and 0 (like `tri`, but flipped). * * @return {Pattern} + * @group generators * @example * n(itri.segment(8).range(0,7)).scale("C:minor") * @@ -144,6 +157,7 @@ export const itri = fastcat(isaw, saw); * An inverted triangle signal between -1 and 1 (like `itri`, but bipolar). * * @return {Pattern} + * @group generators */ export const itri2 = fastcat(isaw2, saw2); @@ -151,6 +165,7 @@ export const itri2 = fastcat(isaw2, saw2); * A signal representing the cycle time. * * @return {Pattern} + * @group generators */ export const time = signal(id); @@ -158,6 +173,7 @@ export const time = signal(id); * The mouse's x position value ranges from 0 to 1. * @name mousex * @return {Pattern} + * @group external_io * @example * n(mousex.segment(4).range(0,7)).scale("C:minor") * @@ -167,6 +183,7 @@ export const time = signal(id); * The mouse's y position value ranges from 0 to 1. * @name mousey * @return {Pattern} + * @group external_io * @example * n(mousey.segment(4).range(0,7)).scale("C:minor") * @@ -221,6 +238,7 @@ const timeToRands = (t, n) => timeToRandsPrime(timeToIntSeed(t), n); /** * A discrete pattern of numbers from 0 to n-1 + * @group generators * @example * n(run(4)).scale("C4:pentatonic") * // n("0 1 2 3").scale("C4:pentatonic") @@ -231,6 +249,7 @@ export const run = (n) => saw.range(0, n).round().segment(n); * Creates a pattern from a binary number. * * @name binary + * @group generators * @param {number} n - input number to convert to binary * @example * "hh".s().struct(binary(5)) @@ -245,6 +264,7 @@ export const binary = (n) => { * Creates a pattern from a binary number, padded to n bits long. * * @name binaryN + * @group generators * @param {number} n - input number to convert to binary * @param {number} nBits - pattern length, defaults to 16 * @example @@ -280,6 +300,7 @@ const _rearrangeWith = (ipat, n, pat) => { * Slices a pattern into the given number of parts, then plays those parts in random order. * Each part will be played exactly once per cycle. * @name shuffle + * @group transforms * @example * note("c d e f").sound("piano").shuffle(4) * @example @@ -293,6 +314,7 @@ export const shuffle = register('shuffle', (n, pat) => { * Slices a pattern into the given number of parts, then plays those parts at random. Similar to `shuffle`, * but parts might be played more than once, or not at all, per cycle. * @name scramble + * @group transforms * @example * note("c d e f").sound("piano").scramble(4) * @example @@ -306,6 +328,7 @@ export const scramble = register('scramble', (n, pat) => { * A continuous pattern of random numbers, between 0 and 1. * * @name rand + * @group generators * @example * // randomly change the cutoff * s("bd*4,hh*8").cutoff(rand.range(500,8000)) @@ -323,6 +346,7 @@ export const _brandBy = (p) => rand.fmap((x) => x < p); * A continuous pattern of 0 or 1 (binary random), with a probability for the value being 1 * * @name brandBy + * @group generators * @param {number} probability - a number between 0 and 1 * @example * s("hh*10").pan(brandBy(0.2)) @@ -333,6 +357,7 @@ export const brandBy = (pPat) => reify(pPat).fmap(_brandBy).innerJoin(); * A continuous pattern of 0 or 1 (binary random) * * @name brand + * @group generators * @example * s("hh*10").pan(brand) */ @@ -344,6 +369,7 @@ export const _irand = (i) => rand.fmap((x) => Math.trunc(x * i)); * A continuous pattern of random integers, between 0 and n-1. * * @name irand + * @group generators * @param {number} n max value (exclusive) * @example * // randomly select scale notes from 0 - 7 (= C to C) @@ -366,6 +392,7 @@ export const __chooseWith = (pat, xs) => { /** * Choose from the list of values (or patterns of values) using the given * pattern of numbers, which should be in the range of 0..1 + * @group transforms * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -379,6 +406,7 @@ export const chooseWith = (pat, xs) => { /** * As with {chooseWith}, but the structure comes from the chosen values, rather * than the pattern you're using to choose with. + * @group transforms * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -389,6 +417,7 @@ export const chooseInWith = (pat, xs) => { /** * Chooses randomly from the given list of elements. + * @group transforms * @param {...any} xs values / patterns to choose from. * @returns {Pattern} - a continuous pattern. * @example @@ -404,6 +433,7 @@ export const chooseOut = choose; * Chooses from the given list of values (or patterns of values), according * to the pattern that the method is called on. The pattern should be in * the range 0 .. 1. + * @group transforms * @param {...any} xs * @returns {Pattern} */ @@ -414,6 +444,7 @@ Pattern.prototype.choose = function (...xs) { /** * As with choose, but the pattern that this method is called on should be * in the range -1 .. 1 + * @group transforms * @param {...any} xs * @returns {Pattern} */ @@ -423,6 +454,7 @@ Pattern.prototype.choose2 = function (...xs) { /** * Picks one of the elements at random each cycle. + * @group transforms * @synonyms randcat * @returns {Pattern} * @example @@ -465,6 +497,7 @@ const wchooseWith = (...args) => _wchooseWith(...args).outerJoin(); /** * Chooses randomly from the given list of elements by giving a probability to each element + * @group transforms * @param {...any} pairs arrays of value and weight * @returns {Pattern} - a continuous pattern. * @example @@ -474,6 +507,7 @@ export const wchoose = (...pairs) => wchooseWith(rand, ...pairs); /** * Picks one of the elements at random each cycle by giving a probability to each element + * @group transforms * @synonyms wrandcat * @returns {Pattern} * @example @@ -521,6 +555,7 @@ export const berlinWith = (tpat) => { /** * Generates a continuous pattern of [perlin noise](https://en.wikipedia.org/wiki/Perlin_noise), in the range 0..1. * + * @group generators * @name perlin * @example * // randomly change the cutoff @@ -533,6 +568,7 @@ export const perlin = perlinWith(time.fmap((v) => Number(v))); * Generates a continuous pattern of [berlin noise](conceived by Jame Coyne and Jade Rowland as a joke but turned out to be surprisingly cool and useful, * like perlin noise but with sawtooth waves), in the range 0..1. * + * @group generators * @name berlin * @example * // ascending arpeggios @@ -553,6 +589,7 @@ export const degradeByWith = register( * 0 = 0% chance of removal * 1 = 100% chance of removal * + * @group transforms * @name degradeBy * @memberof Pattern * @param {number} amount - a number between 0 and 1 @@ -578,6 +615,7 @@ export const degradeBy = register( * * Randomly removes 50% of events from the pattern. Shorthand for `.degradeBy(0.5)` * + * @group transforms * @name degrade * @memberof Pattern * @returns Pattern @@ -594,6 +632,7 @@ export const degrade = register('degrade', (pat) => pat._degradeBy(0.5), true, t * 1 = 0% chance of removal * Events that would be removed by degradeBy are let through by undegradeBy and vice versa (see second example). * + * @group transforms * @name undegradeBy * @memberof Pattern * @param {number} amount - a number between 0 and 1 @@ -622,6 +661,7 @@ export const undegradeBy = register( * Inverse of `degrade`: Randomly removes 50% of events from the pattern. Shorthand for `.undegradeBy(0.5)` * Events that would be removed by degrade are let through by undegrade and vice versa (see second example). * + * @group transforms * @name undegrade * @memberof Pattern * @returns Pattern @@ -640,6 +680,7 @@ export const undegrade = register('undegrade', (pat) => pat._undegradeBy(0.5), t * Randomly applies the given function by the given probability. * Similar to `someCyclesBy` * + * @group transforms * @name sometimesBy * @memberof Pattern * @param {number | Pattern} probability - a number between 0 and 1 @@ -659,6 +700,7 @@ export const sometimesBy = register('sometimesBy', function (patx, func, pat) { * * Applies the given function with a 50% chance * + * @group transforms * @name sometimes * @memberof Pattern * @param {function} function - the transformation to apply @@ -680,6 +722,7 @@ export const sometimes = register('sometimes', function (func, pat) { * @param {number | Pattern} probability - a number between 0 and 1 * @param {function} function - the transformation to apply * @returns Pattern + * @group transforms * @example * s("bd,hh*8").someCyclesBy(.3, x=>x.speed("0.5")) */ @@ -702,6 +745,7 @@ export const someCyclesBy = register('someCyclesBy', function (patx, func, pat) * @name someCycles * @memberof Pattern * @returns Pattern + * @group transforms * @example * s("bd,hh*8").someCycles(x=>x.speed("0.5")) */ @@ -716,6 +760,7 @@ export const someCycles = register('someCycles', function (func, pat) { * @name often * @memberof Pattern * @returns Pattern + * @group transforms * @example * s("hh*8").often(x=>x.speed("0.5")) */ @@ -730,6 +775,7 @@ export const often = register('often', function (func, pat) { * @name rarely * @memberof Pattern * @returns Pattern + * @group transforms * @example * s("hh*8").rarely(x=>x.speed("0.5")) */ @@ -741,6 +787,7 @@ export const rarely = register('rarely', function (func, pat) { * * Shorthand for `.sometimesBy(0.1, fn)` * + * @group transforms * @name almostNever * @memberof Pattern * @returns Pattern @@ -755,6 +802,7 @@ export const almostNever = register('almostNever', function (func, pat) { * * Shorthand for `.sometimesBy(0.9, fn)` * + * @group transforms * @name almostAlways * @memberof Pattern * @returns Pattern @@ -769,6 +817,7 @@ export const almostAlways = register('almostAlways', function (func, pat) { * * Shorthand for `.sometimesBy(0, fn)` (never calls fn) * + * @group transforms * @name never * @memberof Pattern * @returns Pattern @@ -783,6 +832,7 @@ export const never = register('never', function (_, pat) { * * Shorthand for `.sometimesBy(1, fn)` (always calls fn) * + * @group transforms * @name always * @memberof Pattern * @returns Pattern @@ -811,6 +861,7 @@ export function _keyDown(keyname) { * Do something on a keypress, or array of keypresses * [Key name reference](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values) * + * @group external_io * @name whenKey * @memberof Pattern * @returns Pattern @@ -827,6 +878,7 @@ export const whenKey = register('whenKey', function (input, func, pat) { * returns true when a key or array of keys is held * [Key name reference](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values) * + * @group external_io * @name keyDown * @memberof Pattern * @returns Pattern diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index aa0453c3a..552551b5c 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -39,9 +39,11 @@ const GROUP_DISPLAY_NAMES = { external_io: 'External I/O', effects: 'Effects', ungrouped: 'Ungrouped', + structure: 'Structure', + transforms: 'Transforms', }; -const GROUP_ORDER = ['effects', 'external_io', 'ungrouped']; +const GROUP_ORDER = ['effects', 'transforms', 'structure', 'ungrouped', 'external_io']; export function Reference() { const [search, setSearch] = useState(''); @@ -93,7 +95,7 @@ export function Reference() { {sortedGroups.map(([groupId, groupEntries]) => ( <>

- {GROUP_DISPLAY_NAMES[groupId] || groupId} + {GROUP_DISPLAY_NAMES[groupId] || groupId} ({groupEntries.length})

{groupEntries.map((entry, i) => (
Date: Thu, 2 Oct 2025 21:59:45 +0200 Subject: [PATCH 004/124] Annotate controls.mjs --- packages/core/controls.mjs | 162 ++++++++++++++++++++++++++++++ packages/superdough/reverbGen.mjs | 4 +- 2 files changed, 165 insertions(+), 1 deletion(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 7e3c2a8e2..e43af8967 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -77,6 +77,7 @@ export function registerControl(names, ...aliases) { * separated by ':'. * * @name s + * @group samples * @param {string | Pattern} sound The sound / pattern of sounds to pick * @synonyms sound * @example @@ -91,6 +92,7 @@ export const { s, sound } = registerControl(['s', 'n', 'gain'], 'sound'); * Position in the wavetable of the wavetable oscillator * * @name wt + * @group effects * @param {number | Pattern} position Position in the wavetable from 0 to 1 * @synonyms wavetablePosition * @example @@ -102,6 +104,7 @@ export const { wt, wavetablePosition } = registerControl('wt', 'wavetablePositio * Amount of envelope applied wavetable oscillator's position envelope * * @name wtenv + * @group effects * @param {number | Pattern} amount between 0 and 1 */ export const { wtenv } = registerControl('wtenv'); @@ -109,6 +112,7 @@ export const { wtenv } = registerControl('wtenv'); * Attack time of the wavetable oscillator's position envelope * * @name wtattack + * @group effects * @synonyms wtatt * @param {number | Pattern} time attack time in seconds */ @@ -118,6 +122,7 @@ export const { wtattack, wtatt } = registerControl('wtattack', 'wtatt'); * Decay time of the wavetable oscillator's position envelope * * @name wtdecay + * @group effects * @synonyms wtdec * @param {number | Pattern} time decay time in seconds */ @@ -127,6 +132,7 @@ export const { wtdecay, wtdec } = registerControl('wtdecay', 'wtdec'); * Sustain time of the wavetable oscillator's position envelope * * @name wtsustain + * @group effects * @synonyms wtsus * @param {number | Pattern} gain sustain level (0 to 1) */ @@ -136,6 +142,7 @@ export const { wtsustain, wtsus } = registerControl('wtsustain', 'wtsus'); * Release time of the wavetable oscillator's position envelope * * @name wtrelease + * @group effects * @synonyms wtrel * @param {number | Pattern} time release time in seconds */ @@ -145,6 +152,7 @@ export const { wtrelease, wtrel } = registerControl('wtrelease', 'wtrel'); * Rate of the LFO for the wavetable oscillator's position * * @name wtrate + * @group effects * @param {number | Pattern} rate rate in hertz */ export const { wtrate } = registerControl('wtrate'); @@ -152,6 +160,7 @@ export const { wtrate } = registerControl('wtrate'); * cycle synced rate of the LFO for the wavetable oscillator's position * * @name wtsync + * @group effects * @param {number | Pattern} rate rate in cycles */ export const { wtsync } = registerControl('wtsync'); @@ -160,6 +169,7 @@ export const { wtsync } = registerControl('wtsync'); * Depth of the LFO for the wavetable oscillator's position * * @name wtdepth + * @group effects * @param {number | Pattern} depth depth of modulation */ export const { wtdepth } = registerControl('wtdepth'); @@ -168,6 +178,7 @@ export const { wtdepth } = registerControl('wtdepth'); * Shape of the LFO for the wavetable oscillator's position * * @name wtshape + * @group effects * @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..) */ export const { wtshape } = registerControl('wtshape'); @@ -176,6 +187,7 @@ export const { wtshape } = registerControl('wtshape'); * DC offset of the LFO for the wavetable oscillator's position * * @name wtdc + * @group effects * @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar */ export const { wtdc } = registerControl('wtdc'); @@ -184,6 +196,7 @@ export const { wtdc } = registerControl('wtdc'); * Skew of the LFO for the wavetable oscillator's position * * @name wtskew + * @group effects * @param {number | Pattern} skew How much to bend the LFO shape */ export const { wtskew } = registerControl('wtskew'); @@ -192,6 +205,7 @@ export const { wtskew } = registerControl('wtskew'); * Amount of warp (alteration of the waveform) to apply to the wavetable oscillator * * @name warp + * @group effects * @param {number | Pattern} amount Warp of the wavetable from 0 to 1 * @synonyms wavetableWarp * @example @@ -204,6 +218,7 @@ export const { warp, wavetableWarp } = registerControl('warp', 'wavetableWarp'); * Attack time of the wavetable oscillator's warp envelope * * @name warpattack + * @group effects * @synonyms warpatt * @param {number | Pattern} time attack time in seconds */ @@ -213,6 +228,7 @@ export const { warpattack, warpatt } = registerControl('warpattack', 'warpatt'); * Decay time of the wavetable oscillator's warp envelope * * @name warpdecay + * @group effects * @synonyms warpdec * @param {number | Pattern} time decay time in seconds */ @@ -222,6 +238,7 @@ export const { warpdecay, warpdec } = registerControl('warpdecay', 'warpdec'); * Sustain time of the wavetable oscillator's warp envelope * * @name warpsustain + * @group effects * @synonyms warpsus * @param {number | Pattern} gain sustain level (0 to 1) */ @@ -231,6 +248,7 @@ export const { warpsustain, warpsus } = registerControl('warpsustain', 'warpsus' * Release time of the wavetable oscillator's warp envelope * * @name warprelease + * @group effects * @synonyms warprel * @param {number | Pattern} time release time in seconds */ @@ -240,6 +258,7 @@ export const { warprelease, warprel } = registerControl('warprelease', 'warprel' * Rate of the LFO for the wavetable oscillator's warp * * @name warprate + * @group effects * @param {number | Pattern} rate rate in hertz */ export const { warprate } = registerControl('warprate'); @@ -248,6 +267,7 @@ export const { warprate } = registerControl('warprate'); * Depth of the LFO for the wavetable oscillator's warp * * @name warpdepth + * @group effects * @param {number | Pattern} depth depth of modulation */ export const { warpdepth } = registerControl('warpdepth'); @@ -256,6 +276,7 @@ export const { warpdepth } = registerControl('warpdepth'); * Shape of the LFO for the wavetable oscillator's warp * * @name warpshape + * @group effects * @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..) */ export const { warpshape } = registerControl('warpshape'); @@ -264,6 +285,7 @@ export const { warpshape } = registerControl('warpshape'); * DC offset of the LFO for the wavetable oscillator's warp * * @name warpdc + * @group effects * @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar */ export const { warpdc } = registerControl('warpdc'); @@ -272,6 +294,7 @@ export const { warpdc } = registerControl('warpdc'); * Skew of the LFO for the wavetable oscillator's warp * * @name warpskew + * @group effects * @param {number | Pattern} skew How much to bend the LFO shape */ export const { warpskew } = registerControl('warpskew'); @@ -283,6 +306,7 @@ export const { warpskew } = registerControl('warpskew'); * spin, chaos, primes, binary, brownian, reciprocal, wormhole, logistic, sigmoid, fractal, flip * * @name warpmode + * @group effects * @param {number | string | Pattern} mode Warp mode * @synonyms wavetableWarpMode * @example @@ -296,6 +320,7 @@ export const { warpmode, wavetableWarpMode } = registerControl('warpmode', 'wave * Amount of randomness of the initial phase of the wavetable oscillator. * * @name wtphaserand + * @group effects * @param {number | Pattern} amount Randomness of the initial phase. Between 0 (not random) and 1 (fully random) * @synonyms wavetablePhaseRand * @example @@ -308,6 +333,7 @@ export const { wtphaserand, wavetablePhaseRand } = registerControl('wtphaserand' * Amount of envelope applied wavetable oscillator's position envelope * * @name warpenv + * @group effects * @param {number | Pattern} amount between 0 and 1 */ export const { warpenv } = registerControl('warpenv'); @@ -316,6 +342,7 @@ export const { warpenv } = registerControl('warpenv'); * cycle synced rate of the LFO for the wavetable warp position * * @name warpsync + * @group effects * @param {number | Pattern} rate rate in cycles */ export const { warpsync } = registerControl('warpsync'); @@ -324,6 +351,7 @@ export const { warpsync } = registerControl('warpsync'); * Define a custom webaudio node to use as a sound source. * * @name source + * @group external_io * @synonyms src * @param {function} getSource * @synonyms src @@ -336,6 +364,7 @@ export const { source, src } = registerControl('source', 'src'); * `n` can also be used to play midi numbers, but it is recommended to use `note` instead. * * @name n + * @group samples * @param {number | Pattern} value sample index starting from 0 * @example * s("bd sd [~ bd] sd,hh*6").n("<0 1>") @@ -354,6 +383,7 @@ export const { n } = registerControl('n'); * You can also use midi numbers instead of note names, where 69 is mapped to A4 440Hz in 12EDO. * * @name note + * @group music_theory * @example * note("c a f e") * @example @@ -369,6 +399,7 @@ export const { note } = registerControl(['note', 'n']); * A pattern of numbers that speed up (or slow down) samples while they play. Currently only supported by osc / superdirt. * * @name accelerate + * @group samples * @param {number | Pattern} amount acceleration. * @superdirtOnly * @example @@ -380,6 +411,7 @@ export const { accelerate } = registerControl('accelerate'); * Sets the velocity from 0 to 1. Is multiplied together with gain. * * @name velocity + * @group effects * @example * s("hh*8") * .gain(".4!2 1 .4!2 1 .4 1") @@ -390,6 +422,7 @@ export const { velocity } = registerControl('velocity'); * Controls the gain by an exponential amount. * * @name gain + * @group effects * @param {number | Pattern} amount gain. * @example * s("hh*8").gain(".4!2 1 .4!2 1 .4 1").fast(2) @@ -400,6 +433,7 @@ export const { gain } = registerControl('gain'); * Gain applied after all effects have been processed. * * @name postgain + * @group effects * @example * s("bd sd [~ bd] sd,hh*8") * .compressor("-20:20:10:.002:.02").postgain(1.5) @@ -410,6 +444,7 @@ export const { postgain } = registerControl('postgain'); * Like `gain`, but linear. * * @name amp + * @group effects * @param {number | Pattern} amount gain. * @superdirtOnly * @example @@ -421,6 +456,7 @@ export const { amp } = registerControl('amp'); * Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset. * * @name attack + * @group effects * @param {number | Pattern} attack time in seconds. * @synonyms att * @example @@ -436,6 +472,7 @@ export const { attack, att } = registerControl('attack', 'att'); * while decimal numbers and complex ratios sound metallic. * * @name fmh + * @group effects * @param {number | Pattern} harmonicity * @example * note("c e g b g e") @@ -450,6 +487,7 @@ export const { fmh } = registerControl(['fmh', 'fmi'], 'fmh'); * Controls the modulation index, which defines the brightness of the sound. * * @name fm + * @group effects * @param {number | Pattern} brightness modulation index * @synonyms fmi * @example @@ -464,6 +502,7 @@ export const { fmi, fm } = registerControl(['fmi', 'fmh'], 'fm'); * Ramp type of fm envelope. Exp might be a bit broken.. * * @name fmenv + * @group effects * @param {number | Pattern} type lin | exp * @example * note("c e g b g e") @@ -479,6 +518,7 @@ export const { fmenv } = registerControl('fmenv'); * Attack time for the FM envelope: time it takes to reach maximum modulation * * @name fmattack + * @group effects * @param {number | Pattern} time attack time * @example * note("c e g b g e") @@ -493,6 +533,7 @@ export const { fmattack } = registerControl('fmattack'); * Waveform of the fm modulator * * @name fmwave + * @group effects * @param {number | Pattern} wave waveform * @example * n("0 1 2 3".fast(4)).scale("d:minor").s("sine").fmwave("").fm(4).fmh(2.01) @@ -506,6 +547,7 @@ export const { fmwave } = registerControl('fmwave'); * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase. * * @name fmdecay + * @group effects * @param {number | Pattern} time decay time * @example * note("c e g b g e") @@ -520,6 +562,7 @@ export const { fmdecay } = registerControl('fmdecay'); * Sustain level for the FM envelope: how much modulation is applied after the decay phase * * @name fmsustain + * @group effects * @param {number | Pattern} level sustain level * @example * note("c e g b g e") @@ -538,6 +581,7 @@ export const { fmvelocity } = registerControl('fmvelocity'); * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`. * * @name bank + * @group samples * @param {string | Pattern} bank the name of the bank * @example * s("bd sd [~ bd] sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd") @@ -549,6 +593,7 @@ export const { bank } = registerControl('bank'); * mix control for the chorus effect * * @name chorus + * @group effects * @param {string | Pattern} chorus mix amount between 0 and 1 * @example * note("d d a# a").s("sawtooth").chorus(.5) @@ -566,6 +611,7 @@ export const { fft } = registerControl('fft'); * Note that the decay is only audible if the sustain value is lower than 1. * * @name decay + * @group effects * @param {number | Pattern} time decay time in seconds * @synonyms dec * @example @@ -577,6 +623,7 @@ export const { decay, dec } = registerControl('decay', 'dec'); * Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset. * * @name sustain + * @group effects * @param {number | Pattern} gain sustain level between 0 and 1 * @synonyms sus * @example @@ -588,6 +635,7 @@ export const { sustain, sus } = registerControl('sustain', 'sus'); * Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero. * * @name release + * @group effects * @param {number | Pattern} time release time in seconds * @synonyms rel * @example @@ -602,6 +650,7 @@ export const { hold } = registerControl('hold'); * can also optionally supply the 'bpq' parameter separated by ':'. * * @name bpf + * @group effects * @param {number | Pattern} frequency center frequency * @synonyms bandf, bp * @example @@ -614,6 +663,7 @@ export const { bandf, bpf, bp } = registerControl(['bandf', 'bandq', 'bpenv'], ' * Sets the **b**and-**p**ass **q**-factor (resonance). * * @name bpq + * @group effects * @param {number | Pattern} q q factor * @synonyms bandq * @example @@ -628,6 +678,7 @@ export const { bandq, bpq } = registerControl('bandq', 'bpq'); * * @memberof Pattern * @name begin + * @group samples * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample * @example * samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/dirt-samples') @@ -640,6 +691,7 @@ export const { begin } = registerControl('begin'); * * @memberof Pattern * @name end + * @group samples * @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc.. * @example * s("bd*2,oh*4").end("<.1 .2 .5 1>").fast(2) @@ -652,6 +704,7 @@ export const { end } = registerControl('end'); * To change the loop region, use loopBegin / loopEnd. * * @name loop + * @group samples * @param {number | Pattern} on If 1, the sample is looped * @example * s("casio").loop(1) @@ -664,6 +717,7 @@ export const { loop } = registerControl('loop'); * Note: Samples starting with wt_ will automatically loop! (wt = wavetable) * * @name loopBegin + * @group samples * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample * @synonyms loopb * @example @@ -677,6 +731,7 @@ export const { loopBegin, loopb } = registerControl('loopBegin', 'loopb'); * Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`! * * @name loopEnd + * @group samples * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample * @synonyms loope * @example @@ -688,6 +743,7 @@ export const { loopEnd, loope } = registerControl('loopEnd', 'loope'); * Bit crusher effect. * * @name crush + * @group effects * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). * @example * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") @@ -699,6 +755,7 @@ export const { crush } = registerControl('crush'); * Fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers * * @name coarse + * @group effects * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. * @example * s("bd sd [~ bd] sd,hh*8").coarse("<1 4 8 16 32>") @@ -710,6 +767,7 @@ export const { coarse } = registerControl('coarse'); * Modulate the amplitude of a sound with a continuous waveform * * @name tremolo + * @group effects * @synonyms trem * @param {number | Pattern} speed modulation speed in HZ * @example @@ -722,6 +780,7 @@ export const { tremolo } = registerControl(['tremolo', 'tremolodepth', 'tremolos * Modulate the amplitude of a sound with a continuous waveform * * @name tremolosync + * @group effects * @synonyms tremsync * @param {number | Pattern} cycles modulation speed in cycles * @example @@ -737,6 +796,7 @@ export const { tremolosync } = registerControl( * Depth of amplitude modulation * * @name tremolodepth + * @group effects * @synonyms tremdepth * @param {number | Pattern} depth * @example @@ -748,6 +808,7 @@ export const { tremolodepth } = registerControl('tremolodepth', 'tremdepth'); * Alter the shape of the modulation waveform * * @name tremoloskew + * @group effects * @synonyms tremskew * @param {number | Pattern} amount between 0 & 1, the shape of the waveform * @example @@ -760,6 +821,7 @@ export const { tremoloskew } = registerControl('tremoloskew', 'tremskew'); * Alter the phase of the modulation waveform * * @name tremolophase + * @group effects * @synonyms tremphase * @param {number | Pattern} offset the offset in cycles of the modulation * @example @@ -772,6 +834,7 @@ export const { tremolophase } = registerControl('tremolophase', 'tremphase'); * Shape of amplitude modulation * * @name tremoloshape + * @group effects * @synonyms tremshape * @param {number | Pattern} shape tri | square | sine | saw | ramp * @example @@ -783,6 +846,7 @@ export const { tremoloshape } = registerControl('tremoloshape', 'tremshape'); * Filter overdrive for supported filter types * * @name drive + * @group effects * @param {number | Pattern} amount * @example * note("{f g g c d a a#}%16".sub(17)).s("supersaw").lpenv(8).lpf(150).lpq(.8).ftype('ladder').drive("<.5 4>") @@ -796,6 +860,7 @@ export const { drive } = registerControl('drive'); * Can be applied to multiple orbits with the ':' mininotation, e.g. `duckorbit("2:3")` * * @name duckorbit + * @group effects * @synonyms duck * @param {number | Pattern} orbit target orbit * @example @@ -816,6 +881,7 @@ export const { duck } = registerControl('duckorbit', 'duck'); * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`. * * @name duckdepth + * @group effects * @param {number | Pattern} depth depth of modulation from 0 to 1 * @example * stack( n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2), s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack(0.2).duckdepth("<1 .9 .6 0>")) @@ -835,6 +901,7 @@ export const { duckdepth } = registerControl('duckdepth'); * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`. * * @name duckonset + * @group effects * @synonyms duckons * * @param {number | Pattern} time The onset time in seconds @@ -862,6 +929,7 @@ export const { duckonset } = registerControl('duckonset', 'duckons'); * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`. * * @name duckattack + * @group effects * @synonyms duckatt * * @param {number | Pattern} time The attack time in seconds @@ -906,6 +974,7 @@ export const { byteBeatStartTime, bbst } = registerControl('byteBeatStartTime', * Allows you to set the output channels on the interface * * @name channels + * @group external_io * @synonyms ch * * @param {number | Pattern} channels pattern the output channels @@ -919,6 +988,7 @@ export const { channels, ch } = registerControl('channels', 'ch'); * Controls the pulsewidth of the pulse oscillator * * @name pw + * @group effects * @param {number | Pattern} pulsewidth * @example * note("{f a c e}%16").s("pulse").pw(".8:1:.2") @@ -931,6 +1001,7 @@ export const { pw } = registerControl(['pw', 'pwrate', 'pwsweep']); * Controls the lfo rate for the pulsewidth of the pulse oscillator * * @name pwrate + * @group effects * @param {number | Pattern} rate * @example * n(run(8)).scale("D:pentatonic").s("pulse").pw("0.5").pwrate("<5 .1 25>").pwsweep("<0.3 .8>") @@ -943,6 +1014,7 @@ export const { pwrate } = registerControl('pwrate'); * Controls the lfo sweep for the pulsewidth of the pulse oscillator * * @name pwsweep + * @group effects * @param {number | Pattern} sweep * @example * n(run(8)).scale("D:pentatonic").s("pulse").pw("0.5").pwrate("<5 .1 25>").pwsweep("<0.3 .8>") @@ -954,6 +1026,7 @@ export const { pwsweep } = registerControl('pwsweep'); * Phaser audio effect that approximates popular guitar pedals. * * @name phaser + * @group effects * @synonyms ph * @param {number | Pattern} speed speed of modulation * @example @@ -971,6 +1044,7 @@ export const { phaserrate, ph, phaser } = registerControl( * The frequency sweep range of the lfo for the phaser effect. Defaults to 2000 * * @name phasersweep + * @group effects * @synonyms phs * @param {number | Pattern} phasersweep most useful values are between 0 and 4000 * @example @@ -984,6 +1058,7 @@ export const { phasersweep, phs } = registerControl('phasersweep', 'phs'); * The center frequency of the phaser in HZ. Defaults to 1000 * * @name phasercenter + * @group effects * @synonyms phc * @param {number | Pattern} centerfrequency in HZ * @example @@ -998,6 +1073,7 @@ export const { phasercenter, phc } = registerControl('phasercenter', 'phc'); * The amount the signal is affected by the phaser effect. Defaults to 0.75 * * @name phaserdepth + * @group effects * @synonyms phd, phasdp * @param {number | Pattern} depth number between 0 and 1 * @example @@ -1012,6 +1088,7 @@ export const { phaserdepth, phd, phasdp } = registerControl('phaserdepth', 'phd' * Choose the channel the pattern is sent to in superdirt * * @name channel + * @group effects * @param {number | Pattern} channel channel number * */ @@ -1020,6 +1097,7 @@ export const { channel } = registerControl('channel'); * In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open. * * @name cut + * @group effects * @param {number | Pattern} group cut group number * @example * s("[oh hh]*4").cut(1) @@ -1032,6 +1110,7 @@ export const { cut } = registerControl('cut'); * When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'. * * @name lpf + * @group effects * @param {number | Pattern} frequency audible between 0 and 20000 * @synonyms cutoff, ctf, lp * @example @@ -1045,6 +1124,7 @@ export const { cutoff, ctf, lpf, lp } = registerControl(['cutoff', 'resonance', /** * Sets the lowpass filter envelope modulation depth. * @name lpenv + * @group effects * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_ * @synonyms lpe * @example @@ -1058,6 +1138,7 @@ export const { lpenv, lpe } = registerControl('lpenv', 'lpe'); /** * Sets the highpass filter envelope modulation depth. * @name hpenv + * @group effects * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_ * @synonyms hpe * @example @@ -1071,6 +1152,7 @@ export const { hpenv, hpe } = registerControl('hpenv', 'hpe'); /** * Sets the bandpass filter envelope modulation depth. * @name bpenv + * @group effects * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_ * @synonyms bpe * @example @@ -1084,6 +1166,7 @@ export const { bpenv, bpe } = registerControl('bpenv', 'bpe'); /** * Sets the attack duration for the lowpass filter envelope. * @name lpattack + * @group effects * @param {number | Pattern} attack time of the filter envelope * @synonyms lpa * @example @@ -1097,6 +1180,7 @@ export const { lpattack, lpa } = registerControl('lpattack', 'lpa'); /** * Sets the attack duration for the highpass filter envelope. * @name hpattack + * @group effects * @param {number | Pattern} attack time of the highpass filter envelope * @synonyms hpa * @example @@ -1110,6 +1194,7 @@ export const { hpattack, hpa } = registerControl('hpattack', 'hpa'); /** * Sets the attack duration for the bandpass filter envelope. * @name bpattack + * @group effects * @param {number | Pattern} attack time of the bandpass filter envelope * @synonyms bpa * @example @@ -1123,6 +1208,7 @@ export const { bpattack, bpa } = registerControl('bpattack', 'bpa'); /** * Sets the decay duration for the lowpass filter envelope. * @name lpdecay + * @group effects * @param {number | Pattern} decay time of the filter envelope * @synonyms lpd * @example @@ -1136,6 +1222,7 @@ export const { lpdecay, lpd } = registerControl('lpdecay', 'lpd'); /** * Sets the decay duration for the highpass filter envelope. * @name hpdecay + * @group effects * @param {number | Pattern} decay time of the highpass filter envelope * @synonyms hpd * @example @@ -1150,6 +1237,7 @@ export const { hpdecay, hpd } = registerControl('hpdecay', 'hpd'); /** * Sets the decay duration for the bandpass filter envelope. * @name bpdecay + * @group effects * @param {number | Pattern} decay time of the bandpass filter envelope * @synonyms bpd * @example @@ -1164,6 +1252,7 @@ export const { bpdecay, bpd } = registerControl('bpdecay', 'bpd'); /** * Sets the sustain amplitude for the lowpass filter envelope. * @name lpsustain + * @group effects * @param {number | Pattern} sustain amplitude of the lowpass filter envelope * @synonyms lps * @example @@ -1178,6 +1267,7 @@ export const { lpsustain, lps } = registerControl('lpsustain', 'lps'); /** * Sets the sustain amplitude for the highpass filter envelope. * @name hpsustain + * @group effects * @param {number | Pattern} sustain amplitude of the highpass filter envelope * @synonyms hps * @example @@ -1192,6 +1282,7 @@ export const { hpsustain, hps } = registerControl('hpsustain', 'hps'); /** * Sets the sustain amplitude for the bandpass filter envelope. * @name bpsustain + * @group effects * @param {number | Pattern} sustain amplitude of the bandpass filter envelope * @synonyms bps * @example @@ -1206,6 +1297,7 @@ export const { bpsustain, bps } = registerControl('bpsustain', 'bps'); /** * Sets the release time for the lowpass filter envelope. * @name lprelease + * @group effects * @param {number | Pattern} release time of the filter envelope * @synonyms lpr * @example @@ -1221,6 +1313,7 @@ export const { lprelease, lpr } = registerControl('lprelease', 'lpr'); /** * Sets the release time for the highpass filter envelope. * @name hprelease + * @group effects * @param {number | Pattern} release time of the highpass filter envelope * @synonyms hpr * @example @@ -1236,6 +1329,7 @@ export const { hprelease, hpr } = registerControl('hprelease', 'hpr'); /** * Sets the release time for the bandpass filter envelope. * @name bprelease + * @group effects * @param {number | Pattern} release time of the bandpass filter envelope * @synonyms bpr * @example @@ -1251,6 +1345,7 @@ export const { bprelease, bpr } = registerControl('bprelease', 'bpr'); /** * Sets the filter type. The ladder filter is more aggressive. More types might be added in the future. * @name ftype + * @group effects * @param {number | Pattern} type 12db (0), ladder (1), or 24db (2) * @example * note("{f g g c d a a#}%8").s("sawtooth").lpenv(4).lpf(500).ftype("<0 1 2>").lpq(1) @@ -1266,6 +1361,7 @@ export const { ftype } = registerControl('ftype'); /** * controls the center of the filter envelope. 0 is unipolar positive, .5 is bipolar, 1 is unipolar negative * @name fanchor + * @group effects * @param {number | Pattern} center 0 to 1 * @example * note("{f g g c d a a#}%8").s("sawtooth").lpf("{1000}%2") @@ -1278,6 +1374,7 @@ export const { fanchor } = registerControl('fanchor'); * When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'. * * @name hpf + * @group effects * @param {number | Pattern} frequency audible between 0 and 20000 * @synonyms hp, hcutoff * @example @@ -1292,6 +1389,7 @@ export const { fanchor } = registerControl('fanchor'); * Applies a vibrato to the frequency of the oscillator. * * @name vib + * @group effects * @synonyms vibrato, v * @param {number | Pattern} frequency of the vibrato in hertz * @example @@ -1309,6 +1407,7 @@ export const { vib, vibrato, v } = registerControl(['vib', 'vibmod'], 'vibrato', * Adds pink noise to the mix * * @name noise + * @group generators * @param {number | Pattern} wet wet amount * @example * sound("/2") @@ -1318,6 +1417,7 @@ export const { noise } = registerControl('noise'); * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set * * @name vibmod + * @group effects * @synonyms vmod * @param {number | Pattern} depth of vibrato (in semitones) * @example @@ -1336,6 +1436,7 @@ export const { hcutoff, hpf, hp } = registerControl(['hcutoff', 'hresonance', 'h * Controls the **h**igh-**p**ass **q**-value. * * @name hpq + * @group effects * @param {number | Pattern} q resonance factor between 0 and 50 * @synonyms hresonance * @example @@ -1347,6 +1448,7 @@ export const { hresonance, hpq } = registerControl('hresonance', 'hpq'); * Controls the **l**ow-**p**ass **q**-value. * * @name lpq + * @group effects * @param {number | Pattern} q resonance factor between 0 and 50 * @synonyms resonance * @example @@ -1359,6 +1461,7 @@ export const { resonance, lpq } = registerControl('resonance', 'lpq'); * DJ filter, below 0.5 is low pass filter, above is high pass filter. * * @name djf + * @group effects * @param {number | Pattern} cutoff below 0.5 is low pass filter, above is high pass filter * @example * n(irand(16).seg(8)).scale("d:phrygian").s("supersaw").djf("<.5 .3 .2 .75>") @@ -1375,6 +1478,7 @@ export const { djf } = registerControl('djf'); * * * @name delay + * @group effects * @param {number | Pattern} level between 0 and 1 * @example * s("bd bd").delay("<0 .25 .5 1>") @@ -1388,6 +1492,7 @@ export const { delay } = registerControl(['delay', 'delaytime', 'delayfeedback'] * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it * * @name delayfeedback + * @group effects * @param {number | Pattern} feedback between 0 and 1 * @synonyms delayfb, dfb * @example @@ -1401,6 +1506,7 @@ export const { delayfeedback, delayfb, dfb } = registerControl('delayfeedback', * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it * * @name delayfeedback + * @group effects * @param {number | Pattern} feedback between 0 and 1 * @synonyms delayfb, dfb * @example @@ -1412,6 +1518,7 @@ export const { delayspeed } = registerControl('delayspeed'); * Sets the time of the delay effect. * * @name delayspeed + * @group effects * @param {number | Pattern} delayspeed controls the pitch of the delay feedback * @synonyms delayt, dt * @example @@ -1424,6 +1531,7 @@ export const { delaytime, delayt, dt } = registerControl('delaytime', 'delayt', * Sets the time of the delay effect in cycles. * * @name delaysync + * @group effects * @param {number | Pattern} cycles delay length in cycles * @synonyms delayt, dt * @example @@ -1436,6 +1544,7 @@ export const { delaysync } = registerControl('delaysync'); * Specifies whether delaytime is calculated relative to cps. * * @name lock + * @group effects * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. * @superdirtOnly * @example @@ -1449,6 +1558,7 @@ export const { lock } = registerControl('lock'); * Set detune for stacked voices of supported oscillators * * @name detune + * @group effects * @param {number | Pattern} amount * @synonyms det * @example @@ -1460,6 +1570,7 @@ export const { detune, det } = registerControl('detune', 'det'); * Set number of stacked voices for supported oscillators * * @name unison + * @group effects * @param {number | Pattern} numvoices * @example * note("d f a a# a d3").fast(2).s("supersaw").unison("<1 2 7>") @@ -1471,6 +1582,7 @@ export const { unison } = registerControl('unison'); * Set the stereo pan spread for supported oscillators * * @name spread + * @group effects * @param {number | Pattern} spread between 0 and 1 * @example * note("d f a a# a d3").fast(2).s("supersaw").spread("<0 .3 1>") @@ -1481,6 +1593,7 @@ export const { spread } = registerControl('spread'); * Set dryness of reverb. See `room` and `size` for more information about reverb. * * @name dry + * @group effects * @param {number | Pattern} dry 0 = wet, 1 = dry * @example * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() @@ -1506,6 +1619,7 @@ export const { fadeInTime } = registerControl('fadeInTime'); * Set frequency of sound. * * @name freq + * @group transforms * @param {number | Pattern} frequency in Hz. the audible range is between 20 and 20000 Hz * @example * freq("220 110 440 110").s("superzow").osc() @@ -1519,6 +1633,7 @@ export const { freq } = registerControl('freq'); * Attack time of pitch envelope. * * @name pattack + * @group effects * @synonyms patt * @param {number | Pattern} time time in seconds * @example @@ -1530,6 +1645,7 @@ export const { pattack, patt } = registerControl('pattack', 'patt'); * Decay time of pitch envelope. * * @name pdecay + * @group effects * @synonyms pdec * @param {number | Pattern} time time in seconds * @example @@ -1543,6 +1659,7 @@ export const { psustain, psus } = registerControl('psustain', 'psus'); * Release time of pitch envelope * * @name prelease + * @group effects * @synonyms prel * @param {number | Pattern} time time in seconds * @example @@ -1557,6 +1674,7 @@ export const { prelease, prel } = registerControl('prelease', 'prel'); * If you don't set other pitch envelope controls, `pattack:.2` will be the default. * * @name penv + * @group effects * @param {number | Pattern} semitones change in semitones * @example * note("c") @@ -1568,6 +1686,7 @@ export const { penv } = registerControl('penv'); * Curve of envelope. Defaults to linear. exponential is good for kicks * * @name pcurve + * @group effects * @param {number | Pattern} type 0 = linear, 1 = exponential * @example * note("g1*4") @@ -1584,6 +1703,7 @@ export const { pcurve } = registerControl('pcurve'); * If you don't set an anchor, the value will default to the psustain value. * * @name panchor + * @group effects * @param {number | Pattern} anchor anchor offset * @example * note("c c4").penv(12).panchor("<0 .5 1 .5>") @@ -1605,6 +1725,7 @@ export const { gate, gat } = registerControl('gate', 'gat'); * Emulation of a Leslie speaker: speakers rotating in a wooden amplified cabinet. * * @name leslie + * @group effects * @param {number | Pattern} wet between 0 and 1 * @example * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() @@ -1616,6 +1737,7 @@ export const { leslie } = registerControl('leslie'); * Rate of modulation / rotation for leslie effect * * @name lrate + * @group effects * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow * @example * n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() @@ -1628,6 +1750,7 @@ export const { lrate } = registerControl('lrate'); * Physical size of the cabinet in meters. Be careful, it might be slightly larger than your computer. Affects the Doppler amount (pitch warble) * * @name lsize + * @group effects * @param {number | Pattern} meters somewhere between 0 and 1 * @example * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() @@ -1639,6 +1762,7 @@ export const { lsize } = registerControl('lsize'); * Sets the displayed text for an event on the pianoroll * * @name label + * @group visualization * @param {string} label text to display */ export const { activeLabel } = registerControl('activeLabel'); @@ -1704,6 +1828,7 @@ export const { overshape } = registerControl('overshape'); * Sets position in stereo. * * @name pan + * @group effects * @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel) * @example * s("[bd hh]*2").pan("<.5 1 .5 0>") @@ -1769,6 +1894,7 @@ export const { mode } = registerControl(['mode', 'anchor']); * When using mininotation, you can also optionally add the 'size' parameter, separated by ':'. * * @name room + * @group effects * @param {number | Pattern} level between 0 and 1 * @example * s("bd sd [~ bd] sd").room("<0 .2 .4 .6 .8 1>") @@ -1782,6 +1908,7 @@ export const { room } = registerControl(['room', 'size']); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomlp + * @group effects * @synonyms rlp * @param {number} frequency between 0 and 20000hz * @example @@ -1795,6 +1922,7 @@ export const { roomlp, rlp } = registerControl('roomlp', 'rlp'); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomdim + * @group effects * @synonyms rdim * @param {number} frequency between 0 and 20000hz * @example @@ -1809,6 +1937,7 @@ export const { roomdim, rdim } = registerControl('roomdim', 'rdim'); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomfade + * @group effects * @synonyms rfade * @param {number} seconds for the reverb to fade * @example @@ -1821,6 +1950,7 @@ export const { roomfade, rfade } = registerControl('roomfade', 'rfade'); /** * Sets the sample to use as an impulse response for the reverb. * @name iresponse + * @group effects * @param {string | Pattern} sample to use as an impulse response * @synonyms ir * @example @@ -1832,6 +1962,7 @@ export const { ir, iresponse } = registerControl(['ir', 'i'], 'iresponse'); /** * Sets speed of the sample for the impulse response. * @name irspeed + * @group effects * @param {string | Pattern} speed * @example * samples('github:switchangel/pad') @@ -1843,6 +1974,7 @@ export const { irspeed } = registerControl('irspeed'); /** * Sets the beginning of the IR response sample * @name irbegin + * @group effects * @param {string | Pattern} begin between 0 and 1 * @synonyms ir * @example @@ -1856,6 +1988,7 @@ export const { irbegin } = registerControl('irbegin'); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomsize + * @group effects * @param {number | Pattern} size between 0 and 10 * @synonyms rsize, sz, size * @example @@ -1879,6 +2012,7 @@ export const { roomsize, size, sz, rsize } = registerControl('roomsize', 'size', * * * @name shape + * @group effects * @param {number | Pattern} distortion between 0 and 1 * @example * s("bd sd [~ bd] sd,hh*8").shape("<0 .2 .4 .6 .8>") @@ -1891,6 +2025,7 @@ export const { shape } = registerControl(['shape', 'shapevol']); * Most useful values are usually between 0 and 10 (depending on source gain). If you are feeling adventurous, you can turn it up to 11 and beyond ;) * * @name distort + * @group effects * @synonyms dist * @param {number | Pattern} distortion * @example @@ -1905,6 +2040,7 @@ export const { distort, dist } = registerControl(['distort', 'distortvol'], 'dis * More info [here](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode?retiredLocale=de#instance_properties) * * @name compressor + * @group effects * @example * s("bd sd [~ bd] sd,hh*8") * .compressor("-20:20:10:.002:.02") @@ -1925,6 +2061,7 @@ export const { compressorRelease } = registerControl('compressorRelease'); * Changes the speed of sample playback, i.e. a cheap way of changing pitch. * * @name speed + * @group effects * @param {number | Pattern} speed -inf to inf, negative numbers play the sample backwards. * @example * s("bd*6").speed("1 2 4 1 -2 -4") @@ -1938,6 +2075,7 @@ export const { speed } = registerControl('speed'); * Changes the speed of sample playback, i.e. a cheap way of changing pitch. * * @name stretch + * @group effects * @param {number | Pattern} factor -inf to inf, negative numbers play the sample backwards. * @example * s("gm_flute").stretch("1 2 .5") @@ -1948,6 +2086,7 @@ export const { stretch } = registerControl('stretch'); * Used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`. * * @name unit + * @group effects * @param {number | string | Pattern} unit see description above * @example * speed("1 2 .5 3").s("bd").unit("c").osc() @@ -1962,6 +2101,7 @@ export const { unit } = registerControl('unit'); * "A simplistic pitch-raising algorithm. It's not meant to sound natural; its sound is reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter, depending on the input. The algorithm works by cutting the signal into fragments (delimited by upwards-going zero-crossings) and squeezing those fragments in the time domain (i.e. simply playing them back faster than they came in), leaving silences inbetween. All the parameters apart from memlen can be modulated." * * @name squiz + * @group effects * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc. * @example * squiz("2 4/2 6 [8 16]").s("bd").osc() @@ -1986,6 +2126,7 @@ export const { squiz } = registerControl('squiz'); * Formant filter to make things sound like vowels. * * @name vowel + * @group effects * @param {string | Pattern} vowel You can use a e i o u ae aa oe ue y uh un en an on, corresponding to [a] [e] [i] [o] [u] [æ] [ɑ] [ø] [y] [ɯ] [ʌ] [œ̃] [ɛ̃] [ɑ̃] [ɔ̃]. Aliases: aa = å = ɑ, oe = ø = ö, y = ı, ae = æ. * @example * note("[c2 >]*2").s('sawtooth') @@ -2010,6 +2151,7 @@ export const { waveloss } = registerControl('waveloss'); * Noise crackle density * * @name density + * @group effects * @param {number | Pattern} density between 0 and x * @example * s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(4)) @@ -2060,6 +2202,7 @@ export const { cps } = registerControl('cps'); * Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration. * * @name clip + * @group transforms * @synonyms legato * @param {number | Pattern} factor >= 0 * @example @@ -2072,6 +2215,7 @@ export const { clip, legato } = registerControl('clip', 'legato'); * Sets the duration of the event in cycles. Similar to clip / legato, it also cuts samples off at the end if they exceed the duration. * * @name duration + * @group transforms * @synonyms dur * @param {number | Pattern} seconds >= 0 * @example @@ -2100,6 +2244,7 @@ export const { zzfx } = registerControl('zzfx'); /** * Sets the color of the hap in visualizations like pianoroll or highlighting. * @name color + * @group visualization * @synonyms colour * @param {string} color Hexadecimal or CSS color name */ @@ -2114,6 +2259,7 @@ export let createParams = (...names) => * ADSR envelope: Combination of Attack, Decay, Sustain, and Release. * * @name adsr + * @group effects * @param {number | Pattern} time attack time in seconds * @param {number | Pattern} time decay time in seconds * @param {number | Pattern} gain sustain level (0 to 1) @@ -2148,6 +2294,7 @@ export const ar = register('ar', (t, pat) => { * MIDI channel: Sets the MIDI channel for the event. * * @name midichan + * @group examples * @param {number | Pattern} channel MIDI channel number (0-15) * @example * note("c4").midichan(1).midi() @@ -2160,6 +2307,7 @@ export const { midimap } = registerControl('midimap'); * MIDI port: Sets the MIDI port for the event. * * @name midiport + * @group examples * @param {number | Pattern} port MIDI port * @example * note("c a f e").midiport("<0 1 2 3>").midi() @@ -2170,6 +2318,7 @@ export const { midiport } = registerControl('midiport'); * MIDI command: Sends a MIDI command message. * * @name midicmd + * @group examples * @param {number | Pattern} command MIDI command * @example * midicmd("clock*48,/2").midi() @@ -2180,6 +2329,7 @@ export const { midicmd } = registerControl('midicmd'); * MIDI control: Sends a MIDI control change message. * * @name control + * @group examples * @param {number | Pattern} MIDI control number (0-127) * @param {number | Pattern} MIDI controller value (0-127) */ @@ -2195,6 +2345,7 @@ export const control = register('control', (args, pat) => { * MIDI control number: Sends a MIDI control change message. * * @name ccn + * @group examples * @param {number | Pattern} MIDI control number (0-127) */ export const { ccn } = registerControl('ccn'); @@ -2202,6 +2353,7 @@ export const { ccn } = registerControl('ccn'); * MIDI control value: Sends a MIDI control change message. * * @name ccv + * @group examples * @param {number | Pattern} MIDI control value (0-127) */ export const { ccv } = registerControl('ccv'); @@ -2211,6 +2363,7 @@ export const { ctlNum } = registerControl('ctlNum'); /** * MIDI NRPN non-registered parameter number: Sends a MIDI NRPN non-registered parameter number message. * @name nrpnn + * @group examples * @param {number | Pattern} nrpnn MIDI NRPN non-registered parameter number (0-127) * @example * note("c4").nrpnn("1:8").nrpv("123").midichan(1).midi() @@ -2219,6 +2372,7 @@ export const { nrpnn } = registerControl('nrpnn'); /** * MIDI NRPN non-registered parameter value: Sends a MIDI NRPN non-registered parameter value message. * @name nrpv + * @group examples * @param {number | Pattern} nrpv MIDI NRPN non-registered parameter value (0-127) * @example * note("c4").nrpnn("1:8").nrpv("123").midichan(1).midi() @@ -2229,6 +2383,7 @@ export const { nrpv } = registerControl('nrpv'); * MIDI program number: Sends a MIDI program change message. * * @name progNum + * @group examples * @param {number | Pattern} program MIDI program number (0-127) * @example * note("c4").progNum(10).midichan(1).midi() @@ -2238,6 +2393,7 @@ export const { progNum } = registerControl('progNum'); /** * MIDI sysex: Sends a MIDI sysex message. * @name sysex + * @group examples * @param {number | Pattern} id Sysex ID * @param {number | Pattern} data Sysex data * @example @@ -2253,6 +2409,7 @@ export const sysex = register('sysex', (args, pat) => { /** * MIDI sysex ID: Sends a MIDI sysex identifier message. * @name sysexid + * @group examples * @param {number | Pattern} id Sysex ID * @example * note("c4").sysexid("0x77").sysexdata("0x01:0x02:0x03:0x04").midichan(1).midi() @@ -2261,6 +2418,7 @@ export const { sysexid } = registerControl('sysexid'); /** * MIDI sysex data: Sends a MIDI sysex message. * @name sysexdata + * @group examples * @param {number | Pattern} data Sysex data * @example * note("c4").sysexid("0x77").sysexdata("0x01:0x02:0x03:0x04").midichan(1).midi() @@ -2270,6 +2428,7 @@ export const { sysexdata } = registerControl('sysexdata'); /** * MIDI pitch bend: Sends a MIDI pitch bend message. * @name midibend + * @group examples * @param {number | Pattern} midibend MIDI pitch bend (-1 - 1) * @example * note("c4").midibend(sine.slow(4).range(-0.4,0.4)).midi() @@ -2278,6 +2437,7 @@ export const { midibend } = registerControl('midibend'); /** * MIDI key after touch: Sends a MIDI key after touch message. * @name miditouch + * @group examples * @param {number | Pattern} miditouch MIDI key after touch (0-1) * @example * note("c4").miditouch(sine.slow(4).range(0,1)).midi() @@ -2298,6 +2458,7 @@ export const getControlName = (alias) => { * Sets properties in a batch. * * @name as + * @group transforms * @param {String | Array} mapping the control names that are set * @example * "c:.5 a:1 f:.25 e:.8".as("note:clip") @@ -2317,6 +2478,7 @@ export const as = register('as', (mapping, pat) => { * Allows you to scrub an audio file like a tape loop by passing values that represents the position in the audio file * in the optional array syntax ex: "0.5:2", the second value controls the speed of playback * @name scrub + * @group samples * @memberof Pattern * @returns Pattern * @example diff --git a/packages/superdough/reverbGen.mjs b/packages/superdough/reverbGen.mjs index d2533cae4..0d5fee264 100644 --- a/packages/superdough/reverbGen.mjs +++ b/packages/superdough/reverbGen.mjs @@ -79,7 +79,9 @@ reverbGen.generateGraph = function (data, width, height, min, max) { @param {number} lpFreqEnd @param {number} lpFreqEndAt @param {!function(!AudioBuffer)} callback May be called - immediately within the current execution context, or later.*/ + immediately within the current execution context, or later. + @group effects + */ var applyGradualLowpass = function (input, lpFreqStart, lpFreqEnd, lpFreqEndAt, callback) { if (lpFreqStart == 0) { callback(input); From 5032b4e966ecd3c92c47497457a5bf0576d85a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sat, 11 Oct 2025 14:11:50 +0200 Subject: [PATCH 005/124] Rename group -> tags --- jsdoc/jsdoc-synonyms.js | 4 +- packages/core/controls.mjs | 324 +++++++++++++++--------------- packages/core/pattern.mjs | 270 ++++++++++++------------- packages/core/signal.mjs | 104 +++++----- packages/motion/motion.mjs | 30 +-- packages/superdough/reverbGen.mjs | 2 +- packages/supradough/dough.mjs | 2 +- 7 files changed, 368 insertions(+), 368 deletions(-) diff --git a/jsdoc/jsdoc-synonyms.js b/jsdoc/jsdoc-synonyms.js index 423c7ad6e..c5860626a 100644 --- a/jsdoc/jsdoc-synonyms.js +++ b/jsdoc/jsdoc-synonyms.js @@ -13,10 +13,10 @@ function defineTags(dictionary) { }, }); - dictionary.defineTag('group', { + dictionary.defineTag('tags', { mustHaveValue: true, onTagged: function (doclet, tag) { - doclet.group = tag.value; + doclet.tags = tag.value.split(/[ ,]+/); }, }); } diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index e43af8967..44b358e67 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -77,7 +77,7 @@ export function registerControl(names, ...aliases) { * separated by ':'. * * @name s - * @group samples + * @tags samples * @param {string | Pattern} sound The sound / pattern of sounds to pick * @synonyms sound * @example @@ -92,7 +92,7 @@ export const { s, sound } = registerControl(['s', 'n', 'gain'], 'sound'); * Position in the wavetable of the wavetable oscillator * * @name wt - * @group effects + * @tags effects * @param {number | Pattern} position Position in the wavetable from 0 to 1 * @synonyms wavetablePosition * @example @@ -104,7 +104,7 @@ export const { wt, wavetablePosition } = registerControl('wt', 'wavetablePositio * Amount of envelope applied wavetable oscillator's position envelope * * @name wtenv - * @group effects + * @tags effects * @param {number | Pattern} amount between 0 and 1 */ export const { wtenv } = registerControl('wtenv'); @@ -112,7 +112,7 @@ export const { wtenv } = registerControl('wtenv'); * Attack time of the wavetable oscillator's position envelope * * @name wtattack - * @group effects + * @tags effects * @synonyms wtatt * @param {number | Pattern} time attack time in seconds */ @@ -122,7 +122,7 @@ export const { wtattack, wtatt } = registerControl('wtattack', 'wtatt'); * Decay time of the wavetable oscillator's position envelope * * @name wtdecay - * @group effects + * @tags effects * @synonyms wtdec * @param {number | Pattern} time decay time in seconds */ @@ -132,7 +132,7 @@ export const { wtdecay, wtdec } = registerControl('wtdecay', 'wtdec'); * Sustain time of the wavetable oscillator's position envelope * * @name wtsustain - * @group effects + * @tags effects * @synonyms wtsus * @param {number | Pattern} gain sustain level (0 to 1) */ @@ -142,7 +142,7 @@ export const { wtsustain, wtsus } = registerControl('wtsustain', 'wtsus'); * Release time of the wavetable oscillator's position envelope * * @name wtrelease - * @group effects + * @tags effects * @synonyms wtrel * @param {number | Pattern} time release time in seconds */ @@ -152,7 +152,7 @@ export const { wtrelease, wtrel } = registerControl('wtrelease', 'wtrel'); * Rate of the LFO for the wavetable oscillator's position * * @name wtrate - * @group effects + * @tags effects * @param {number | Pattern} rate rate in hertz */ export const { wtrate } = registerControl('wtrate'); @@ -160,7 +160,7 @@ export const { wtrate } = registerControl('wtrate'); * cycle synced rate of the LFO for the wavetable oscillator's position * * @name wtsync - * @group effects + * @tags effects * @param {number | Pattern} rate rate in cycles */ export const { wtsync } = registerControl('wtsync'); @@ -169,7 +169,7 @@ export const { wtsync } = registerControl('wtsync'); * Depth of the LFO for the wavetable oscillator's position * * @name wtdepth - * @group effects + * @tags effects * @param {number | Pattern} depth depth of modulation */ export const { wtdepth } = registerControl('wtdepth'); @@ -178,7 +178,7 @@ export const { wtdepth } = registerControl('wtdepth'); * Shape of the LFO for the wavetable oscillator's position * * @name wtshape - * @group effects + * @tags effects * @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..) */ export const { wtshape } = registerControl('wtshape'); @@ -187,7 +187,7 @@ export const { wtshape } = registerControl('wtshape'); * DC offset of the LFO for the wavetable oscillator's position * * @name wtdc - * @group effects + * @tags effects * @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar */ export const { wtdc } = registerControl('wtdc'); @@ -196,7 +196,7 @@ export const { wtdc } = registerControl('wtdc'); * Skew of the LFO for the wavetable oscillator's position * * @name wtskew - * @group effects + * @tags effects * @param {number | Pattern} skew How much to bend the LFO shape */ export const { wtskew } = registerControl('wtskew'); @@ -205,7 +205,7 @@ export const { wtskew } = registerControl('wtskew'); * Amount of warp (alteration of the waveform) to apply to the wavetable oscillator * * @name warp - * @group effects + * @tags effects * @param {number | Pattern} amount Warp of the wavetable from 0 to 1 * @synonyms wavetableWarp * @example @@ -218,7 +218,7 @@ export const { warp, wavetableWarp } = registerControl('warp', 'wavetableWarp'); * Attack time of the wavetable oscillator's warp envelope * * @name warpattack - * @group effects + * @tags effects * @synonyms warpatt * @param {number | Pattern} time attack time in seconds */ @@ -228,7 +228,7 @@ export const { warpattack, warpatt } = registerControl('warpattack', 'warpatt'); * Decay time of the wavetable oscillator's warp envelope * * @name warpdecay - * @group effects + * @tags effects * @synonyms warpdec * @param {number | Pattern} time decay time in seconds */ @@ -238,7 +238,7 @@ export const { warpdecay, warpdec } = registerControl('warpdecay', 'warpdec'); * Sustain time of the wavetable oscillator's warp envelope * * @name warpsustain - * @group effects + * @tags effects * @synonyms warpsus * @param {number | Pattern} gain sustain level (0 to 1) */ @@ -248,7 +248,7 @@ export const { warpsustain, warpsus } = registerControl('warpsustain', 'warpsus' * Release time of the wavetable oscillator's warp envelope * * @name warprelease - * @group effects + * @tags effects * @synonyms warprel * @param {number | Pattern} time release time in seconds */ @@ -258,7 +258,7 @@ export const { warprelease, warprel } = registerControl('warprelease', 'warprel' * Rate of the LFO for the wavetable oscillator's warp * * @name warprate - * @group effects + * @tags effects * @param {number | Pattern} rate rate in hertz */ export const { warprate } = registerControl('warprate'); @@ -267,7 +267,7 @@ export const { warprate } = registerControl('warprate'); * Depth of the LFO for the wavetable oscillator's warp * * @name warpdepth - * @group effects + * @tags effects * @param {number | Pattern} depth depth of modulation */ export const { warpdepth } = registerControl('warpdepth'); @@ -276,7 +276,7 @@ export const { warpdepth } = registerControl('warpdepth'); * Shape of the LFO for the wavetable oscillator's warp * * @name warpshape - * @group effects + * @tags effects * @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..) */ export const { warpshape } = registerControl('warpshape'); @@ -285,7 +285,7 @@ export const { warpshape } = registerControl('warpshape'); * DC offset of the LFO for the wavetable oscillator's warp * * @name warpdc - * @group effects + * @tags effects * @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar */ export const { warpdc } = registerControl('warpdc'); @@ -294,7 +294,7 @@ export const { warpdc } = registerControl('warpdc'); * Skew of the LFO for the wavetable oscillator's warp * * @name warpskew - * @group effects + * @tags effects * @param {number | Pattern} skew How much to bend the LFO shape */ export const { warpskew } = registerControl('warpskew'); @@ -306,7 +306,7 @@ export const { warpskew } = registerControl('warpskew'); * spin, chaos, primes, binary, brownian, reciprocal, wormhole, logistic, sigmoid, fractal, flip * * @name warpmode - * @group effects + * @tags effects * @param {number | string | Pattern} mode Warp mode * @synonyms wavetableWarpMode * @example @@ -320,7 +320,7 @@ export const { warpmode, wavetableWarpMode } = registerControl('warpmode', 'wave * Amount of randomness of the initial phase of the wavetable oscillator. * * @name wtphaserand - * @group effects + * @tags effects * @param {number | Pattern} amount Randomness of the initial phase. Between 0 (not random) and 1 (fully random) * @synonyms wavetablePhaseRand * @example @@ -333,7 +333,7 @@ export const { wtphaserand, wavetablePhaseRand } = registerControl('wtphaserand' * Amount of envelope applied wavetable oscillator's position envelope * * @name warpenv - * @group effects + * @tags effects * @param {number | Pattern} amount between 0 and 1 */ export const { warpenv } = registerControl('warpenv'); @@ -342,7 +342,7 @@ export const { warpenv } = registerControl('warpenv'); * cycle synced rate of the LFO for the wavetable warp position * * @name warpsync - * @group effects + * @tags effects * @param {number | Pattern} rate rate in cycles */ export const { warpsync } = registerControl('warpsync'); @@ -351,7 +351,7 @@ export const { warpsync } = registerControl('warpsync'); * Define a custom webaudio node to use as a sound source. * * @name source - * @group external_io + * @tags external_io * @synonyms src * @param {function} getSource * @synonyms src @@ -364,7 +364,7 @@ export const { source, src } = registerControl('source', 'src'); * `n` can also be used to play midi numbers, but it is recommended to use `note` instead. * * @name n - * @group samples + * @tags samples * @param {number | Pattern} value sample index starting from 0 * @example * s("bd sd [~ bd] sd,hh*6").n("<0 1>") @@ -383,7 +383,7 @@ export const { n } = registerControl('n'); * You can also use midi numbers instead of note names, where 69 is mapped to A4 440Hz in 12EDO. * * @name note - * @group music_theory + * @tags music_theory * @example * note("c a f e") * @example @@ -399,7 +399,7 @@ export const { note } = registerControl(['note', 'n']); * A pattern of numbers that speed up (or slow down) samples while they play. Currently only supported by osc / superdirt. * * @name accelerate - * @group samples + * @tags samples * @param {number | Pattern} amount acceleration. * @superdirtOnly * @example @@ -411,7 +411,7 @@ export const { accelerate } = registerControl('accelerate'); * Sets the velocity from 0 to 1. Is multiplied together with gain. * * @name velocity - * @group effects + * @tags effects * @example * s("hh*8") * .gain(".4!2 1 .4!2 1 .4 1") @@ -422,7 +422,7 @@ export const { velocity } = registerControl('velocity'); * Controls the gain by an exponential amount. * * @name gain - * @group effects + * @tags effects * @param {number | Pattern} amount gain. * @example * s("hh*8").gain(".4!2 1 .4!2 1 .4 1").fast(2) @@ -433,7 +433,7 @@ export const { gain } = registerControl('gain'); * Gain applied after all effects have been processed. * * @name postgain - * @group effects + * @tags effects * @example * s("bd sd [~ bd] sd,hh*8") * .compressor("-20:20:10:.002:.02").postgain(1.5) @@ -444,7 +444,7 @@ export const { postgain } = registerControl('postgain'); * Like `gain`, but linear. * * @name amp - * @group effects + * @tags effects * @param {number | Pattern} amount gain. * @superdirtOnly * @example @@ -456,7 +456,7 @@ export const { amp } = registerControl('amp'); * Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset. * * @name attack - * @group effects + * @tags effects * @param {number | Pattern} attack time in seconds. * @synonyms att * @example @@ -472,7 +472,7 @@ export const { attack, att } = registerControl('attack', 'att'); * while decimal numbers and complex ratios sound metallic. * * @name fmh - * @group effects + * @tags effects * @param {number | Pattern} harmonicity * @example * note("c e g b g e") @@ -487,7 +487,7 @@ export const { fmh } = registerControl(['fmh', 'fmi'], 'fmh'); * Controls the modulation index, which defines the brightness of the sound. * * @name fm - * @group effects + * @tags effects * @param {number | Pattern} brightness modulation index * @synonyms fmi * @example @@ -502,7 +502,7 @@ export const { fmi, fm } = registerControl(['fmi', 'fmh'], 'fm'); * Ramp type of fm envelope. Exp might be a bit broken.. * * @name fmenv - * @group effects + * @tags effects * @param {number | Pattern} type lin | exp * @example * note("c e g b g e") @@ -518,7 +518,7 @@ export const { fmenv } = registerControl('fmenv'); * Attack time for the FM envelope: time it takes to reach maximum modulation * * @name fmattack - * @group effects + * @tags effects * @param {number | Pattern} time attack time * @example * note("c e g b g e") @@ -533,7 +533,7 @@ export const { fmattack } = registerControl('fmattack'); * Waveform of the fm modulator * * @name fmwave - * @group effects + * @tags effects * @param {number | Pattern} wave waveform * @example * n("0 1 2 3".fast(4)).scale("d:minor").s("sine").fmwave("").fm(4).fmh(2.01) @@ -547,7 +547,7 @@ export const { fmwave } = registerControl('fmwave'); * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase. * * @name fmdecay - * @group effects + * @tags effects * @param {number | Pattern} time decay time * @example * note("c e g b g e") @@ -562,7 +562,7 @@ export const { fmdecay } = registerControl('fmdecay'); * Sustain level for the FM envelope: how much modulation is applied after the decay phase * * @name fmsustain - * @group effects + * @tags effects * @param {number | Pattern} level sustain level * @example * note("c e g b g e") @@ -581,7 +581,7 @@ export const { fmvelocity } = registerControl('fmvelocity'); * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`. * * @name bank - * @group samples + * @tags samples * @param {string | Pattern} bank the name of the bank * @example * s("bd sd [~ bd] sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd") @@ -593,7 +593,7 @@ export const { bank } = registerControl('bank'); * mix control for the chorus effect * * @name chorus - * @group effects + * @tags effects * @param {string | Pattern} chorus mix amount between 0 and 1 * @example * note("d d a# a").s("sawtooth").chorus(.5) @@ -611,7 +611,7 @@ export const { fft } = registerControl('fft'); * Note that the decay is only audible if the sustain value is lower than 1. * * @name decay - * @group effects + * @tags effects * @param {number | Pattern} time decay time in seconds * @synonyms dec * @example @@ -623,7 +623,7 @@ export const { decay, dec } = registerControl('decay', 'dec'); * Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset. * * @name sustain - * @group effects + * @tags effects * @param {number | Pattern} gain sustain level between 0 and 1 * @synonyms sus * @example @@ -635,7 +635,7 @@ export const { sustain, sus } = registerControl('sustain', 'sus'); * Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero. * * @name release - * @group effects + * @tags effects * @param {number | Pattern} time release time in seconds * @synonyms rel * @example @@ -650,7 +650,7 @@ export const { hold } = registerControl('hold'); * can also optionally supply the 'bpq' parameter separated by ':'. * * @name bpf - * @group effects + * @tags effects * @param {number | Pattern} frequency center frequency * @synonyms bandf, bp * @example @@ -663,7 +663,7 @@ export const { bandf, bpf, bp } = registerControl(['bandf', 'bandq', 'bpenv'], ' * Sets the **b**and-**p**ass **q**-factor (resonance). * * @name bpq - * @group effects + * @tags effects * @param {number | Pattern} q q factor * @synonyms bandq * @example @@ -678,7 +678,7 @@ export const { bandq, bpq } = registerControl('bandq', 'bpq'); * * @memberof Pattern * @name begin - * @group samples + * @tags samples * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample * @example * samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/dirt-samples') @@ -691,7 +691,7 @@ export const { begin } = registerControl('begin'); * * @memberof Pattern * @name end - * @group samples + * @tags samples * @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc.. * @example * s("bd*2,oh*4").end("<.1 .2 .5 1>").fast(2) @@ -704,7 +704,7 @@ export const { end } = registerControl('end'); * To change the loop region, use loopBegin / loopEnd. * * @name loop - * @group samples + * @tags samples * @param {number | Pattern} on If 1, the sample is looped * @example * s("casio").loop(1) @@ -717,7 +717,7 @@ export const { loop } = registerControl('loop'); * Note: Samples starting with wt_ will automatically loop! (wt = wavetable) * * @name loopBegin - * @group samples + * @tags samples * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample * @synonyms loopb * @example @@ -731,7 +731,7 @@ export const { loopBegin, loopb } = registerControl('loopBegin', 'loopb'); * Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`! * * @name loopEnd - * @group samples + * @tags samples * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample * @synonyms loope * @example @@ -743,7 +743,7 @@ export const { loopEnd, loope } = registerControl('loopEnd', 'loope'); * Bit crusher effect. * * @name crush - * @group effects + * @tags effects * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). * @example * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") @@ -755,7 +755,7 @@ export const { crush } = registerControl('crush'); * Fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers * * @name coarse - * @group effects + * @tags effects * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. * @example * s("bd sd [~ bd] sd,hh*8").coarse("<1 4 8 16 32>") @@ -767,7 +767,7 @@ export const { coarse } = registerControl('coarse'); * Modulate the amplitude of a sound with a continuous waveform * * @name tremolo - * @group effects + * @tags effects * @synonyms trem * @param {number | Pattern} speed modulation speed in HZ * @example @@ -780,7 +780,7 @@ export const { tremolo } = registerControl(['tremolo', 'tremolodepth', 'tremolos * Modulate the amplitude of a sound with a continuous waveform * * @name tremolosync - * @group effects + * @tags effects * @synonyms tremsync * @param {number | Pattern} cycles modulation speed in cycles * @example @@ -796,7 +796,7 @@ export const { tremolosync } = registerControl( * Depth of amplitude modulation * * @name tremolodepth - * @group effects + * @tags effects * @synonyms tremdepth * @param {number | Pattern} depth * @example @@ -808,7 +808,7 @@ export const { tremolodepth } = registerControl('tremolodepth', 'tremdepth'); * Alter the shape of the modulation waveform * * @name tremoloskew - * @group effects + * @tags effects * @synonyms tremskew * @param {number | Pattern} amount between 0 & 1, the shape of the waveform * @example @@ -821,7 +821,7 @@ export const { tremoloskew } = registerControl('tremoloskew', 'tremskew'); * Alter the phase of the modulation waveform * * @name tremolophase - * @group effects + * @tags effects * @synonyms tremphase * @param {number | Pattern} offset the offset in cycles of the modulation * @example @@ -834,7 +834,7 @@ export const { tremolophase } = registerControl('tremolophase', 'tremphase'); * Shape of amplitude modulation * * @name tremoloshape - * @group effects + * @tags effects * @synonyms tremshape * @param {number | Pattern} shape tri | square | sine | saw | ramp * @example @@ -846,7 +846,7 @@ export const { tremoloshape } = registerControl('tremoloshape', 'tremshape'); * Filter overdrive for supported filter types * * @name drive - * @group effects + * @tags effects * @param {number | Pattern} amount * @example * note("{f g g c d a a#}%16".sub(17)).s("supersaw").lpenv(8).lpf(150).lpq(.8).ftype('ladder').drive("<.5 4>") @@ -860,7 +860,7 @@ export const { drive } = registerControl('drive'); * Can be applied to multiple orbits with the ':' mininotation, e.g. `duckorbit("2:3")` * * @name duckorbit - * @group effects + * @tags effects * @synonyms duck * @param {number | Pattern} orbit target orbit * @example @@ -881,7 +881,7 @@ export const { duck } = registerControl('duckorbit', 'duck'); * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`. * * @name duckdepth - * @group effects + * @tags effects * @param {number | Pattern} depth depth of modulation from 0 to 1 * @example * stack( n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2), s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack(0.2).duckdepth("<1 .9 .6 0>")) @@ -901,7 +901,7 @@ export const { duckdepth } = registerControl('duckdepth'); * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`. * * @name duckonset - * @group effects + * @tags effects * @synonyms duckons * * @param {number | Pattern} time The onset time in seconds @@ -929,7 +929,7 @@ export const { duckonset } = registerControl('duckonset', 'duckons'); * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`. * * @name duckattack - * @group effects + * @tags effects * @synonyms duckatt * * @param {number | Pattern} time The attack time in seconds @@ -974,7 +974,7 @@ export const { byteBeatStartTime, bbst } = registerControl('byteBeatStartTime', * Allows you to set the output channels on the interface * * @name channels - * @group external_io + * @tags external_io * @synonyms ch * * @param {number | Pattern} channels pattern the output channels @@ -988,7 +988,7 @@ export const { channels, ch } = registerControl('channels', 'ch'); * Controls the pulsewidth of the pulse oscillator * * @name pw - * @group effects + * @tags effects * @param {number | Pattern} pulsewidth * @example * note("{f a c e}%16").s("pulse").pw(".8:1:.2") @@ -1001,7 +1001,7 @@ export const { pw } = registerControl(['pw', 'pwrate', 'pwsweep']); * Controls the lfo rate for the pulsewidth of the pulse oscillator * * @name pwrate - * @group effects + * @tags effects * @param {number | Pattern} rate * @example * n(run(8)).scale("D:pentatonic").s("pulse").pw("0.5").pwrate("<5 .1 25>").pwsweep("<0.3 .8>") @@ -1014,7 +1014,7 @@ export const { pwrate } = registerControl('pwrate'); * Controls the lfo sweep for the pulsewidth of the pulse oscillator * * @name pwsweep - * @group effects + * @tags effects * @param {number | Pattern} sweep * @example * n(run(8)).scale("D:pentatonic").s("pulse").pw("0.5").pwrate("<5 .1 25>").pwsweep("<0.3 .8>") @@ -1026,7 +1026,7 @@ export const { pwsweep } = registerControl('pwsweep'); * Phaser audio effect that approximates popular guitar pedals. * * @name phaser - * @group effects + * @tags effects * @synonyms ph * @param {number | Pattern} speed speed of modulation * @example @@ -1044,7 +1044,7 @@ export const { phaserrate, ph, phaser } = registerControl( * The frequency sweep range of the lfo for the phaser effect. Defaults to 2000 * * @name phasersweep - * @group effects + * @tags effects * @synonyms phs * @param {number | Pattern} phasersweep most useful values are between 0 and 4000 * @example @@ -1058,7 +1058,7 @@ export const { phasersweep, phs } = registerControl('phasersweep', 'phs'); * The center frequency of the phaser in HZ. Defaults to 1000 * * @name phasercenter - * @group effects + * @tags effects * @synonyms phc * @param {number | Pattern} centerfrequency in HZ * @example @@ -1073,7 +1073,7 @@ export const { phasercenter, phc } = registerControl('phasercenter', 'phc'); * The amount the signal is affected by the phaser effect. Defaults to 0.75 * * @name phaserdepth - * @group effects + * @tags effects * @synonyms phd, phasdp * @param {number | Pattern} depth number between 0 and 1 * @example @@ -1088,7 +1088,7 @@ export const { phaserdepth, phd, phasdp } = registerControl('phaserdepth', 'phd' * Choose the channel the pattern is sent to in superdirt * * @name channel - * @group effects + * @tags effects * @param {number | Pattern} channel channel number * */ @@ -1097,7 +1097,7 @@ export const { channel } = registerControl('channel'); * In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open. * * @name cut - * @group effects + * @tags effects * @param {number | Pattern} group cut group number * @example * s("[oh hh]*4").cut(1) @@ -1110,7 +1110,7 @@ export const { cut } = registerControl('cut'); * When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'. * * @name lpf - * @group effects + * @tags effects * @param {number | Pattern} frequency audible between 0 and 20000 * @synonyms cutoff, ctf, lp * @example @@ -1124,7 +1124,7 @@ export const { cutoff, ctf, lpf, lp } = registerControl(['cutoff', 'resonance', /** * Sets the lowpass filter envelope modulation depth. * @name lpenv - * @group effects + * @tags effects * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_ * @synonyms lpe * @example @@ -1138,7 +1138,7 @@ export const { lpenv, lpe } = registerControl('lpenv', 'lpe'); /** * Sets the highpass filter envelope modulation depth. * @name hpenv - * @group effects + * @tags effects * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_ * @synonyms hpe * @example @@ -1152,7 +1152,7 @@ export const { hpenv, hpe } = registerControl('hpenv', 'hpe'); /** * Sets the bandpass filter envelope modulation depth. * @name bpenv - * @group effects + * @tags effects * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_ * @synonyms bpe * @example @@ -1166,7 +1166,7 @@ export const { bpenv, bpe } = registerControl('bpenv', 'bpe'); /** * Sets the attack duration for the lowpass filter envelope. * @name lpattack - * @group effects + * @tags effects * @param {number | Pattern} attack time of the filter envelope * @synonyms lpa * @example @@ -1180,7 +1180,7 @@ export const { lpattack, lpa } = registerControl('lpattack', 'lpa'); /** * Sets the attack duration for the highpass filter envelope. * @name hpattack - * @group effects + * @tags effects * @param {number | Pattern} attack time of the highpass filter envelope * @synonyms hpa * @example @@ -1194,7 +1194,7 @@ export const { hpattack, hpa } = registerControl('hpattack', 'hpa'); /** * Sets the attack duration for the bandpass filter envelope. * @name bpattack - * @group effects + * @tags effects * @param {number | Pattern} attack time of the bandpass filter envelope * @synonyms bpa * @example @@ -1208,7 +1208,7 @@ export const { bpattack, bpa } = registerControl('bpattack', 'bpa'); /** * Sets the decay duration for the lowpass filter envelope. * @name lpdecay - * @group effects + * @tags effects * @param {number | Pattern} decay time of the filter envelope * @synonyms lpd * @example @@ -1222,7 +1222,7 @@ export const { lpdecay, lpd } = registerControl('lpdecay', 'lpd'); /** * Sets the decay duration for the highpass filter envelope. * @name hpdecay - * @group effects + * @tags effects * @param {number | Pattern} decay time of the highpass filter envelope * @synonyms hpd * @example @@ -1237,7 +1237,7 @@ export const { hpdecay, hpd } = registerControl('hpdecay', 'hpd'); /** * Sets the decay duration for the bandpass filter envelope. * @name bpdecay - * @group effects + * @tags effects * @param {number | Pattern} decay time of the bandpass filter envelope * @synonyms bpd * @example @@ -1252,7 +1252,7 @@ export const { bpdecay, bpd } = registerControl('bpdecay', 'bpd'); /** * Sets the sustain amplitude for the lowpass filter envelope. * @name lpsustain - * @group effects + * @tags effects * @param {number | Pattern} sustain amplitude of the lowpass filter envelope * @synonyms lps * @example @@ -1267,7 +1267,7 @@ export const { lpsustain, lps } = registerControl('lpsustain', 'lps'); /** * Sets the sustain amplitude for the highpass filter envelope. * @name hpsustain - * @group effects + * @tags effects * @param {number | Pattern} sustain amplitude of the highpass filter envelope * @synonyms hps * @example @@ -1282,7 +1282,7 @@ export const { hpsustain, hps } = registerControl('hpsustain', 'hps'); /** * Sets the sustain amplitude for the bandpass filter envelope. * @name bpsustain - * @group effects + * @tags effects * @param {number | Pattern} sustain amplitude of the bandpass filter envelope * @synonyms bps * @example @@ -1297,7 +1297,7 @@ export const { bpsustain, bps } = registerControl('bpsustain', 'bps'); /** * Sets the release time for the lowpass filter envelope. * @name lprelease - * @group effects + * @tags effects * @param {number | Pattern} release time of the filter envelope * @synonyms lpr * @example @@ -1313,7 +1313,7 @@ export const { lprelease, lpr } = registerControl('lprelease', 'lpr'); /** * Sets the release time for the highpass filter envelope. * @name hprelease - * @group effects + * @tags effects * @param {number | Pattern} release time of the highpass filter envelope * @synonyms hpr * @example @@ -1329,7 +1329,7 @@ export const { hprelease, hpr } = registerControl('hprelease', 'hpr'); /** * Sets the release time for the bandpass filter envelope. * @name bprelease - * @group effects + * @tags effects * @param {number | Pattern} release time of the bandpass filter envelope * @synonyms bpr * @example @@ -1345,7 +1345,7 @@ export const { bprelease, bpr } = registerControl('bprelease', 'bpr'); /** * Sets the filter type. The ladder filter is more aggressive. More types might be added in the future. * @name ftype - * @group effects + * @tags effects * @param {number | Pattern} type 12db (0), ladder (1), or 24db (2) * @example * note("{f g g c d a a#}%8").s("sawtooth").lpenv(4).lpf(500).ftype("<0 1 2>").lpq(1) @@ -1361,7 +1361,7 @@ export const { ftype } = registerControl('ftype'); /** * controls the center of the filter envelope. 0 is unipolar positive, .5 is bipolar, 1 is unipolar negative * @name fanchor - * @group effects + * @tags effects * @param {number | Pattern} center 0 to 1 * @example * note("{f g g c d a a#}%8").s("sawtooth").lpf("{1000}%2") @@ -1374,7 +1374,7 @@ export const { fanchor } = registerControl('fanchor'); * When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'. * * @name hpf - * @group effects + * @tags effects * @param {number | Pattern} frequency audible between 0 and 20000 * @synonyms hp, hcutoff * @example @@ -1389,7 +1389,7 @@ export const { fanchor } = registerControl('fanchor'); * Applies a vibrato to the frequency of the oscillator. * * @name vib - * @group effects + * @tags effects * @synonyms vibrato, v * @param {number | Pattern} frequency of the vibrato in hertz * @example @@ -1407,7 +1407,7 @@ export const { vib, vibrato, v } = registerControl(['vib', 'vibmod'], 'vibrato', * Adds pink noise to the mix * * @name noise - * @group generators + * @tags generators * @param {number | Pattern} wet wet amount * @example * sound("/2") @@ -1417,7 +1417,7 @@ export const { noise } = registerControl('noise'); * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set * * @name vibmod - * @group effects + * @tags effects * @synonyms vmod * @param {number | Pattern} depth of vibrato (in semitones) * @example @@ -1436,7 +1436,7 @@ export const { hcutoff, hpf, hp } = registerControl(['hcutoff', 'hresonance', 'h * Controls the **h**igh-**p**ass **q**-value. * * @name hpq - * @group effects + * @tags effects * @param {number | Pattern} q resonance factor between 0 and 50 * @synonyms hresonance * @example @@ -1448,7 +1448,7 @@ export const { hresonance, hpq } = registerControl('hresonance', 'hpq'); * Controls the **l**ow-**p**ass **q**-value. * * @name lpq - * @group effects + * @tags effects * @param {number | Pattern} q resonance factor between 0 and 50 * @synonyms resonance * @example @@ -1461,7 +1461,7 @@ export const { resonance, lpq } = registerControl('resonance', 'lpq'); * DJ filter, below 0.5 is low pass filter, above is high pass filter. * * @name djf - * @group effects + * @tags effects * @param {number | Pattern} cutoff below 0.5 is low pass filter, above is high pass filter * @example * n(irand(16).seg(8)).scale("d:phrygian").s("supersaw").djf("<.5 .3 .2 .75>") @@ -1478,7 +1478,7 @@ export const { djf } = registerControl('djf'); * * * @name delay - * @group effects + * @tags effects * @param {number | Pattern} level between 0 and 1 * @example * s("bd bd").delay("<0 .25 .5 1>") @@ -1492,7 +1492,7 @@ export const { delay } = registerControl(['delay', 'delaytime', 'delayfeedback'] * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it * * @name delayfeedback - * @group effects + * @tags effects * @param {number | Pattern} feedback between 0 and 1 * @synonyms delayfb, dfb * @example @@ -1506,7 +1506,7 @@ export const { delayfeedback, delayfb, dfb } = registerControl('delayfeedback', * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it * * @name delayfeedback - * @group effects + * @tags effects * @param {number | Pattern} feedback between 0 and 1 * @synonyms delayfb, dfb * @example @@ -1518,7 +1518,7 @@ export const { delayspeed } = registerControl('delayspeed'); * Sets the time of the delay effect. * * @name delayspeed - * @group effects + * @tags effects, foo * @param {number | Pattern} delayspeed controls the pitch of the delay feedback * @synonyms delayt, dt * @example @@ -1531,7 +1531,7 @@ export const { delaytime, delayt, dt } = registerControl('delaytime', 'delayt', * Sets the time of the delay effect in cycles. * * @name delaysync - * @group effects + * @tags effects * @param {number | Pattern} cycles delay length in cycles * @synonyms delayt, dt * @example @@ -1544,7 +1544,7 @@ export const { delaysync } = registerControl('delaysync'); * Specifies whether delaytime is calculated relative to cps. * * @name lock - * @group effects + * @tags effects, foo * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. * @superdirtOnly * @example @@ -1558,7 +1558,7 @@ export const { lock } = registerControl('lock'); * Set detune for stacked voices of supported oscillators * * @name detune - * @group effects + * @tags effects * @param {number | Pattern} amount * @synonyms det * @example @@ -1570,7 +1570,7 @@ export const { detune, det } = registerControl('detune', 'det'); * Set number of stacked voices for supported oscillators * * @name unison - * @group effects + * @tags effects * @param {number | Pattern} numvoices * @example * note("d f a a# a d3").fast(2).s("supersaw").unison("<1 2 7>") @@ -1582,7 +1582,7 @@ export const { unison } = registerControl('unison'); * Set the stereo pan spread for supported oscillators * * @name spread - * @group effects + * @tags effects * @param {number | Pattern} spread between 0 and 1 * @example * note("d f a a# a d3").fast(2).s("supersaw").spread("<0 .3 1>") @@ -1593,7 +1593,7 @@ export const { spread } = registerControl('spread'); * Set dryness of reverb. See `room` and `size` for more information about reverb. * * @name dry - * @group effects + * @tags effects * @param {number | Pattern} dry 0 = wet, 1 = dry * @example * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() @@ -1619,7 +1619,7 @@ export const { fadeInTime } = registerControl('fadeInTime'); * Set frequency of sound. * * @name freq - * @group transforms + * @tags transforms * @param {number | Pattern} frequency in Hz. the audible range is between 20 and 20000 Hz * @example * freq("220 110 440 110").s("superzow").osc() @@ -1633,7 +1633,7 @@ export const { freq } = registerControl('freq'); * Attack time of pitch envelope. * * @name pattack - * @group effects + * @tags effects * @synonyms patt * @param {number | Pattern} time time in seconds * @example @@ -1645,7 +1645,7 @@ export const { pattack, patt } = registerControl('pattack', 'patt'); * Decay time of pitch envelope. * * @name pdecay - * @group effects + * @tags effects * @synonyms pdec * @param {number | Pattern} time time in seconds * @example @@ -1659,7 +1659,7 @@ export const { psustain, psus } = registerControl('psustain', 'psus'); * Release time of pitch envelope * * @name prelease - * @group effects + * @tags effects * @synonyms prel * @param {number | Pattern} time time in seconds * @example @@ -1674,7 +1674,7 @@ export const { prelease, prel } = registerControl('prelease', 'prel'); * If you don't set other pitch envelope controls, `pattack:.2` will be the default. * * @name penv - * @group effects + * @tags effects * @param {number | Pattern} semitones change in semitones * @example * note("c") @@ -1686,7 +1686,7 @@ export const { penv } = registerControl('penv'); * Curve of envelope. Defaults to linear. exponential is good for kicks * * @name pcurve - * @group effects + * @tags effects * @param {number | Pattern} type 0 = linear, 1 = exponential * @example * note("g1*4") @@ -1703,7 +1703,7 @@ export const { pcurve } = registerControl('pcurve'); * If you don't set an anchor, the value will default to the psustain value. * * @name panchor - * @group effects + * @tags effects * @param {number | Pattern} anchor anchor offset * @example * note("c c4").penv(12).panchor("<0 .5 1 .5>") @@ -1725,7 +1725,7 @@ export const { gate, gat } = registerControl('gate', 'gat'); * Emulation of a Leslie speaker: speakers rotating in a wooden amplified cabinet. * * @name leslie - * @group effects + * @tags effects * @param {number | Pattern} wet between 0 and 1 * @example * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() @@ -1737,7 +1737,7 @@ export const { leslie } = registerControl('leslie'); * Rate of modulation / rotation for leslie effect * * @name lrate - * @group effects + * @tags effects * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow * @example * n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() @@ -1750,7 +1750,7 @@ export const { lrate } = registerControl('lrate'); * Physical size of the cabinet in meters. Be careful, it might be slightly larger than your computer. Affects the Doppler amount (pitch warble) * * @name lsize - * @group effects + * @tags effects * @param {number | Pattern} meters somewhere between 0 and 1 * @example * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() @@ -1762,7 +1762,7 @@ export const { lsize } = registerControl('lsize'); * Sets the displayed text for an event on the pianoroll * * @name label - * @group visualization + * @tags visualization * @param {string} label text to display */ export const { activeLabel } = registerControl('activeLabel'); @@ -1828,7 +1828,7 @@ export const { overshape } = registerControl('overshape'); * Sets position in stereo. * * @name pan - * @group effects + * @tags effects * @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel) * @example * s("[bd hh]*2").pan("<.5 1 .5 0>") @@ -1894,7 +1894,7 @@ export const { mode } = registerControl(['mode', 'anchor']); * When using mininotation, you can also optionally add the 'size' parameter, separated by ':'. * * @name room - * @group effects + * @tags effects * @param {number | Pattern} level between 0 and 1 * @example * s("bd sd [~ bd] sd").room("<0 .2 .4 .6 .8 1>") @@ -1908,7 +1908,7 @@ export const { room } = registerControl(['room', 'size']); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomlp - * @group effects + * @tags effects * @synonyms rlp * @param {number} frequency between 0 and 20000hz * @example @@ -1922,7 +1922,7 @@ export const { roomlp, rlp } = registerControl('roomlp', 'rlp'); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomdim - * @group effects + * @tags effects * @synonyms rdim * @param {number} frequency between 0 and 20000hz * @example @@ -1937,7 +1937,7 @@ export const { roomdim, rdim } = registerControl('roomdim', 'rdim'); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomfade - * @group effects + * @tags effects * @synonyms rfade * @param {number} seconds for the reverb to fade * @example @@ -1950,7 +1950,7 @@ export const { roomfade, rfade } = registerControl('roomfade', 'rfade'); /** * Sets the sample to use as an impulse response for the reverb. * @name iresponse - * @group effects + * @tags effects * @param {string | Pattern} sample to use as an impulse response * @synonyms ir * @example @@ -1962,7 +1962,7 @@ export const { ir, iresponse } = registerControl(['ir', 'i'], 'iresponse'); /** * Sets speed of the sample for the impulse response. * @name irspeed - * @group effects + * @tags effects * @param {string | Pattern} speed * @example * samples('github:switchangel/pad') @@ -1974,7 +1974,7 @@ export const { irspeed } = registerControl('irspeed'); /** * Sets the beginning of the IR response sample * @name irbegin - * @group effects + * @tags effects * @param {string | Pattern} begin between 0 and 1 * @synonyms ir * @example @@ -1988,7 +1988,7 @@ export const { irbegin } = registerControl('irbegin'); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomsize - * @group effects + * @tags effects * @param {number | Pattern} size between 0 and 10 * @synonyms rsize, sz, size * @example @@ -2012,7 +2012,7 @@ export const { roomsize, size, sz, rsize } = registerControl('roomsize', 'size', * * * @name shape - * @group effects + * @tags effects * @param {number | Pattern} distortion between 0 and 1 * @example * s("bd sd [~ bd] sd,hh*8").shape("<0 .2 .4 .6 .8>") @@ -2025,7 +2025,7 @@ export const { shape } = registerControl(['shape', 'shapevol']); * Most useful values are usually between 0 and 10 (depending on source gain). If you are feeling adventurous, you can turn it up to 11 and beyond ;) * * @name distort - * @group effects + * @tags effects * @synonyms dist * @param {number | Pattern} distortion * @example @@ -2040,7 +2040,7 @@ export const { distort, dist } = registerControl(['distort', 'distortvol'], 'dis * More info [here](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode?retiredLocale=de#instance_properties) * * @name compressor - * @group effects + * @tags effects * @example * s("bd sd [~ bd] sd,hh*8") * .compressor("-20:20:10:.002:.02") @@ -2061,7 +2061,7 @@ export const { compressorRelease } = registerControl('compressorRelease'); * Changes the speed of sample playback, i.e. a cheap way of changing pitch. * * @name speed - * @group effects + * @tags effects * @param {number | Pattern} speed -inf to inf, negative numbers play the sample backwards. * @example * s("bd*6").speed("1 2 4 1 -2 -4") @@ -2075,7 +2075,7 @@ export const { speed } = registerControl('speed'); * Changes the speed of sample playback, i.e. a cheap way of changing pitch. * * @name stretch - * @group effects + * @tags effects * @param {number | Pattern} factor -inf to inf, negative numbers play the sample backwards. * @example * s("gm_flute").stretch("1 2 .5") @@ -2086,7 +2086,7 @@ export const { stretch } = registerControl('stretch'); * Used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`. * * @name unit - * @group effects + * @tags effects * @param {number | string | Pattern} unit see description above * @example * speed("1 2 .5 3").s("bd").unit("c").osc() @@ -2101,7 +2101,7 @@ export const { unit } = registerControl('unit'); * "A simplistic pitch-raising algorithm. It's not meant to sound natural; its sound is reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter, depending on the input. The algorithm works by cutting the signal into fragments (delimited by upwards-going zero-crossings) and squeezing those fragments in the time domain (i.e. simply playing them back faster than they came in), leaving silences inbetween. All the parameters apart from memlen can be modulated." * * @name squiz - * @group effects + * @tags effects * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc. * @example * squiz("2 4/2 6 [8 16]").s("bd").osc() @@ -2126,7 +2126,7 @@ export const { squiz } = registerControl('squiz'); * Formant filter to make things sound like vowels. * * @name vowel - * @group effects + * @tags effects * @param {string | Pattern} vowel You can use a e i o u ae aa oe ue y uh un en an on, corresponding to [a] [e] [i] [o] [u] [æ] [ɑ] [ø] [y] [ɯ] [ʌ] [œ̃] [ɛ̃] [ɑ̃] [ɔ̃]. Aliases: aa = å = ɑ, oe = ø = ö, y = ı, ae = æ. * @example * note("[c2 >]*2").s('sawtooth') @@ -2151,7 +2151,7 @@ export const { waveloss } = registerControl('waveloss'); * Noise crackle density * * @name density - * @group effects + * @tags effects * @param {number | Pattern} density between 0 and x * @example * s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(4)) @@ -2202,7 +2202,7 @@ export const { cps } = registerControl('cps'); * Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration. * * @name clip - * @group transforms + * @tags transforms * @synonyms legato * @param {number | Pattern} factor >= 0 * @example @@ -2215,7 +2215,7 @@ export const { clip, legato } = registerControl('clip', 'legato'); * Sets the duration of the event in cycles. Similar to clip / legato, it also cuts samples off at the end if they exceed the duration. * * @name duration - * @group transforms + * @tags transforms * @synonyms dur * @param {number | Pattern} seconds >= 0 * @example @@ -2244,7 +2244,7 @@ export const { zzfx } = registerControl('zzfx'); /** * Sets the color of the hap in visualizations like pianoroll or highlighting. * @name color - * @group visualization + * @tags visualization * @synonyms colour * @param {string} color Hexadecimal or CSS color name */ @@ -2259,7 +2259,7 @@ export let createParams = (...names) => * ADSR envelope: Combination of Attack, Decay, Sustain, and Release. * * @name adsr - * @group effects + * @tags effects * @param {number | Pattern} time attack time in seconds * @param {number | Pattern} time decay time in seconds * @param {number | Pattern} gain sustain level (0 to 1) @@ -2294,7 +2294,7 @@ export const ar = register('ar', (t, pat) => { * MIDI channel: Sets the MIDI channel for the event. * * @name midichan - * @group examples + * @tags examples * @param {number | Pattern} channel MIDI channel number (0-15) * @example * note("c4").midichan(1).midi() @@ -2307,7 +2307,7 @@ export const { midimap } = registerControl('midimap'); * MIDI port: Sets the MIDI port for the event. * * @name midiport - * @group examples + * @tags examples * @param {number | Pattern} port MIDI port * @example * note("c a f e").midiport("<0 1 2 3>").midi() @@ -2318,7 +2318,7 @@ export const { midiport } = registerControl('midiport'); * MIDI command: Sends a MIDI command message. * * @name midicmd - * @group examples + * @tags examples * @param {number | Pattern} command MIDI command * @example * midicmd("clock*48,/2").midi() @@ -2329,7 +2329,7 @@ export const { midicmd } = registerControl('midicmd'); * MIDI control: Sends a MIDI control change message. * * @name control - * @group examples + * @tags examples * @param {number | Pattern} MIDI control number (0-127) * @param {number | Pattern} MIDI controller value (0-127) */ @@ -2345,7 +2345,7 @@ export const control = register('control', (args, pat) => { * MIDI control number: Sends a MIDI control change message. * * @name ccn - * @group examples + * @tags examples * @param {number | Pattern} MIDI control number (0-127) */ export const { ccn } = registerControl('ccn'); @@ -2353,7 +2353,7 @@ export const { ccn } = registerControl('ccn'); * MIDI control value: Sends a MIDI control change message. * * @name ccv - * @group examples + * @tags examples * @param {number | Pattern} MIDI control value (0-127) */ export const { ccv } = registerControl('ccv'); @@ -2363,7 +2363,7 @@ export const { ctlNum } = registerControl('ctlNum'); /** * MIDI NRPN non-registered parameter number: Sends a MIDI NRPN non-registered parameter number message. * @name nrpnn - * @group examples + * @tags examples * @param {number | Pattern} nrpnn MIDI NRPN non-registered parameter number (0-127) * @example * note("c4").nrpnn("1:8").nrpv("123").midichan(1).midi() @@ -2372,7 +2372,7 @@ export const { nrpnn } = registerControl('nrpnn'); /** * MIDI NRPN non-registered parameter value: Sends a MIDI NRPN non-registered parameter value message. * @name nrpv - * @group examples + * @tags examples * @param {number | Pattern} nrpv MIDI NRPN non-registered parameter value (0-127) * @example * note("c4").nrpnn("1:8").nrpv("123").midichan(1).midi() @@ -2383,7 +2383,7 @@ export const { nrpv } = registerControl('nrpv'); * MIDI program number: Sends a MIDI program change message. * * @name progNum - * @group examples + * @tags examples * @param {number | Pattern} program MIDI program number (0-127) * @example * note("c4").progNum(10).midichan(1).midi() @@ -2393,7 +2393,7 @@ export const { progNum } = registerControl('progNum'); /** * MIDI sysex: Sends a MIDI sysex message. * @name sysex - * @group examples + * @tags examples * @param {number | Pattern} id Sysex ID * @param {number | Pattern} data Sysex data * @example @@ -2409,7 +2409,7 @@ export const sysex = register('sysex', (args, pat) => { /** * MIDI sysex ID: Sends a MIDI sysex identifier message. * @name sysexid - * @group examples + * @tags examples * @param {number | Pattern} id Sysex ID * @example * note("c4").sysexid("0x77").sysexdata("0x01:0x02:0x03:0x04").midichan(1).midi() @@ -2418,7 +2418,7 @@ export const { sysexid } = registerControl('sysexid'); /** * MIDI sysex data: Sends a MIDI sysex message. * @name sysexdata - * @group examples + * @tags examples * @param {number | Pattern} data Sysex data * @example * note("c4").sysexid("0x77").sysexdata("0x01:0x02:0x03:0x04").midichan(1).midi() @@ -2428,7 +2428,7 @@ export const { sysexdata } = registerControl('sysexdata'); /** * MIDI pitch bend: Sends a MIDI pitch bend message. * @name midibend - * @group examples + * @tags examples * @param {number | Pattern} midibend MIDI pitch bend (-1 - 1) * @example * note("c4").midibend(sine.slow(4).range(-0.4,0.4)).midi() @@ -2437,7 +2437,7 @@ export const { midibend } = registerControl('midibend'); /** * MIDI key after touch: Sends a MIDI key after touch message. * @name miditouch - * @group examples + * @tags examples * @param {number | Pattern} miditouch MIDI key after touch (0-1) * @example * note("c4").miditouch(sine.slow(4).range(0,1)).midi() @@ -2458,7 +2458,7 @@ export const getControlName = (alias) => { * Sets properties in a batch. * * @name as - * @group transforms + * @tags transforms * @param {String | Array} mapping the control names that are set * @example * "c:.5 a:1 f:.25 e:.8".as("note:clip") @@ -2478,7 +2478,7 @@ export const as = register('as', (mapping, pat) => { * Allows you to scrub an audio file like a tape loop by passing values that represents the position in the audio file * in the optional array syntax ex: "0.5:2", the second value controls the speed of playback * @name scrub - * @group samples + * @tags samples * @memberof Pattern * @returns Pattern * @example diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index e3d4f5f9c..39d6555f5 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -84,7 +84,7 @@ export class Pattern { /** * Returns a new pattern, with the function applied to the value of * each hap. It has the alias `fmap`. - * @group functional + * @tags functional * @synonyms fmap * @param {Function} func to to apply to the value * @returns Pattern @@ -117,7 +117,7 @@ export class Pattern { * Assumes 'this' is a pattern of functions, and given a function to * resolve wholes, applies a given pattern of values to that * pattern of functions. - * @group functional + * @tags functional * @param {Function} whole_func * @param {Function} func * @noAutocomplete @@ -155,7 +155,7 @@ export class Pattern { * In this `_appBoth` variant, where timespans of the function and value haps * are not the same but do intersect, the resulting hap has a timespan of the * intersection. This applies to both the part and the whole timespan. - * @group functional + * @tags functional * @param {Pattern} pat_val * @noAutocomplete * @returns Pattern @@ -183,7 +183,7 @@ export class Pattern { * on. In practice, this means that the pattern structure, including onsets, * are preserved from the pattern of functions (often referred to as the left * hand or inner pattern). - * @group functional + * @tags functional * @param {Pattern} pat_val * @noAutocomplete * @returns Pattern @@ -217,7 +217,7 @@ export class Pattern { * As with `appLeft`, but `whole` timespans are instead taken from the * pattern of values, i.e. structure is preserved from the right hand/outer * pattern. - * @group functional + * @tags functional * @param {Pattern} pat_val * @noAutocomplete * @returns Pattern @@ -408,7 +408,7 @@ export class Pattern { /** * Query haps inside the given time span. * - * @group internals + * @tags internals * @param {Fraction | number} begin from time * @param {Fraction | number} end to time * @returns Hap[] @@ -432,7 +432,7 @@ export class Pattern { * Returns a new pattern, with queries split at cycle boundaries. This makes * some calculations easier to express, as all haps are then constrained to * happen within a cycle. - * @group internals + * @tags internals * @returns Pattern * @noAutocomplete */ @@ -447,7 +447,7 @@ export class Pattern { /** * Returns a new pattern, where the given function is applied to the query * timespan before passing it to the original pattern. - * @group internals + * @tags internals * @param {Function} func the function to apply * @returns Pattern * @noAutocomplete @@ -470,7 +470,7 @@ export class Pattern { /** * As with `withQuerySpan`, but the function is applied to both the * begin and end time of the query timespan. - * @group internals + * @tags internals * @param {Function} func the function to apply * @returns Pattern * @noAutocomplete @@ -483,7 +483,7 @@ export class Pattern { * Similar to `withQuerySpan`, but the function is applied to the timespans * of all haps returned by pattern queries (both `part` timespans, and where * present, `whole` timespans). - * @group internals + * @tags internals * @param {Function} func * @returns Pattern * @noAutocomplete @@ -495,7 +495,7 @@ export class Pattern { /** * As with `withHapSpan`, but the function is applied to both the * begin and end time of the hap timespans. - * @group internals + * @tags internals * @param {Function} func the function to apply * @returns Pattern * @noAutocomplete @@ -506,7 +506,7 @@ export class Pattern { /** * Returns a new pattern with the given function applied to the list of haps returned by every query. - * @group internals + * @tags internals * @param {Function} func * @returns Pattern * @noAutocomplete @@ -519,7 +519,7 @@ export class Pattern { /** * As with `withHaps`, but applies the function to every hap, rather than every list of haps. - * @group internals + * @tags internals * @param {Function} func * @returns Pattern * @noAutocomplete @@ -530,7 +530,7 @@ export class Pattern { /** * Returns a new pattern with the context field set to every hap set to the given value. - * @group internals + * @tags internals * @param {*} context * @returns Pattern * @noAutocomplete @@ -541,7 +541,7 @@ export class Pattern { /** * Returns a new pattern with the given function applied to the context field of every hap. - * @group internals + * @tags internals * @param {Function} func * @returns Pattern * @noAutocomplete @@ -557,7 +557,7 @@ export class Pattern { /** * Returns a new pattern with the context field of every hap set to an empty object. - * @group internals + * @tags internals * @returns Pattern * @noAutocomplete */ @@ -568,7 +568,7 @@ export class Pattern { /** * Returns a new pattern with the given location information added to the * context of every hap. - * @group internals + * @tags internals * @param {Number} start start offset * @param {Number} end end offset * @returns Pattern @@ -592,7 +592,7 @@ export class Pattern { /** * Returns a new Pattern, which only returns haps that meet the given test. - * @group internals + * @tags internals * @param {Function} hap_test - a function which returns false for haps to be removed from the pattern * @returns Pattern * @noAutocomplete @@ -604,7 +604,7 @@ export class Pattern { /** * As with `filterHaps`, but the function is applied to values * inside haps. - * @group internals + * @tags internals * @param {Function} value_test * @returns Pattern * @noAutocomplete @@ -616,7 +616,7 @@ export class Pattern { /** * Returns a new pattern, with haps containing undefined values removed from * query results. - * @group internals + * @tags internals * @returns Pattern * @noAutocomplete */ @@ -628,7 +628,7 @@ export class Pattern { * Returns a new pattern, with all haps without onsets filtered out. A hap * with an onset is one with a `whole` timespan that begins at the same time * as its `part` timespan. - * @group internals + * @tags internals * @returns Pattern * @noAutocomplete */ @@ -642,7 +642,7 @@ export class Pattern { /** * Returns a new pattern, with 'continuous' haps (those without 'whole' * timespans) removed from query results. - * @group internals + * @tags internals * @returns Pattern * @noAutocomplete */ @@ -654,7 +654,7 @@ export class Pattern { /** * Combines adjacent haps with the same value and whole. Only * intended for use in tests. - * @group internals + * @tags internals * @noAutocomplete */ defragmentHaps() { @@ -707,7 +707,7 @@ export class Pattern { /** * Queries the pattern for the first cycle, returning Haps. Mainly of use when * debugging a pattern. - * @group internals + * @tags internals * @param {Boolean} with_context - set to true, otherwise the context field * will be stripped from the resulting haps. * @returns [Hap] @@ -723,7 +723,7 @@ export class Pattern { /** * Accessor for a list of values returned by querying the first cycle. - * @group internals + * @tags internals * @noAutocomplete */ get firstCycleValues() { @@ -732,7 +732,7 @@ export class Pattern { /** * More human-readable version of the `firstCycleValues` accessor. - * @group internals + * @tags internals * @noAutocomplete */ get showFirstCycle() { @@ -744,7 +744,7 @@ export class Pattern { /** * Returns a new pattern, which returns haps sorted in temporal order. Mainly * of use when comparing two patterns for equality, in tests. - * @group internals + * @tags internals * @returns Pattern * @noAutocomplete */ @@ -761,7 +761,7 @@ export class Pattern { /** * Returns a new pattern with all values parsed as numerals. - * @group internals + * @tags internals */ asNumber() { return this.fmap(parseNumeral); @@ -813,7 +813,7 @@ export class Pattern { /** * Layers the result of the given function(s). Like `superimpose`, but without the original pattern: * @name layer - * @group combiners + * @tags combiners * @memberof Pattern * @synonyms apply * @returns Pattern @@ -829,7 +829,7 @@ export class Pattern { /** * Superimposes the result of the given function(s) on top of the original pattern: * @name superimpose - * @group combiners + * @tags combiners * @memberof Pattern * @returns Pattern * @example @@ -889,7 +889,7 @@ export class Pattern { /** * Writes the content of the current event to the console (visible in the side menu). - * @group visualizers + * @tags visualizers * @name log * @memberof Pattern * @example @@ -904,7 +904,7 @@ export class Pattern { /** * A simplified version of `log` which writes all "values" (various configurable parameters) * within the event to the console (visible in the side menu). - * @group visualizers + * @tags visualizers * @name logValues * @memberof Pattern * @example @@ -938,7 +938,7 @@ export class Pattern { * source pattern to be looped, and for an (optional) given function to be * applied. False values result in the corresponding part of the source pattern * to be played unchanged. - * @group structure + * @tags structure * @name into * @memberof Pattern * @example @@ -978,7 +978,7 @@ Pattern.prototype.collect = function () { /** * Selects indices in in stacked notes. - * @group transforms + * @tags transforms * @example * note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>") * .arpWith(haps => haps[2]) @@ -993,7 +993,7 @@ export const arpWith = register('arpWith', (func, pat) => { /** * Selects indices in in stacked notes. - * @group transforms + * @tags transforms * @example * note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>") * .arp("0 [0,2] 1 [0,2]") @@ -1076,7 +1076,7 @@ function _composeOp(a, b, func) { * note("c3 e3 g3".add("<0 5 7 0>")) * // Behind the scenes, the notes are converted to midi numbers: * // note("48 52 55".add("<0 5 7 0>")) - * @group transforms + * @tags math */ add: [numeralArgs((a, b) => a + b)], // support string concatenation /** @@ -1189,7 +1189,7 @@ function _composeOp(a, b, func) { /** * Applies the given structure to the pattern: * - * @group structure + * @tags structure * @example * note("c,eb,g") * .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~") @@ -1204,7 +1204,7 @@ function _composeOp(a, b, func) { /** * Returns silence when mask is 0 or "~" * - * @group structure + * @tags structure * @example * note("c [eb,g] d [eb,g]").mask("<1 [0 1]>") */ @@ -1217,7 +1217,7 @@ function _composeOp(a, b, func) { /** * Resets the pattern to the start of the cycle for each onset of the reset pattern. * - * @group structure + * @tags structure * @example * s("[ sd]*2, hh*8").reset("") */ @@ -1231,7 +1231,7 @@ function _composeOp(a, b, func) { * Restarts the pattern for each onset of the restart pattern. * While reset will only reset the current cycle, restart will start from cycle 0. * - * @group structure + * @tags structure * @example * s("[ sd]*2, hh*8").restart("") */ @@ -1272,7 +1272,7 @@ export const pm = polymeter; /** * Does absolutely nothing, but with a given metrical 'steps' * @name gap - * @group generators + * @tags generators * @param {number} steps * @example * gap(3) // "~@3" @@ -1282,7 +1282,7 @@ export const gap = (steps) => new Pattern(() => [], steps); /** * Does absolutely nothing.. * @name silence - * @group generators + * @tags generators * @example * silence // "~" */ @@ -1294,7 +1294,7 @@ export const nothing = gap(0); /** * A discrete value that repeats once per cycle. * - * @group generators + * @tags generators * @returns {Pattern} * @example * pure('e4') // "e4" @@ -1339,7 +1339,7 @@ export function reify(thing) { /** * Takes a list of patterns, and returns a pattern of lists. * - * @group transforms + * @tags transforms */ export function sequenceP(pats) { let result = pure([]); @@ -1352,7 +1352,7 @@ export function sequenceP(pats) { /** * The given items are played at the same time at the same length. * - * @group structure + * @tags structure * @return {Pattern} * @synonyms polyrhythm, pr * @example @@ -1437,7 +1437,7 @@ export function stackBy(by, ...pats) { /** * Concatenation: combines a list of patterns, switching between them successively, one per cycle. * - * @group combiners + * @tags combiners * @return {Pattern} * @synonyms cat * @example @@ -1471,7 +1471,7 @@ export function slowcat(...pats) { } /** Concatenation: combines a list of patterns, switching between them successively, one per cycle. Unlike slowcat, this version will skip cycles. - * @group combiners + * @tags combiners * @param {...any} items - The items to concatenate * @return {Pattern} */ @@ -1487,7 +1487,7 @@ export function slowcatPrime(...pats) { /** The given items are con**cat**enated, where each one takes one cycle. * - * @group combiners + * @tags combiners * @param {...any} items - The items to concatenate * @synonyms slowcat * @return {Pattern} @@ -1509,7 +1509,7 @@ export function cat(...pats) { * Allows to arrange multiple patterns together over multiple cycles. * Takes a variable number of arrays with two elements specifying the number of cycles and the pattern to use. * - * @group combiners + * @tags combiners * @return {Pattern} * @example * arrange( @@ -1527,7 +1527,7 @@ export function arrange(...sections) { * Similarly to `arrange`, allows you to arrange multiple patterns together over multiple cycles. * Unlike `arrange`, you specify a start and stop time for each pattern rather than duration, which * means that patterns can overlap. - * @group combiners + * @tags combiners * @return {Pattern} * @example seqPLoop([0, 2, "bd(3,8)"], @@ -1572,7 +1572,7 @@ export function sequence(...pats) { } /** Like **cat**, but the items are crammed into one cycle. - * @group combiners + * @tags combiners * @synonyms sequence, fastcat * @example * seq("e5", "b4", ["d5", "c5"]).note() @@ -1746,7 +1746,7 @@ function stepRegister(name, func, patternify = true, preserveSteps = false, join * Assumes a numerical pattern. Returns a new pattern with all values rounded * to the nearest integer. * @name round - * @group math + * @tags math * @memberof Pattern * @returns Pattern * @example @@ -1761,7 +1761,7 @@ export const round = register('round', function (pat) { * replaced with `-5`. * @name floor * @memberof Pattern - * @group math + * @tags math * @returns Pattern * @example * note("42 42.1 42.5 43".floor()) @@ -1776,7 +1776,7 @@ export const floor = register('floor', function (pat) { * replaced with `-4`. * @name ceil * @memberof Pattern - * @group math + * @tags math * @returns Pattern * @example * note("42 42.1 42.5 43".ceil()) @@ -1787,7 +1787,7 @@ export const ceil = register('ceil', function (pat) { /** * Assumes a numerical pattern, containing unipolar values in the range 0 .. * 1. Returns a new pattern with values scaled to the bipolar range -1 .. 1 - * @group math + * @tags math * @returns Pattern * @noAutocomplete */ @@ -1798,7 +1798,7 @@ export const toBipolar = register('toBipolar', function (pat) { /** * Assumes a numerical pattern, containing bipolar values in the range -1 .. 1 * Returns a new pattern with values scaled to the unipolar range 0 .. 1 - * @group math + * @tags math * @returns Pattern * @noAutocomplete */ @@ -1812,7 +1812,7 @@ export const fromBipolar = register('fromBipolar', function (pat) { * Most useful in combination with continuous patterns. * @name range * @memberof Pattern - * @group math + * @tags math * @returns Pattern * @example * s("[bd sd]*2,hh*8") @@ -1828,7 +1828,7 @@ export const range = register('range', function (min, max, pat) { * following an exponential curve. * @name rangex * @memberof Pattern - * @group math + * @tags math * @returns Pattern * @example * s("[bd sd]*2,hh*8") @@ -1843,7 +1843,7 @@ export const rangex = register('rangex', function (min, max, pat) { * Returns a new pattern with values scaled to the given min/max range. * @name range2 * @memberof Pattern - * @group math + * @tags math * @returns Pattern * @example * s("[bd sd]*2,hh*8") @@ -1858,7 +1858,7 @@ export const range2 = register('range2', function (min, max, pat) { * Returns a new pattern with just numbers. * @name ratio * @memberof Pattern - * @group math + * @tags math * @returns Pattern * @example * ratio("1, 5:4, 3:2").mul(110) @@ -1877,7 +1877,7 @@ export const ratio = register('ratio', (pat) => // Structural and temporal transformations /** Compress each cycle into the given timespan, leaving a gap - * @group structure + * @tags structure * @example * cat( * s("bd sd").compress(.25,.75), @@ -1899,7 +1899,7 @@ export const { compressSpan, compressspan } = register(['compressSpan', 'compres /** * speeds up a pattern like fast, but rather than it playing multiple times as fast would it instead leaves a gap in the remaining space of the cycle. For example, the following will play the sound pattern "bd sn" only once but compressed into the first half of the cycle, i.e. twice as fast. - * @group structure + * @tags structure * @name fastGap * @synonyms fastgap * @example @@ -1937,7 +1937,7 @@ export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (f /** * Similar to `compress`, but doesn't leave gaps, and the 'focus' can be bigger than a cycle - * @group structure + * @tags structure * @example * s("bd hh sd hh").focus(1/4, 3/4) */ @@ -1955,7 +1955,7 @@ export const { focusSpan, focusspan } = register(['focusSpan', 'focusspan'], fun }); /** The ply function repeats each event the given number of times. - * @group structure + * @tags structure * @example * s("bd ~ sd cp").ply("<1 2 3>") */ @@ -1970,7 +1970,7 @@ export const ply = register('ply', function (factor, pat) { /** * Speed up a pattern by the given factor. Used by "*" in mini notation. * - * @group structure + * @tags structure * @name fast * @synonyms density * @memberof Pattern @@ -1995,7 +1995,7 @@ export const { fast, density } = register( /** * Both speeds up the pattern (like 'fast') and the sample playback (like 'speed'). - * @group structure + * @tags structure * @example * s("bd sd:2").hurry("<1 2 4 3>").slow(1.5) */ @@ -2006,7 +2006,7 @@ export const hurry = register('hurry', function (r, pat) { /** * Slow down a pattern over the given number of cycles. Like the "/" operator in mini notation. * - * @group structure + * @tags structure * @name slow * @synonyms sparsity * @memberof Pattern @@ -2024,7 +2024,7 @@ export const { slow, sparsity } = register(['slow', 'sparsity'], function (facto /** * Carries out an operation 'inside' a cycle. - * @group structure + * @tags structure * @example * "0 1 2 3 4 3 2 1".inside(4, rev).scale('C major').note() * // "0 1 2 3 4 3 2 1".slow(4).rev().fast(4).scale('C major').note() @@ -2035,7 +2035,7 @@ export const inside = register('inside', function (factor, f, pat) { /** * Carries out an operation 'outside' a cycle. - * @group structure + * @tags structure * @example * "<[0 1] 2 [3 4] 5>".outside(4, rev).scale('C major').note() * // "<[0 1] 2 [3 4] 5>".fast(4).rev().slow(4).scale('C major').note() @@ -2046,7 +2046,7 @@ export const outside = register('outside', function (factor, f, pat) { /** * Applies the given function every n cycles, starting from the last cycle. - * @group structure + * @tags structure * @name lastOf * @memberof Pattern * @param {number} n how many cycles @@ -2063,7 +2063,7 @@ export const lastOf = register('lastOf', function (n, func, pat) { /** * Applies the given function every n cycles, starting from the first cycle. - * @group structure + * @tags structure * @name firstOf * @memberof Pattern * @param {number} n how many cycles @@ -2075,7 +2075,7 @@ export const lastOf = register('lastOf', function (n, func, pat) { /** * An alias for `firstOf` - * @group structure + * @tags structure * @name every * @memberof Pattern * @param {number} n how many cycles @@ -2092,7 +2092,7 @@ export const { firstOf, every } = register(['firstOf', 'every'], function (n, fu /** * Like layer, but with a single function: - * @group structure + * @tags structure * @name apply * @example * "".scale('C minor').apply(scaleTranspose("0,2,4")).note() @@ -2104,7 +2104,7 @@ export const apply = register('apply', function (func, pat) { /** * Plays the pattern at the given cycles per minute. - * @group structure + * @tags structure * @deprecated * @example * s(",hh*2").cpm(90) // = 90 bpm @@ -2117,7 +2117,7 @@ export const cpm = register('cpm', function (cpm, pat) { /** * Nudge a pattern to start earlier in time. Equivalent of Tidal's <~ operator * - * @group structure + * @tags structure * @name early * @memberof Pattern * @param {number | Pattern} cycles number of cycles to nudge left @@ -2138,7 +2138,7 @@ export const early = register( /** * Nudge a pattern to start later in time. Equivalent of Tidal's ~> operator * - * @group structure + * @tags structure * @name late * @memberof Pattern * @param {number | Pattern} cycles number of cycles to nudge right @@ -2159,7 +2159,7 @@ export const late = register( /** * Plays a portion of a pattern, specified by the beginning and end of a time span. The new resulting pattern is played over the time period of the original pattern: * - * @group structure + * @tags structure * @example * s("bd*2 hh*3 [sd bd]*2 perc").zoom(0.25, 0.75) * // s("hh*3 [sd bd]*2") // equivalent @@ -2186,7 +2186,7 @@ export const { zoomArc, zoomarc } = register(['zoomArc', 'zoomarc'], function (a /** * Splits a pattern into the given number of slices, and plays them according to a pattern of slice numbers. * Similar to `slice`, but slices up patterns rather than sound samples. - * @group structure + * @tags structure * @param {number} number of slices * @param {number} slices to play * @example @@ -2214,7 +2214,7 @@ export const bite = register( /** * Selects the given fraction of the pattern and repeats that part to fill the remainder of the cycle. - * @group structure + * @tags structure * @param {number} fraction fraction to select * @example * s("lt ht mt cp, [hh oh]*2").linger("<1 .5 .25 .125>") @@ -2235,7 +2235,7 @@ export const linger = register( /** * Samples the pattern at a rate of n events per cycle. Useful for turning a continuous pattern into a discrete one. - * @group structure + * @tags structure * @name segment * @synonyms seg * @param {number} segments number of segments per cycle @@ -2248,7 +2248,7 @@ export const { segment, seg } = register(['segment', 'seg'], function (rate, pat /** * 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 - * @group structure + * @tags structure * @param {number} subdivision * @param {number} offset * @example @@ -2258,7 +2258,7 @@ export const swingBy = register('swingBy', (swing, n, pat) => pat.inside(n, late /** * Shorthand for swingBy with 1/3: - * @group structure + * @tags structure * @param {number} subdivision * @example * s("hh*8").swing(4) @@ -2268,7 +2268,7 @@ export const swing = register('swing', (n, pat) => pat.swingBy(1 / 3, n)); /** * Swaps 1s and 0s in a binary pattern. - * @group structure + * @tags structure * @name invert * @synonyms inv * @example @@ -2286,7 +2286,7 @@ export const { invert, inv } = register( /** * Applies the given function whenever the given pattern is in a true state. - * @group structure + * @tags structure * @name when * @memberof Pattern * @param {Pattern} binary_pat @@ -2301,7 +2301,7 @@ export const when = register('when', function (on, func, pat) { /** * Superimposes the function result on top of the original pattern, delayed by the given time. - * @group structure + * @tags structure * @name off * @memberof Pattern * @param {Pattern | number} time offset time @@ -2318,7 +2318,7 @@ export const off = register('off', function (time_pat, func, pat) { * Returns a new pattern where every other cycle is played once, twice as * fast, and offset in time by one quarter of a cycle. Creates a kind of * breakbeat feel. - * @group structure + * @tags structure * @returns Pattern */ export const brak = register('brak', function (pat) { @@ -2328,7 +2328,7 @@ export const brak = register('brak', function (pat) { /** * Reverse all haps in a pattern * - * @group structure + * @tags structure * @name rev * @memberof Pattern * @returns Pattern @@ -2362,7 +2362,7 @@ export const rev = register( /** Like press, but allows you to specify the amount by which each * event is shifted. pressBy(0.5) is the same as press, while * pressBy(1/3) shifts each event by a third of its timespan. - * @group structure + * @tags structure * @example * stack(s("hh*4"), * s("bd mt sd ht").pressBy("<0 0.5 0.25>") @@ -2374,7 +2374,7 @@ export const pressBy = register('pressBy', function (r, pat) { /** * Syncopates a rhythm, by shifting each event halfway into its timespan. - * @group structure + * @tags structure * @example * stack(s("hh*4"), * s("bd mt sd ht").every(4, press) @@ -2386,7 +2386,7 @@ export const press = register('press', function (pat) { /** * Silences a pattern. - * @group structure + * @tags structure * @example * stack( * s("bd").hush(), @@ -2399,7 +2399,7 @@ Pattern.prototype.hush = function () { /** * Applies `rev` to a pattern every other cycle, so that the pattern alternates between forwards and backwards. - * @group structure + * @tags structure * @example * note("c d e g").palindrome() */ @@ -2414,7 +2414,7 @@ export const palindrome = register( /** * Jux with adjustable stereo width. 0 = mono, 1 = full stereo. - * @group structure + * @tags structure * @name juxBy * @synonyms juxby * @example @@ -2436,7 +2436,7 @@ export const { juxBy, juxby } = register(['juxBy', 'juxby'], function (by, func, /** * The jux function creates strange stereo effects, by applying a function to a pattern, but only in the right-hand channel. - * @group structure + * @tags structure * @example * s("bd lt [~ ht] mt cp ~ bd hh").jux(rev) * @example @@ -2450,7 +2450,7 @@ export const jux = register('jux', function (func, pat) { /** * Superimpose and offset multiple times, applying the given function each time. - * @group structure + * @tags structure * @name echoWith * @synonyms echowith, stutWith, stutwith * @param {number} times how many times to repeat @@ -2470,7 +2470,7 @@ export const { echoWith, echowith, stutWith, stutwith } = register( /** * Superimpose and offset multiple times, gradually decreasing the velocity - * @group structure + * @tags structure * @name echo * @memberof Pattern * @returns Pattern @@ -2486,7 +2486,7 @@ export const echo = register('echo', function (times, time, feedback, pat) { /** * Deprecated. Like echo, but the last 2 parameters are flipped. - * @group structure + * @tags structure * @name stut * @param {number} times how many times to repeat * @param {number} feedback velocity multiplicator for each iteration @@ -2508,7 +2508,7 @@ export const applyN = register('applyN', function (n, func, p) { /** * The plyWith function repeats each event the given number of times, applying the given function to each event.\n - * @group structure + * @tags structure * @name plyWith * @synonyms plywith * @param {number} factor how many times to repeat @@ -2531,7 +2531,7 @@ export const plyWith = register(['plyWith', 'plywith'], function (factor, func, /** * The plyForEach function repeats each event the given number of times, applying the given function to each event. * This version of ply uses the iteration index as an argument to the function, similar to echoWith. - * @group structure + * @tags structure * @name plyForEach * @synonyms plyforeach * @param {number} factor how many times to repeat @@ -2553,7 +2553,7 @@ export const plyForEach = register(['plyForEach', 'plyforeach'], function (facto /** * Divides a pattern into a given number of subdivisions, plays the subdivisions in order, but increments the starting subdivision each cycle. The pattern wraps to the first subdivision after the last subdivision is played. - * @group structure + * @tags structure * @name iter * @memberof Pattern * @returns Pattern @@ -2581,7 +2581,7 @@ export const iter = register( /** * Like `iter`, but plays the subdivisions in reverse order. Known as iter' in tidalcycles - * @group structure + * @tags structure * @name iterBack * @synonyms iterback * @memberof Pattern @@ -2600,7 +2600,7 @@ export const { iterBack, iterback } = register( /** * Repeats each cycle the given number of times. - * @group structure + * @tags structure * @name repeatCycles * @memberof Pattern * @returns Pattern @@ -2624,7 +2624,7 @@ export const { repeatCycles } = register( /** * Divides a pattern into a given number of parts, then cycles through those parts in turn, applying the given function to each part in turn (one part per cycle). - * @group structure + * @tags structure * @name chunk * @synonyms slowChunk, slowchunk * @memberof Pattern @@ -2656,7 +2656,7 @@ export const { chunk, slowchunk, slowChunk } = register( /** * Like `chunk`, but cycles through the parts in reverse order. Known as chunk' in tidalcycles - * @group structure + * @tags structure * @name chunkBack * @synonyms chunkback * @memberof Pattern @@ -2677,7 +2677,7 @@ export const { chunkBack, chunkback } = register( /** * Like `chunk`, but the cycles of the source pattern aren't repeated * for each set of chunks. - * @group structure + * @tags structure * @name fastChunk * @synonyms fastchunk * @memberof Pattern @@ -2698,7 +2698,7 @@ export const { fastchunk, fastChunk } = register( /** * Like `chunk`, but the function is applied to a looped subcycle of the source pattern. - * @group structure + * @tags structure * @name chunkInto * @synonyms chunkinto * @memberof Pattern @@ -2712,7 +2712,7 @@ export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], fun /** * Like `chunkInto`, but moves backwards through the chunks. - * @group structure + * @tags structure * @name chunkBackInto * @synonyms chunkbackinto * @memberof Pattern @@ -2743,7 +2743,7 @@ export const bypass = register( /** * Loops the pattern inside an `offset` for `cycles`. * If you think of the entire span of time in cycles as a ribbon, you can cut a single piece and loop it. - * @group structure + * @tags structure * @name ribbon * @synonyms rib * @param {number} offset start point of loop in cycles @@ -2772,7 +2772,7 @@ export const hsl = register('hsl', (h, s, l, pat) => { /** * Tags each Hap with an identifier. Good for filtering. The function populates Hap.context.tags (Array). * @name tag - * @group structure + * @tags structure * @noAutocomplete * @param {string} tag anything unique */ @@ -2783,7 +2783,7 @@ Pattern.prototype.tag = function (tag) { /** * Filters haps using the given function * @name filter - * @group structure + * @tags structure * @param {Function} test function to test Hap * @example * s("hh!7 oh").filter(hap => hap.value.s==='hh') @@ -2793,7 +2793,7 @@ export const filter = register('filter', (test, pat) => pat.withHaps((haps) => h /** * Filters haps by their begin time * @name filterWhen - * @group structure + * @tags structure * @noAutocomplete * @param {Function} test function to test Hap.whole.begin */ @@ -2802,7 +2802,7 @@ export const filterWhen = register('filterWhen', (test, pat) => pat.filter((h) = /** * Use within to apply a function to only a part of a pattern. * @name within - * @group structure + * @tags structure * @param {number} start start within cycle (0 - 1) * @param {number} end end within cycle (0 - 1). Must be > start * @param {Function} func function to be applied to the sub-pattern @@ -2877,7 +2877,7 @@ export function _match(span, hap_p) { * *Experimental* * * Speeds a pattern up or down, to fit to the given number of steps per cycle. - * @group structure + * @tags structure * @example * sound("bd sd cp").pace(4) * // The same as sound("{bd sd cp}%4") or sound("*4") @@ -2919,7 +2919,7 @@ export function _polymeterListSteps(steps, ...args) { * *Experimental* * * Aligns the steps of the patterns, creating polymeters. The patterns are repeated until they all fit the cycle. For example, in the below the first pattern is repeated twice, and the second is repeated three times, to fit the lowest common multiple of six steps. - * @group structure + * @tags structure * @synonyms pm * @example * // The same as note("{c eb g, c2 g2}%6") @@ -2952,7 +2952,7 @@ export function polymeter(...args) { * The steps can either be inferred from the pattern, or provided as a [length, pattern] pair. * Has the alias `timecat`. * @name stepcat - * @group combiners + * @tags combiners * @synonyms timeCat, timecat * @return {Pattern} * @example @@ -3010,7 +3010,7 @@ export function stepcat(...timepats) { * Concatenates patterns stepwise, according to an inferred 'steps per cycle'. * Similar to `stepcat`, but if an argument is a list, the whole pattern will alternate between the elements in the list. * - * @group combiners + * @tags combiners * @return {Pattern} * @example * stepalt(["bd cp", "mt"], "bd").sound() @@ -3037,7 +3037,7 @@ export function stepalt(...groups) { * * Takes the given number of steps from a pattern (dropping the rest). * A positive number will take steps from the start of a pattern, and a negative number from the end. - * @group structure + * @tags structure * @return {Pattern} * @example * "bd cp ht mt".take("2").sound() @@ -3082,7 +3082,7 @@ export const take = stepRegister('take', function (i, pat) { * * Drops the given number of steps from a pattern. * A positive number will drop steps from the start of a pattern, and a negative number from the end. - * @group structure + * @tags structure * @return {Pattern} * @example * "tha dhi thom nam".drop("1").sound().bank("mridangam") @@ -3111,7 +3111,7 @@ export const drop = stepRegister('drop', function (i, pat) { * `extend` is similar to `fast` in that it increases its density, but it also increases the step count * accordingly. So `stepcat("a b".extend(2), "c d")` would be the same as `"a b a b c d"`, whereas * `stepcat("a b".fast(2), "c d")` would be the same as `"[a b] [a b] c d"`. - * @group structure + * @tags structure * @example * stepcat( * sound("bd bd - cp").extend(2), @@ -3126,7 +3126,7 @@ export const extend = stepRegister('extend', function (factor, pat) { * *Experimental* * * Expands the step size of the pattern by the given factor. - * @group structure + * @tags structure * @example * sound("tha dhi thom nam").bank("mridangam").expand("3 2 1 1 2 3").pace(8) */ @@ -3138,7 +3138,7 @@ export const expand = stepRegister('expand', function (factor, pat) { * *Experimental* * * Contracts the step size of the pattern by the given factor. See also `expand`. - * @group structure + * @tags structure * @example * sound("tha dhi thom nam").bank("mridangam").contract("3 2 1 1 2 3").pace(8) */ @@ -3193,7 +3193,7 @@ export const shrinklist = (amount, pat) => pat.shrinklist(amount); * Progressively shrinks the pattern by 'n' steps until there's nothing left, or if a second value is given (using mininotation list syntax with `:`), * that number of times. * A positive number will progressively drop steps from the start of a pattern, and a negative number from the end. - * @group structure + * @tags structure * @return {Pattern} * @example * "tha dhi thom nam".shrink("1").sound() @@ -3233,7 +3233,7 @@ export const shrink = register( * Progressively grows the pattern by 'n' steps until the full pattern is played, or if a second value is given (using mininotation list syntax with `:`), * that number of times. * A positive number will progressively grow steps from the start of a pattern, and a negative number from the end. - * @group structure + * @tags structure * @return {Pattern} * @example * "tha dhi thom nam".grow("1").sound() @@ -3274,7 +3274,7 @@ export const grow = register( * on successive repetitions. The patterns are added together stepwise, with all repetitions taking place over a single cycle. Using `pace` to set the * number of steps per cycle is therefore usually recommended. * - * @group combiners + * @tags combiners * @return {Pattern} * @example * "[c g]".tour("e f", "e f g", "g f e c").note() @@ -3301,7 +3301,7 @@ Pattern.prototype.tour = function (...many) { * 'zips' together the steps of the provided patterns. This can create a long repetition, taking place over a single, dense cycle. * Using `pace` to set the number of steps per cycle is therefore usually recommended. * - * @group combiners + * @tags combiners * @returns {Pattern} * @example * zip("e f", "e f g", "g [f e] a f4 c").note() @@ -3353,7 +3353,7 @@ Pattern.prototype.steps = Pattern.prototype.pace; * Cuts each sample into the given number of parts, allowing you to explore a technique known as 'granular synthesis'. * It turns a pattern of samples into a pattern of parts of samples. * @name chop - * @group transforms + * @tags transforms * @memberof Pattern * @returns Pattern * @example @@ -3384,7 +3384,7 @@ export const chop = register('chop', function (n, pat) { /** * Cuts each sample into the given number of parts, triggering progressive portions of each sample at each loop. * @name striate - * @group transforms + * @tags transforms * @memberof Pattern * @returns Pattern * @example @@ -3403,7 +3403,7 @@ export const striate = register('striate', function (n, pat) { /** * Makes the sample fit the given number of cycles by changing the speed. * @name loopAt - * @group transforms + * @tags transforms * @memberof Pattern * @returns Pattern * @example @@ -3422,7 +3422,7 @@ const _loopAt = function (factor, pat, cps = 0.5) { * Chops samples into the given number of slices, triggering those slices with a given pattern of slice numbers. * Instead of a number, it also accepts a list of numbers from 0 to 1 to slice at specific points. * @name slice - * @group transforms + * @tags transforms * @memberof Pattern * @returns Pattern * @example @@ -3458,7 +3458,7 @@ export const slice = register( * make something happen on event time * uses browser timeout which is innacurate for audio tasks * @name onTriggerTime - * @group external_io + * @tags external_io * @memberof Pattern * @returns Pattern * @example @@ -3476,7 +3476,7 @@ Pattern.prototype.onTriggerTime = function (func) { /** * Works the same as slice, but changes the playback speed of each slice to match the duration of its step. * @name splice - * @group transforms + * @tags transforms * @example * samples('github:tidalcycles/dirt-samples') * s("breaks165") @@ -3514,7 +3514,7 @@ export const { loopAt, loopat } = register(['loopAt', 'loopat'], function (facto * Makes the sample fit its event duration. Good for rhythmical loops like drum breaks. * Similar to `loopAt`. * @name fit - * @group transforms + * @tags transforms * @example * samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' }) * s("rhodes/2").fit() @@ -3540,7 +3540,7 @@ export const fit = register('fit', (pat) => * given by a global clock and this function will be * deprecated/removed. * @name loopAtCps - * @group transforms + * @tags transforms * @memberof Pattern * @returns Pattern * @example @@ -3567,7 +3567,7 @@ let fadeGain = (p) => (p < 0.5 ? 1 : 1 - (p - 0.5) / 0.5); * - 1 = (no left, full right) * * @name xfade - * @group combiners + * @tags combiners * @example * xfade(s("bd*2"), "<0 .25 .5 .75 1>", s("hh*8")) */ @@ -3589,7 +3589,7 @@ Pattern.prototype.xfade = function (pos, b) { * creates a structure pattern from divisions of a cycle * especially useful for creating rhythms * @name beat - * @group structure + * @tags structure * @example * s("bd").beat("0,7,10", 16) * @example @@ -3666,7 +3666,7 @@ export const _morph = (from, to, by) => { * sine.slow(8) // slowly morph between the rhythms * ) * ) - * @group structure + * @tags structure */ export const morph = (frompat, topat, bypat) => { frompat = reify(frompat); diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 9f6636c84..03dd641a5 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -24,7 +24,7 @@ export const signal = (func) => { * A sawtooth signal between 0 and 1. * * @return {Pattern} - * @group generators + * @tags generators * @example * note("*8") * .clip(saw.slow(2)) @@ -39,7 +39,7 @@ export const saw = signal((t) => t % 1); * A sawtooth signal between -1 and 1 (like `saw`, but bipolar). * * @return {Pattern} - * @group generators + * @tags generators */ export const saw2 = saw.toBipolar(); @@ -47,7 +47,7 @@ export const saw2 = saw.toBipolar(); * A sawtooth signal between 1 and 0 (like `saw`, but flipped). * * @return {Pattern} - * @group generators + * @tags generators * @example * note("*8") * .clip(isaw.slow(2)) @@ -62,7 +62,7 @@ export const isaw = signal((t) => 1 - (t % 1)); * A sawtooth signal between 1 and -1 (like `saw2`, but flipped). * * @return {Pattern} - * @group generators + * @tags generators */ export const isaw2 = isaw.toBipolar(); @@ -70,14 +70,14 @@ export const isaw2 = isaw.toBipolar(); * A sine signal between -1 and 1 (like `sine`, but bipolar). * * @return {Pattern} - * @group generators + * @tags generators */ export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t)); /** * A sine signal between 0 and 1. * @return {Pattern} - * @group generators + * @tags generators * @example * n(sine.segment(16).range(0,15)) * .scale("C:minor") @@ -89,7 +89,7 @@ export const sine = sine2.fromBipolar(); * A cosine signal between 0 and 1. * * @return {Pattern} - * @group generators + * @tags generators * @example * n(stack(sine,cosine).segment(16).range(0,15)) * .scale("C:minor") @@ -101,14 +101,14 @@ export const cosine = sine._early(Fraction(1).div(4)); * A cosine signal between -1 and 1 (like `cosine`, but bipolar). * * @return {Pattern} - * @group generators + * @tags generators */ export const cosine2 = sine2._early(Fraction(1).div(4)); /** * A square signal between 0 and 1. * @return {Pattern} - * @group generators + * @tags generators * @example * n(square.segment(4).range(0,7)).scale("C:minor") * @@ -119,7 +119,7 @@ export const square = signal((t) => Math.floor((t * 2) % 2)); * A square signal between -1 and 1 (like `square`, but bipolar). * * @return {Pattern} - * @group generators + * @tags generators */ export const square2 = square.toBipolar(); @@ -127,7 +127,7 @@ export const square2 = square.toBipolar(); * A triangle signal between 0 and 1. * * @return {Pattern} - * @group generators + * @tags generators * @example * n(tri.segment(8).range(0,7)).scale("C:minor") * @@ -138,7 +138,7 @@ export const tri = fastcat(saw, isaw); * A triangle signal between -1 and 1 (like `tri`, but bipolar). * * @return {Pattern} - * @group generators + * @tags generators */ export const tri2 = fastcat(saw2, isaw2); @@ -146,7 +146,7 @@ export const tri2 = fastcat(saw2, isaw2); * An inverted triangle signal between 1 and 0 (like `tri`, but flipped). * * @return {Pattern} - * @group generators + * @tags generators * @example * n(itri.segment(8).range(0,7)).scale("C:minor") * @@ -157,7 +157,7 @@ export const itri = fastcat(isaw, saw); * An inverted triangle signal between -1 and 1 (like `itri`, but bipolar). * * @return {Pattern} - * @group generators + * @tags generators */ export const itri2 = fastcat(isaw2, saw2); @@ -165,7 +165,7 @@ export const itri2 = fastcat(isaw2, saw2); * A signal representing the cycle time. * * @return {Pattern} - * @group generators + * @tags generators */ export const time = signal(id); @@ -173,7 +173,7 @@ export const time = signal(id); * The mouse's x position value ranges from 0 to 1. * @name mousex * @return {Pattern} - * @group external_io + * @tags external_io * @example * n(mousex.segment(4).range(0,7)).scale("C:minor") * @@ -183,7 +183,7 @@ export const time = signal(id); * The mouse's y position value ranges from 0 to 1. * @name mousey * @return {Pattern} - * @group external_io + * @tags external_io * @example * n(mousey.segment(4).range(0,7)).scale("C:minor") * @@ -238,7 +238,7 @@ const timeToRands = (t, n) => timeToRandsPrime(timeToIntSeed(t), n); /** * A discrete pattern of numbers from 0 to n-1 - * @group generators + * @tags generators * @example * n(run(4)).scale("C4:pentatonic") * // n("0 1 2 3").scale("C4:pentatonic") @@ -249,7 +249,7 @@ export const run = (n) => saw.range(0, n).round().segment(n); * Creates a pattern from a binary number. * * @name binary - * @group generators + * @tags generators * @param {number} n - input number to convert to binary * @example * "hh".s().struct(binary(5)) @@ -264,7 +264,7 @@ export const binary = (n) => { * Creates a pattern from a binary number, padded to n bits long. * * @name binaryN - * @group generators + * @tags generators * @param {number} n - input number to convert to binary * @param {number} nBits - pattern length, defaults to 16 * @example @@ -300,7 +300,7 @@ const _rearrangeWith = (ipat, n, pat) => { * Slices a pattern into the given number of parts, then plays those parts in random order. * Each part will be played exactly once per cycle. * @name shuffle - * @group transforms + * @tags transforms * @example * note("c d e f").sound("piano").shuffle(4) * @example @@ -314,7 +314,7 @@ export const shuffle = register('shuffle', (n, pat) => { * Slices a pattern into the given number of parts, then plays those parts at random. Similar to `shuffle`, * but parts might be played more than once, or not at all, per cycle. * @name scramble - * @group transforms + * @tags transforms * @example * note("c d e f").sound("piano").scramble(4) * @example @@ -328,7 +328,7 @@ export const scramble = register('scramble', (n, pat) => { * A continuous pattern of random numbers, between 0 and 1. * * @name rand - * @group generators + * @tags generators * @example * // randomly change the cutoff * s("bd*4,hh*8").cutoff(rand.range(500,8000)) @@ -346,7 +346,7 @@ export const _brandBy = (p) => rand.fmap((x) => x < p); * A continuous pattern of 0 or 1 (binary random), with a probability for the value being 1 * * @name brandBy - * @group generators + * @tags generators * @param {number} probability - a number between 0 and 1 * @example * s("hh*10").pan(brandBy(0.2)) @@ -357,7 +357,7 @@ export const brandBy = (pPat) => reify(pPat).fmap(_brandBy).innerJoin(); * A continuous pattern of 0 or 1 (binary random) * * @name brand - * @group generators + * @tags generators * @example * s("hh*10").pan(brand) */ @@ -369,7 +369,7 @@ export const _irand = (i) => rand.fmap((x) => Math.trunc(x * i)); * A continuous pattern of random integers, between 0 and n-1. * * @name irand - * @group generators + * @tags generators * @param {number} n max value (exclusive) * @example * // randomly select scale notes from 0 - 7 (= C to C) @@ -392,7 +392,7 @@ export const __chooseWith = (pat, xs) => { /** * Choose from the list of values (or patterns of values) using the given * pattern of numbers, which should be in the range of 0..1 - * @group transforms + * @tags transforms * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -406,7 +406,7 @@ export const chooseWith = (pat, xs) => { /** * As with {chooseWith}, but the structure comes from the chosen values, rather * than the pattern you're using to choose with. - * @group transforms + * @tags transforms * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -417,7 +417,7 @@ export const chooseInWith = (pat, xs) => { /** * Chooses randomly from the given list of elements. - * @group transforms + * @tags transforms * @param {...any} xs values / patterns to choose from. * @returns {Pattern} - a continuous pattern. * @example @@ -433,7 +433,7 @@ export const chooseOut = choose; * Chooses from the given list of values (or patterns of values), according * to the pattern that the method is called on. The pattern should be in * the range 0 .. 1. - * @group transforms + * @tags transforms * @param {...any} xs * @returns {Pattern} */ @@ -444,7 +444,7 @@ Pattern.prototype.choose = function (...xs) { /** * As with choose, but the pattern that this method is called on should be * in the range -1 .. 1 - * @group transforms + * @tags transforms * @param {...any} xs * @returns {Pattern} */ @@ -454,7 +454,7 @@ Pattern.prototype.choose2 = function (...xs) { /** * Picks one of the elements at random each cycle. - * @group transforms + * @tags transforms * @synonyms randcat * @returns {Pattern} * @example @@ -497,7 +497,7 @@ const wchooseWith = (...args) => _wchooseWith(...args).outerJoin(); /** * Chooses randomly from the given list of elements by giving a probability to each element - * @group transforms + * @tags transforms * @param {...any} pairs arrays of value and weight * @returns {Pattern} - a continuous pattern. * @example @@ -507,7 +507,7 @@ export const wchoose = (...pairs) => wchooseWith(rand, ...pairs); /** * Picks one of the elements at random each cycle by giving a probability to each element - * @group transforms + * @tags transforms * @synonyms wrandcat * @returns {Pattern} * @example @@ -555,7 +555,7 @@ export const berlinWith = (tpat) => { /** * Generates a continuous pattern of [perlin noise](https://en.wikipedia.org/wiki/Perlin_noise), in the range 0..1. * - * @group generators + * @tags generators * @name perlin * @example * // randomly change the cutoff @@ -568,7 +568,7 @@ export const perlin = perlinWith(time.fmap((v) => Number(v))); * Generates a continuous pattern of [berlin noise](conceived by Jame Coyne and Jade Rowland as a joke but turned out to be surprisingly cool and useful, * like perlin noise but with sawtooth waves), in the range 0..1. * - * @group generators + * @tags generators * @name berlin * @example * // ascending arpeggios @@ -589,7 +589,7 @@ export const degradeByWith = register( * 0 = 0% chance of removal * 1 = 100% chance of removal * - * @group transforms + * @tags transforms * @name degradeBy * @memberof Pattern * @param {number} amount - a number between 0 and 1 @@ -615,7 +615,7 @@ export const degradeBy = register( * * Randomly removes 50% of events from the pattern. Shorthand for `.degradeBy(0.5)` * - * @group transforms + * @tags transforms * @name degrade * @memberof Pattern * @returns Pattern @@ -632,7 +632,7 @@ export const degrade = register('degrade', (pat) => pat._degradeBy(0.5), true, t * 1 = 0% chance of removal * Events that would be removed by degradeBy are let through by undegradeBy and vice versa (see second example). * - * @group transforms + * @tags transforms * @name undegradeBy * @memberof Pattern * @param {number} amount - a number between 0 and 1 @@ -661,7 +661,7 @@ export const undegradeBy = register( * Inverse of `degrade`: Randomly removes 50% of events from the pattern. Shorthand for `.undegradeBy(0.5)` * Events that would be removed by degrade are let through by undegrade and vice versa (see second example). * - * @group transforms + * @tags transforms * @name undegrade * @memberof Pattern * @returns Pattern @@ -680,7 +680,7 @@ export const undegrade = register('undegrade', (pat) => pat._undegradeBy(0.5), t * Randomly applies the given function by the given probability. * Similar to `someCyclesBy` * - * @group transforms + * @tags transforms * @name sometimesBy * @memberof Pattern * @param {number | Pattern} probability - a number between 0 and 1 @@ -700,7 +700,7 @@ export const sometimesBy = register('sometimesBy', function (patx, func, pat) { * * Applies the given function with a 50% chance * - * @group transforms + * @tags transforms * @name sometimes * @memberof Pattern * @param {function} function - the transformation to apply @@ -722,7 +722,7 @@ export const sometimes = register('sometimes', function (func, pat) { * @param {number | Pattern} probability - a number between 0 and 1 * @param {function} function - the transformation to apply * @returns Pattern - * @group transforms + * @tags transforms * @example * s("bd,hh*8").someCyclesBy(.3, x=>x.speed("0.5")) */ @@ -745,7 +745,7 @@ export const someCyclesBy = register('someCyclesBy', function (patx, func, pat) * @name someCycles * @memberof Pattern * @returns Pattern - * @group transforms + * @tags transforms * @example * s("bd,hh*8").someCycles(x=>x.speed("0.5")) */ @@ -760,7 +760,7 @@ export const someCycles = register('someCycles', function (func, pat) { * @name often * @memberof Pattern * @returns Pattern - * @group transforms + * @tags transforms * @example * s("hh*8").often(x=>x.speed("0.5")) */ @@ -775,7 +775,7 @@ export const often = register('often', function (func, pat) { * @name rarely * @memberof Pattern * @returns Pattern - * @group transforms + * @tags transforms * @example * s("hh*8").rarely(x=>x.speed("0.5")) */ @@ -787,7 +787,7 @@ export const rarely = register('rarely', function (func, pat) { * * Shorthand for `.sometimesBy(0.1, fn)` * - * @group transforms + * @tags transforms * @name almostNever * @memberof Pattern * @returns Pattern @@ -802,7 +802,7 @@ export const almostNever = register('almostNever', function (func, pat) { * * Shorthand for `.sometimesBy(0.9, fn)` * - * @group transforms + * @tags transforms * @name almostAlways * @memberof Pattern * @returns Pattern @@ -817,7 +817,7 @@ export const almostAlways = register('almostAlways', function (func, pat) { * * Shorthand for `.sometimesBy(0, fn)` (never calls fn) * - * @group transforms + * @tags transforms * @name never * @memberof Pattern * @returns Pattern @@ -832,7 +832,7 @@ export const never = register('never', function (_, pat) { * * Shorthand for `.sometimesBy(1, fn)` (always calls fn) * - * @group transforms + * @tags transforms * @name always * @memberof Pattern * @returns Pattern @@ -861,7 +861,7 @@ export function _keyDown(keyname) { * Do something on a keypress, or array of keypresses * [Key name reference](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values) * - * @group external_io + * @tags external_io * @name whenKey * @memberof Pattern * @returns Pattern @@ -878,7 +878,7 @@ export const whenKey = register('whenKey', function (input, func, pat) { * returns true when a key or array of keys is held * [Key name reference](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values) * - * @group external_io + * @tags external_io * @name keyDown * @memberof Pattern * @returns Pattern diff --git a/packages/motion/motion.mjs b/packages/motion/motion.mjs index cba814a84..fed0fbe0c 100644 --- a/packages/motion/motion.mjs +++ b/packages/motion/motion.mjs @@ -7,7 +7,7 @@ import { signal } from '../core/signal.mjs'; * @name accelerationX * @return {Pattern} * @synonyms accX - * @group external_io + * @tags external_io * @example * n(accelerationX.segment(4).range(0,7)).scale("C:minor") * @@ -18,7 +18,7 @@ import { signal } from '../core/signal.mjs'; * @name accelerationY * @return {Pattern} * @synonyms accY - * @group external_io + * @tags external_io * @example * n(accelerationY.segment(4).range(0,7)).scale("C:minor") * @@ -29,7 +29,7 @@ import { signal } from '../core/signal.mjs'; * @name accelerationZ * @return {Pattern} * @synonyms accZ - * @group external_io + * @tags external_io * @example * n(accelerationZ.segment(4).range(0,7)).scale("C:minor") * @@ -40,7 +40,7 @@ import { signal } from '../core/signal.mjs'; * @name gravityX * @return {Pattern} * @synonyms gravX - * @group external_io + * @tags external_io * @example * n(gravityX.segment(4).range(0,7)).scale("C:minor") * @@ -51,7 +51,7 @@ import { signal } from '../core/signal.mjs'; * @name gravityY * @return {Pattern} * @synonyms gravY - * @group external_io + * @tags external_io * @example * n(gravityY.segment(4).range(0,7)).scale("C:minor") * @@ -62,7 +62,7 @@ import { signal } from '../core/signal.mjs'; * @name gravityZ * @return {Pattern} * @synonyms gravZ - * @group external_io + * @tags external_io * @example * n(gravityZ.segment(4).range(0,7)).scale("C:minor") * @@ -73,7 +73,7 @@ import { signal } from '../core/signal.mjs'; * @name rotationAlpha * @return {Pattern} * @synonyms rotA, rotZ, rotationZ - * @group external_io + * @tags external_io * @example * n(rotationAlpha.segment(4).range(0,7)).scale("C:minor") * @@ -84,7 +84,7 @@ import { signal } from '../core/signal.mjs'; * @name rotationBeta * @return {Pattern} * @synonyms rotB, rotX, rotationX - * @group external_io + * @tags external_io * @example * n(rotationBeta.segment(4).range(0,7)).scale("C:minor") * @@ -95,7 +95,7 @@ import { signal } from '../core/signal.mjs'; * @name rotationGamma * @return {Pattern} * @synonyms rotG, rotY, rotationY - * @group external_io + * @tags external_io * @example * n(rotationGamma.segment(4).range(0,7)).scale("C:minor") * @@ -106,7 +106,7 @@ import { signal } from '../core/signal.mjs'; * @name orientationAlpha * @return {Pattern} * @synonyms oriA, oriZ, orientationZ - * @group external_io + * @tags external_io * @example * n(orientationAlpha.segment(4).range(0,7)).scale("C:minor") * @@ -117,7 +117,7 @@ import { signal } from '../core/signal.mjs'; * @name orientationBeta * @return {Pattern} * @synonyms oriB, oriX, orientationX - * @group external_io + * @tags external_io * @example * n(orientationBeta.segment(4).range(0,7)).scale("C:minor") * @@ -128,7 +128,7 @@ import { signal } from '../core/signal.mjs'; * @name orientationGamma * @return {Pattern} * @synonyms oriG, oriY, orientationY - * @group external_io + * @tags external_io * @example * n(orientationGamma.segment(4).range(0,7)).scale("C:minor") * @@ -139,7 +139,7 @@ import { signal } from '../core/signal.mjs'; * @name absoluteOrientationAlpha * @return {Pattern} * @synonyms absOriA, absOriZ, absoluteOrientationZ - * @group external_io + * @tags external_io * @example * n(absoluteOrientationAlpha.segment(4).range(0,7)).scale("C:minor") * @@ -150,7 +150,7 @@ import { signal } from '../core/signal.mjs'; * @name absoluteOrientationBeta * @return {Pattern} * @synonyms absOriB, absOriX, absoluteOrientationX - * @group external_io + * @tags external_io * @example * n(absoluteOrientationBeta.segment(4).range(0,7)).scale("C:minor") * @@ -161,7 +161,7 @@ import { signal } from '../core/signal.mjs'; * @name absoluteOrientationGamma * @return {Pattern} * @synonyms absOriG, absOriY, absoluteOrientationY - * @group external_io + * @tags external_io * @example * n(absoluteOrientationGamma.segment(4).range(0,7)).scale("C:minor") * diff --git a/packages/superdough/reverbGen.mjs b/packages/superdough/reverbGen.mjs index 0d5fee264..42da7c6d5 100644 --- a/packages/superdough/reverbGen.mjs +++ b/packages/superdough/reverbGen.mjs @@ -80,7 +80,7 @@ reverbGen.generateGraph = function (data, width, height, min, max) { @param {number} lpFreqEndAt @param {!function(!AudioBuffer)} callback May be called immediately within the current execution context, or later. - @group effects + @tags effects */ var applyGradualLowpass = function (input, lpFreqStart, lpFreqEnd, lpFreqEndAt, callback) { if (lpFreqStart == 0) { diff --git a/packages/supradough/dough.mjs b/packages/supradough/dough.mjs index 7eeb7a507..23dfbee09 100644 --- a/packages/supradough/dough.mjs +++ b/packages/supradough/dough.mjs @@ -18,7 +18,7 @@ function applyGainCurve(val) { * @param {number} a - Signal A (can be a single value or an array value in buffer processing). * @param {number} b - Signal B (can be a single value or an array value in buffer processing). * @param {number} m - Crossfade parameter (0.0 = all A, 1.0 = all B, 0.5 = equal mix). - * @group effects + * @tags effects * @returns {number} Crossfaded output value. */ function crossfade(a, b, m) { From d516d765d5556617adb1a5cfe8b672c35a09effa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sat, 11 Oct 2025 14:12:45 +0200 Subject: [PATCH 006/124] Redo frontend for tags --- .../src/repl/components/panel/Reference.jsx | 92 +++++++++++++------ 1 file changed, 64 insertions(+), 28 deletions(-) diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index 552551b5c..3faa5c6f1 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -10,8 +10,10 @@ const availableFunctions = (() => { const functions = []; for (const doc of jsdocJson.docs) { if (!isValid(doc)) continue; + if (seen.has(doc.name)) continue; functions.push(doc); const synonyms = doc.synonyms || []; + seen.add(doc.name); for (const s of synonyms) { if (!s || seen.has(s)) continue; seen.add(s); @@ -38,18 +40,33 @@ const getInnerText = (html) => { const GROUP_DISPLAY_NAMES = { external_io: 'External I/O', effects: 'Effects', - ungrouped: 'Ungrouped', + untagged: 'Untagged', structure: 'Structure', transforms: 'Transforms', }; -const GROUP_ORDER = ['effects', 'transforms', 'structure', 'ungrouped', 'external_io']; +const GROUP_ORDER = ['effects', 'transforms', 'structure', 'untagged', 'external_io']; export function Reference() { const [search, setSearch] = useState(''); + const [selectedTag, setSelectedTag] = useState(null); + + const toggleTag = (tag) => { + if (selectedTag === tag) { + setSelectedTag(null); + } else { + setSelectedTag(tag); + } + }; const visibleFunctions = useMemo(() => { return availableFunctions.filter((entry) => { + if (selectedTag) { + if (!(entry.tags || ['untagged']).includes(selectedTag)) { + return false; + } + } + if (!search) { return true; } @@ -60,12 +77,12 @@ export function Reference() { (entry.synonyms?.some((s) => s.includes(lowCaseSearch)) ?? false) ); }); - }, [search]); + }, [search, selectedTag]); const visibleFunctionsByGroup = (() => { const groups = {}; for (const doc of visibleFunctions) { - const group = doc.group || 'ungrouped'; + const group = (doc.tags || ['untagged'])[0]; if (!groups[group]) { groups[group] = []; } @@ -85,37 +102,56 @@ export function Reference() { return ai - bi; }); + const tagCounts = {}; + for (const doc of availableFunctions) { + (doc.tags || ['untagged']).forEach((t) => { + if (typeof t === 'string' && t) { + tagCounts[t] = (tagCounts[t] || 0) + 1; + } + }); + } + return ( -
-
-
+
+
From c62428ca27b34f62598db1506dd42af36f23f80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sun, 12 Oct 2025 16:07:11 +0200 Subject: [PATCH 007/124] Document remaining functions --- packages/codemirror/codemirror.mjs | 1 + packages/codemirror/slider.mjs | 1 + packages/core/controls.mjs | 7 +- packages/core/drawLine.mjs | 1 + packages/core/euclid.mjs | 5 + packages/core/pattern.mjs | 159 ++++++++++++++------------- packages/core/pick.mjs | 13 +++ packages/core/repl.mjs | 5 + packages/core/signal.mjs | 1 + packages/csound/index.mjs | 2 + packages/draw/pianoroll.mjs | 2 + packages/draw/pitchwheel.mjs | 1 + packages/draw/spiral.mjs | 1 + packages/midi/midi.mjs | 4 + packages/osc/osc.mjs | 1 + packages/superdough/ola-processor.js | 29 +++-- packages/superdough/reverbGen.mjs | 5 +- packages/superdough/sampler.mjs | 1 + packages/superdough/superdough.mjs | 3 + packages/superdough/wavetable.mjs | 1 + packages/superdough/worklets.mjs | 20 +++- packages/tonal/tonal.mjs | 3 + packages/tonal/voicings.mjs | 4 + packages/webaudio/scope.mjs | 2 + packages/webaudio/spectrum.mjs | 1 + 25 files changed, 179 insertions(+), 94 deletions(-) diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 4dc23996f..193b93e3e 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -379,6 +379,7 @@ function s4() { /** * Overrides the css of highlighted events. Make sure to use single quotes! * @name markcss + * @tag visualization * @example * note("c a f e") * .markcss('text-decoration:underline') diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index 72f951254..cc224a6cc 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -117,6 +117,7 @@ export const sliderPlugin = ViewPlugin.fromClass( * Displays a slider widget to allow the user manipulate a value * * @name slider + * @tags external_io, visualization * @param {number} value Initial value * @param {number} min Minimum value - optional, defaults to 0 * @param {number} max Maximum value - optional, defaults to 1 diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 44b358e67..4e60fc487 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -949,6 +949,7 @@ export const { duckattack } = registerControl('duckattack', 'duckatt'); * * @name byteBeatExpression * @synonyms bbexpr + * @tags effects * * @param {number | Pattern} byteBeatExpression bitwise expression for creating bytebeat * @example @@ -962,6 +963,7 @@ export const { byteBeatExpression, bbexpr } = registerControl('byteBeatExpressio * * @name byteBeatStartTime * @synonyms bbst + * @tags effects * * @param {number | Pattern} byteBeatStartTime in samples (t) * @example @@ -1518,7 +1520,7 @@ export const { delayspeed } = registerControl('delayspeed'); * Sets the time of the delay effect. * * @name delayspeed - * @tags effects, foo + * @tags effects * @param {number | Pattern} delayspeed controls the pitch of the delay feedback * @synonyms delayt, dt * @example @@ -1544,7 +1546,7 @@ export const { delaysync } = registerControl('delaysync'); * Specifies whether delaytime is calculated relative to cps. * * @name lock - * @tags effects, foo + * @tags effects * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. * @superdirtOnly * @example @@ -1812,6 +1814,7 @@ export const { octave } = registerControl('octave'); * An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share the same global effects. * * @name orbit + * @tags effects * @param {number | Pattern} number * @example * stack( diff --git a/packages/core/drawLine.mjs b/packages/core/drawLine.mjs index 7509c0f6f..212d06279 100644 --- a/packages/core/drawLine.mjs +++ b/packages/core/drawLine.mjs @@ -15,6 +15,7 @@ import Fraction, { gcd } from './fraction.mjs'; * - "-" hold previous value * - "." silence * + * @tags visualization * @param {Pattern} pattern the pattern to use * @param {number} chars max number of characters (approximately) * @returns string diff --git a/packages/core/euclid.mjs b/packages/core/euclid.mjs index 44ab07f11..e8252a984 100644 --- a/packages/core/euclid.mjs +++ b/packages/core/euclid.mjs @@ -61,6 +61,7 @@ export const bjork = function (ons, steps) { * * @memberof Pattern * @name euclid + * @tags temporal * @param {number} pulses the number of onsets/beats * @param {number} steps the number of steps to fill * @returns Pattern @@ -73,6 +74,7 @@ export const bjork = function (ons, steps) { * Like `euclid`, but has an additional parameter for 'rotating' the resulting sequence. * @memberof Pattern * @name euclidRot + * @tags temporal * @param {number} pulses the number of onsets/beats * @param {number} steps the number of steps to fill * @param {number} rotation offset in steps @@ -156,6 +158,7 @@ export const { euclidrot, euclidRot } = register(['euclidrot', 'euclidRot'], fun * so there will be no gaps. * @name euclidLegato * @memberof Pattern + * @tags temporal * @param {number} pulses the number of onsets/beats * @param {number} steps the number of steps to fill * @param rotation offset in steps @@ -187,6 +190,7 @@ export const euclidLegato = register(['euclidLegato'], function (pulses, steps, * the resulting sequence * @name euclidLegatoRot * @memberof Pattern + * @tags temporal * @param {number} pulses the number of onsets/beats * @param {number} steps the number of steps to fill * @param {number} rotation offset in steps @@ -208,6 +212,7 @@ export const euclidLegatoRot = register(['euclidLegatoRot'], function (pulses, s * @name euclidish * @synonyms eish * @memberof Pattern + * @tags temporal * @param {number} pulses the number of onsets * @param {number} steps the number of steps to fill * @param {number} groove exists between the extremes of 0 (straight euclidian) and 1 (straight pulse) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 39d6555f5..2d6459a89 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -889,7 +889,7 @@ export class Pattern { /** * Writes the content of the current event to the console (visible in the side menu). - * @tags visualizers + * @tags visualization * @name log * @memberof Pattern * @example @@ -904,7 +904,7 @@ export class Pattern { /** * A simplified version of `log` which writes all "values" (various configurable parameters) * within the event to the console (visible in the side menu). - * @tags visualizers + * @tags visualization * @name logValues * @memberof Pattern * @example @@ -938,7 +938,7 @@ export class Pattern { * source pattern to be looped, and for an (optional) given function to be * applied. False values result in the corresponding part of the source pattern * to be played unchanged. - * @tags structure + * @tags temporal * @name into * @memberof Pattern * @example @@ -1066,6 +1066,7 @@ function _composeOp(a, b, func) { * Assumes a pattern of numbers. Adds the given number to each item in the pattern. * @name add * @memberof Pattern + * @tags math * @example * // Here, the triad 0, 2, 4 is shifted by different amounts * n("0 2 4".add("<0 3 4 0>")).scale("C:major") @@ -1076,7 +1077,6 @@ function _composeOp(a, b, func) { * note("c3 e3 g3".add("<0 5 7 0>")) * // Behind the scenes, the notes are converted to midi numbers: * // note("48 52 55".add("<0 5 7 0>")) - * @tags math */ add: [numeralArgs((a, b) => a + b)], // support string concatenation /** @@ -1084,6 +1084,7 @@ function _composeOp(a, b, func) { * Like add, but the given numbers are subtracted. * @name sub * @memberof Pattern + * @tags math * @example * n("0 2 4".sub("<0 1 2 3>")).scale("C4:minor") * // See add for more information. @@ -1094,6 +1095,7 @@ function _composeOp(a, b, func) { * Multiplies each number by the given factor. * @name mul * @memberof Pattern + * @tags math * @example * "<1 1.5 [1.66, <2 2.33>]>*4".mul(150).freq() */ @@ -1103,6 +1105,7 @@ function _composeOp(a, b, func) { * Divides each number by the given factor. * @name div * @memberof Pattern + * @tags math */ div: [numeralArgs((a, b) => a / b)], mod: [numeralArgs(_mod)], @@ -1189,7 +1192,7 @@ function _composeOp(a, b, func) { /** * Applies the given structure to the pattern: * - * @tags structure + * @tags temporal * @example * note("c,eb,g") * .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~") @@ -1204,7 +1207,7 @@ function _composeOp(a, b, func) { /** * Returns silence when mask is 0 or "~" * - * @tags structure + * @tags temporal * @example * note("c [eb,g] d [eb,g]").mask("<1 [0 1]>") */ @@ -1217,7 +1220,7 @@ function _composeOp(a, b, func) { /** * Resets the pattern to the start of the cycle for each onset of the reset pattern. * - * @tags structure + * @tags temporal * @example * s("[ sd]*2, hh*8").reset("") */ @@ -1231,7 +1234,7 @@ function _composeOp(a, b, func) { * Restarts the pattern for each onset of the restart pattern. * While reset will only reset the current cycle, restart will start from cycle 0. * - * @tags structure + * @tags temporal * @example * s("[ sd]*2, hh*8").restart("") */ @@ -1352,7 +1355,7 @@ export function sequenceP(pats) { /** * The given items are played at the same time at the same length. * - * @tags structure + * @tags temporal * @return {Pattern} * @synonyms polyrhythm, pr * @example @@ -1566,14 +1569,9 @@ export function fastcat(...pats) { return result; } -/** See `fastcat` */ -export function sequence(...pats) { - return fastcat(...pats); -} - /** Like **cat**, but the items are crammed into one cycle. * @tags combiners - * @synonyms sequence, fastcat + * @synonyms seq, fastcat * @example * seq("e5", "b4", ["d5", "c5"]).note() * // "e5 b4 [d5 c5]".note() @@ -1584,6 +1582,9 @@ export function sequence(...pats) { * note("c4(5,8)") * ) */ +export function sequence(...pats) { + return fastcat(...pats); +} export function seq(...pats) { return fastcat(...pats); @@ -1877,7 +1878,7 @@ export const ratio = register('ratio', (pat) => // Structural and temporal transformations /** Compress each cycle into the given timespan, leaving a gap - * @tags structure + * @tags temporal * @example * cat( * s("bd sd").compress(.25,.75), @@ -1899,7 +1900,7 @@ export const { compressSpan, compressspan } = register(['compressSpan', 'compres /** * speeds up a pattern like fast, but rather than it playing multiple times as fast would it instead leaves a gap in the remaining space of the cycle. For example, the following will play the sound pattern "bd sn" only once but compressed into the first half of the cycle, i.e. twice as fast. - * @tags structure + * @tags temporal * @name fastGap * @synonyms fastgap * @example @@ -1937,7 +1938,7 @@ export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (f /** * Similar to `compress`, but doesn't leave gaps, and the 'focus' can be bigger than a cycle - * @tags structure + * @tags temporal * @example * s("bd hh sd hh").focus(1/4, 3/4) */ @@ -1955,7 +1956,7 @@ export const { focusSpan, focusspan } = register(['focusSpan', 'focusspan'], fun }); /** The ply function repeats each event the given number of times. - * @tags structure + * @tags temporal * @example * s("bd ~ sd cp").ply("<1 2 3>") */ @@ -1970,7 +1971,7 @@ export const ply = register('ply', function (factor, pat) { /** * Speed up a pattern by the given factor. Used by "*" in mini notation. * - * @tags structure + * @tags temporal * @name fast * @synonyms density * @memberof Pattern @@ -1995,7 +1996,7 @@ export const { fast, density } = register( /** * Both speeds up the pattern (like 'fast') and the sample playback (like 'speed'). - * @tags structure + * @tags temporal * @example * s("bd sd:2").hurry("<1 2 4 3>").slow(1.5) */ @@ -2006,7 +2007,7 @@ export const hurry = register('hurry', function (r, pat) { /** * Slow down a pattern over the given number of cycles. Like the "/" operator in mini notation. * - * @tags structure + * @tags temporal * @name slow * @synonyms sparsity * @memberof Pattern @@ -2024,7 +2025,7 @@ export const { slow, sparsity } = register(['slow', 'sparsity'], function (facto /** * Carries out an operation 'inside' a cycle. - * @tags structure + * @tags temporal * @example * "0 1 2 3 4 3 2 1".inside(4, rev).scale('C major').note() * // "0 1 2 3 4 3 2 1".slow(4).rev().fast(4).scale('C major').note() @@ -2035,7 +2036,7 @@ export const inside = register('inside', function (factor, f, pat) { /** * Carries out an operation 'outside' a cycle. - * @tags structure + * @tags temporal * @example * "<[0 1] 2 [3 4] 5>".outside(4, rev).scale('C major').note() * // "<[0 1] 2 [3 4] 5>".fast(4).rev().slow(4).scale('C major').note() @@ -2046,7 +2047,7 @@ export const outside = register('outside', function (factor, f, pat) { /** * Applies the given function every n cycles, starting from the last cycle. - * @tags structure + * @tags temporal * @name lastOf * @memberof Pattern * @param {number} n how many cycles @@ -2063,7 +2064,7 @@ export const lastOf = register('lastOf', function (n, func, pat) { /** * Applies the given function every n cycles, starting from the first cycle. - * @tags structure + * @tags temporal * @name firstOf * @memberof Pattern * @param {number} n how many cycles @@ -2075,7 +2076,7 @@ export const lastOf = register('lastOf', function (n, func, pat) { /** * An alias for `firstOf` - * @tags structure + * @tags temporal * @name every * @memberof Pattern * @param {number} n how many cycles @@ -2092,7 +2093,7 @@ export const { firstOf, every } = register(['firstOf', 'every'], function (n, fu /** * Like layer, but with a single function: - * @tags structure + * @tags temporal * @name apply * @example * "".scale('C minor').apply(scaleTranspose("0,2,4")).note() @@ -2104,7 +2105,7 @@ export const apply = register('apply', function (func, pat) { /** * Plays the pattern at the given cycles per minute. - * @tags structure + * @tags temporal * @deprecated * @example * s(",hh*2").cpm(90) // = 90 bpm @@ -2117,7 +2118,7 @@ export const cpm = register('cpm', function (cpm, pat) { /** * Nudge a pattern to start earlier in time. Equivalent of Tidal's <~ operator * - * @tags structure + * @tags temporal * @name early * @memberof Pattern * @param {number | Pattern} cycles number of cycles to nudge left @@ -2138,7 +2139,7 @@ export const early = register( /** * Nudge a pattern to start later in time. Equivalent of Tidal's ~> operator * - * @tags structure + * @tags temporal * @name late * @memberof Pattern * @param {number | Pattern} cycles number of cycles to nudge right @@ -2159,7 +2160,7 @@ export const late = register( /** * Plays a portion of a pattern, specified by the beginning and end of a time span. The new resulting pattern is played over the time period of the original pattern: * - * @tags structure + * @tags temporal * @example * s("bd*2 hh*3 [sd bd]*2 perc").zoom(0.25, 0.75) * // s("hh*3 [sd bd]*2") // equivalent @@ -2186,7 +2187,7 @@ export const { zoomArc, zoomarc } = register(['zoomArc', 'zoomarc'], function (a /** * Splits a pattern into the given number of slices, and plays them according to a pattern of slice numbers. * Similar to `slice`, but slices up patterns rather than sound samples. - * @tags structure + * @tags temporal * @param {number} number of slices * @param {number} slices to play * @example @@ -2214,7 +2215,7 @@ export const bite = register( /** * Selects the given fraction of the pattern and repeats that part to fill the remainder of the cycle. - * @tags structure + * @tags temporal * @param {number} fraction fraction to select * @example * s("lt ht mt cp, [hh oh]*2").linger("<1 .5 .25 .125>") @@ -2235,7 +2236,7 @@ export const linger = register( /** * Samples the pattern at a rate of n events per cycle. Useful for turning a continuous pattern into a discrete one. - * @tags structure + * @tags temporal * @name segment * @synonyms seg * @param {number} segments number of segments per cycle @@ -2248,7 +2249,7 @@ export const { segment, seg } = register(['segment', 'seg'], function (rate, pat /** * 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 - * @tags structure + * @tags temporal * @param {number} subdivision * @param {number} offset * @example @@ -2258,7 +2259,7 @@ export const swingBy = register('swingBy', (swing, n, pat) => pat.inside(n, late /** * Shorthand for swingBy with 1/3: - * @tags structure + * @tags temporal * @param {number} subdivision * @example * s("hh*8").swing(4) @@ -2268,7 +2269,7 @@ export const swing = register('swing', (n, pat) => pat.swingBy(1 / 3, n)); /** * Swaps 1s and 0s in a binary pattern. - * @tags structure + * @tags temporal * @name invert * @synonyms inv * @example @@ -2286,7 +2287,7 @@ export const { invert, inv } = register( /** * Applies the given function whenever the given pattern is in a true state. - * @tags structure + * @tags temporal * @name when * @memberof Pattern * @param {Pattern} binary_pat @@ -2301,7 +2302,7 @@ export const when = register('when', function (on, func, pat) { /** * Superimposes the function result on top of the original pattern, delayed by the given time. - * @tags structure + * @tags temporal * @name off * @memberof Pattern * @param {Pattern | number} time offset time @@ -2318,7 +2319,7 @@ export const off = register('off', function (time_pat, func, pat) { * Returns a new pattern where every other cycle is played once, twice as * fast, and offset in time by one quarter of a cycle. Creates a kind of * breakbeat feel. - * @tags structure + * @tags temporal * @returns Pattern */ export const brak = register('brak', function (pat) { @@ -2328,7 +2329,7 @@ export const brak = register('brak', function (pat) { /** * Reverse all haps in a pattern * - * @tags structure + * @tags temporal * @name rev * @memberof Pattern * @returns Pattern @@ -2362,7 +2363,7 @@ export const rev = register( /** Like press, but allows you to specify the amount by which each * event is shifted. pressBy(0.5) is the same as press, while * pressBy(1/3) shifts each event by a third of its timespan. - * @tags structure + * @tags temporal * @example * stack(s("hh*4"), * s("bd mt sd ht").pressBy("<0 0.5 0.25>") @@ -2374,7 +2375,7 @@ export const pressBy = register('pressBy', function (r, pat) { /** * Syncopates a rhythm, by shifting each event halfway into its timespan. - * @tags structure + * @tags temporal * @example * stack(s("hh*4"), * s("bd mt sd ht").every(4, press) @@ -2386,7 +2387,7 @@ export const press = register('press', function (pat) { /** * Silences a pattern. - * @tags structure + * @tags temporal * @example * stack( * s("bd").hush(), @@ -2399,7 +2400,7 @@ Pattern.prototype.hush = function () { /** * Applies `rev` to a pattern every other cycle, so that the pattern alternates between forwards and backwards. - * @tags structure + * @tags temporal * @example * note("c d e g").palindrome() */ @@ -2414,7 +2415,7 @@ export const palindrome = register( /** * Jux with adjustable stereo width. 0 = mono, 1 = full stereo. - * @tags structure + * @tags temporal * @name juxBy * @synonyms juxby * @example @@ -2436,7 +2437,7 @@ export const { juxBy, juxby } = register(['juxBy', 'juxby'], function (by, func, /** * The jux function creates strange stereo effects, by applying a function to a pattern, but only in the right-hand channel. - * @tags structure + * @tags temporal * @example * s("bd lt [~ ht] mt cp ~ bd hh").jux(rev) * @example @@ -2450,7 +2451,7 @@ export const jux = register('jux', function (func, pat) { /** * Superimpose and offset multiple times, applying the given function each time. - * @tags structure + * @tags temporal * @name echoWith * @synonyms echowith, stutWith, stutwith * @param {number} times how many times to repeat @@ -2470,7 +2471,7 @@ export const { echoWith, echowith, stutWith, stutwith } = register( /** * Superimpose and offset multiple times, gradually decreasing the velocity - * @tags structure + * @tags temporal * @name echo * @memberof Pattern * @returns Pattern @@ -2486,7 +2487,7 @@ export const echo = register('echo', function (times, time, feedback, pat) { /** * Deprecated. Like echo, but the last 2 parameters are flipped. - * @tags structure + * @tags temporal * @name stut * @param {number} times how many times to repeat * @param {number} feedback velocity multiplicator for each iteration @@ -2508,7 +2509,7 @@ export const applyN = register('applyN', function (n, func, p) { /** * The plyWith function repeats each event the given number of times, applying the given function to each event.\n - * @tags structure + * @tags temporal * @name plyWith * @synonyms plywith * @param {number} factor how many times to repeat @@ -2531,7 +2532,7 @@ export const plyWith = register(['plyWith', 'plywith'], function (factor, func, /** * The plyForEach function repeats each event the given number of times, applying the given function to each event. * This version of ply uses the iteration index as an argument to the function, similar to echoWith. - * @tags structure + * @tags temporal * @name plyForEach * @synonyms plyforeach * @param {number} factor how many times to repeat @@ -2553,7 +2554,7 @@ export const plyForEach = register(['plyForEach', 'plyforeach'], function (facto /** * Divides a pattern into a given number of subdivisions, plays the subdivisions in order, but increments the starting subdivision each cycle. The pattern wraps to the first subdivision after the last subdivision is played. - * @tags structure + * @tags temporal * @name iter * @memberof Pattern * @returns Pattern @@ -2581,7 +2582,7 @@ export const iter = register( /** * Like `iter`, but plays the subdivisions in reverse order. Known as iter' in tidalcycles - * @tags structure + * @tags temporal * @name iterBack * @synonyms iterback * @memberof Pattern @@ -2600,7 +2601,7 @@ export const { iterBack, iterback } = register( /** * Repeats each cycle the given number of times. - * @tags structure + * @tags temporal * @name repeatCycles * @memberof Pattern * @returns Pattern @@ -2624,7 +2625,7 @@ export const { repeatCycles } = register( /** * Divides a pattern into a given number of parts, then cycles through those parts in turn, applying the given function to each part in turn (one part per cycle). - * @tags structure + * @tags temporal * @name chunk * @synonyms slowChunk, slowchunk * @memberof Pattern @@ -2656,7 +2657,7 @@ export const { chunk, slowchunk, slowChunk } = register( /** * Like `chunk`, but cycles through the parts in reverse order. Known as chunk' in tidalcycles - * @tags structure + * @tags temporal * @name chunkBack * @synonyms chunkback * @memberof Pattern @@ -2677,7 +2678,7 @@ export const { chunkBack, chunkback } = register( /** * Like `chunk`, but the cycles of the source pattern aren't repeated * for each set of chunks. - * @tags structure + * @tags temporal * @name fastChunk * @synonyms fastchunk * @memberof Pattern @@ -2698,7 +2699,7 @@ export const { fastchunk, fastChunk } = register( /** * Like `chunk`, but the function is applied to a looped subcycle of the source pattern. - * @tags structure + * @tags temporal * @name chunkInto * @synonyms chunkinto * @memberof Pattern @@ -2712,7 +2713,7 @@ export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], fun /** * Like `chunkInto`, but moves backwards through the chunks. - * @tags structure + * @tags temporal * @name chunkBackInto * @synonyms chunkbackinto * @memberof Pattern @@ -2743,7 +2744,7 @@ export const bypass = register( /** * Loops the pattern inside an `offset` for `cycles`. * If you think of the entire span of time in cycles as a ribbon, you can cut a single piece and loop it. - * @tags structure + * @tags temporal * @name ribbon * @synonyms rib * @param {number} offset start point of loop in cycles @@ -2772,7 +2773,7 @@ export const hsl = register('hsl', (h, s, l, pat) => { /** * Tags each Hap with an identifier. Good for filtering. The function populates Hap.context.tags (Array). * @name tag - * @tags structure + * @tags temporal * @noAutocomplete * @param {string} tag anything unique */ @@ -2783,7 +2784,7 @@ Pattern.prototype.tag = function (tag) { /** * Filters haps using the given function * @name filter - * @tags structure + * @tags temporal * @param {Function} test function to test Hap * @example * s("hh!7 oh").filter(hap => hap.value.s==='hh') @@ -2793,7 +2794,7 @@ export const filter = register('filter', (test, pat) => pat.withHaps((haps) => h /** * Filters haps by their begin time * @name filterWhen - * @tags structure + * @tags temporal * @noAutocomplete * @param {Function} test function to test Hap.whole.begin */ @@ -2802,7 +2803,7 @@ export const filterWhen = register('filterWhen', (test, pat) => pat.filter((h) = /** * Use within to apply a function to only a part of a pattern. * @name within - * @tags structure + * @tags temporal * @param {number} start start within cycle (0 - 1) * @param {number} end end within cycle (0 - 1). Must be > start * @param {Function} func function to be applied to the sub-pattern @@ -2877,7 +2878,7 @@ export function _match(span, hap_p) { * *Experimental* * * Speeds a pattern up or down, to fit to the given number of steps per cycle. - * @tags structure + * @tags temporal * @example * sound("bd sd cp").pace(4) * // The same as sound("{bd sd cp}%4") or sound("*4") @@ -2919,7 +2920,7 @@ export function _polymeterListSteps(steps, ...args) { * *Experimental* * * Aligns the steps of the patterns, creating polymeters. The patterns are repeated until they all fit the cycle. For example, in the below the first pattern is repeated twice, and the second is repeated three times, to fit the lowest common multiple of six steps. - * @tags structure + * @tags temporal * @synonyms pm * @example * // The same as note("{c eb g, c2 g2}%6") @@ -3037,7 +3038,7 @@ export function stepalt(...groups) { * * Takes the given number of steps from a pattern (dropping the rest). * A positive number will take steps from the start of a pattern, and a negative number from the end. - * @tags structure + * @tags temporal * @return {Pattern} * @example * "bd cp ht mt".take("2").sound() @@ -3082,7 +3083,7 @@ export const take = stepRegister('take', function (i, pat) { * * Drops the given number of steps from a pattern. * A positive number will drop steps from the start of a pattern, and a negative number from the end. - * @tags structure + * @tags temporal * @return {Pattern} * @example * "tha dhi thom nam".drop("1").sound().bank("mridangam") @@ -3111,7 +3112,7 @@ export const drop = stepRegister('drop', function (i, pat) { * `extend` is similar to `fast` in that it increases its density, but it also increases the step count * accordingly. So `stepcat("a b".extend(2), "c d")` would be the same as `"a b a b c d"`, whereas * `stepcat("a b".fast(2), "c d")` would be the same as `"[a b] [a b] c d"`. - * @tags structure + * @tags temporal * @example * stepcat( * sound("bd bd - cp").extend(2), @@ -3126,7 +3127,7 @@ export const extend = stepRegister('extend', function (factor, pat) { * *Experimental* * * Expands the step size of the pattern by the given factor. - * @tags structure + * @tags temporal * @example * sound("tha dhi thom nam").bank("mridangam").expand("3 2 1 1 2 3").pace(8) */ @@ -3138,7 +3139,7 @@ export const expand = stepRegister('expand', function (factor, pat) { * *Experimental* * * Contracts the step size of the pattern by the given factor. See also `expand`. - * @tags structure + * @tags temporal * @example * sound("tha dhi thom nam").bank("mridangam").contract("3 2 1 1 2 3").pace(8) */ @@ -3193,7 +3194,7 @@ export const shrinklist = (amount, pat) => pat.shrinklist(amount); * Progressively shrinks the pattern by 'n' steps until there's nothing left, or if a second value is given (using mininotation list syntax with `:`), * that number of times. * A positive number will progressively drop steps from the start of a pattern, and a negative number from the end. - * @tags structure + * @tags temporal * @return {Pattern} * @example * "tha dhi thom nam".shrink("1").sound() @@ -3233,7 +3234,7 @@ export const shrink = register( * Progressively grows the pattern by 'n' steps until the full pattern is played, or if a second value is given (using mininotation list syntax with `:`), * that number of times. * A positive number will progressively grow steps from the start of a pattern, and a negative number from the end. - * @tags structure + * @tags temporal * @return {Pattern} * @example * "tha dhi thom nam".grow("1").sound() @@ -3552,7 +3553,9 @@ export const { loopAtCps, loopatcps } = register(['loopAtCps', 'loopatcps'], fun return _loopAt(factor, pat, cps); }); -/** exposes a custom value at query time. basically allows mutating state without evaluation */ +/** exposes a custom value at query time. basically allows mutating state without evaluation + * @tags internals + */ export const ref = (accessor) => pure(1) .withValue(() => reify(accessor())) @@ -3589,7 +3592,7 @@ Pattern.prototype.xfade = function (pos, b) { * creates a structure pattern from divisions of a cycle * especially useful for creating rhythms * @name beat - * @tags structure + * @tags temporal * @example * s("bd").beat("0,7,10", 16) * @example @@ -3666,7 +3669,7 @@ export const _morph = (from, to, by) => { * sine.slow(8) // slowly morph between the rhythms * ) * ) - * @tags structure + * @tags temporal */ export const morph = (frompat, topat, bypat) => { frompat = reify(frompat); diff --git a/packages/core/pick.mjs b/packages/core/pick.mjs index 206fa619b..cce691905 100644 --- a/packages/core/pick.mjs +++ b/packages/core/pick.mjs @@ -28,6 +28,7 @@ const _pick = function (lookup, pat, modulo = true) { /** * Picks patterns (or plain values) either from a list (by index) or a lookup table (by name). * Similar to `inhabit`, but maintains the structure of the original patterns. + * @tags combiners * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -57,6 +58,7 @@ const __pick = register('pick', function (lookup, pat) { * it wraps around, rather than sticking at the maximum value. * For example, if you pick the fifth pattern of a list of three, you'll get the * second one. + * @tags combiners * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -67,6 +69,7 @@ export const pickmod = register('pickmod', function (lookup, pat) { }); /** * pickF lets you use a pattern of numbers to pick which function to apply to another pattern. + * @tags combiners, functional * @param {Pattern} pat * @param {Pattern} lookup a pattern of indices * @param {function[]} funcs the array of functions from which to pull @@ -83,6 +86,7 @@ export const pickF = register('pickF', function (lookup, funcs, pat) { /** * The same as `pickF`, but if you pick a number greater than the size of the functions list, * it wraps around, rather than sticking at the maximum value. + * @tags combiners * @param {Pattern} pat * @param {Pattern} lookup a pattern of indices * @param {function[]} funcs the array of functions from which to pull @@ -93,6 +97,7 @@ export const pickmodF = register('pickmodF', function (lookup, funcs, pat) { }); /** * Similar to `pick`, but it applies an outerJoin instead of an innerJoin. + * @tags combiners * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -103,6 +108,7 @@ export const pickOut = register('pickOut', function (lookup, pat) { /** * The same as `pickOut`, but if you pick a number greater than the size of the list, * it wraps around, rather than sticking at the maximum value. + * @tags combiners * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -112,6 +118,7 @@ export const pickmodOut = register('pickmodOut', function (lookup, pat) { }); /** * Similar to `pick`, but the choosen pattern is restarted when its index is triggered. + * @tags combiners * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -122,6 +129,7 @@ export const pickRestart = register('pickRestart', function (lookup, pat) { /** * The same as `pickRestart`, but if you pick a number greater than the size of the list, * it wraps around, rather than sticking at the maximum value. + * @tags combiners * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -138,6 +146,7 @@ export const pickmodRestart = register('pickmodRestart', function (lookup, pat) }); /** * Similar to `pick`, but the choosen pattern is reset when its index is triggered. + * @tags combiners * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -148,6 +157,7 @@ export const pickReset = register('pickReset', function (lookup, pat) { /** * The same as `pickReset`, but if you pick a number greater than the size of the list, * it wraps around, rather than sticking at the maximum value. + * @tags combiners * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -159,6 +169,7 @@ export const pickmodReset = register('pickmodReset', function (lookup, pat) { /** Picks patterns (or plain values) either from a list (by index) or a lookup table (by name). * Similar to `pick`, but cycles are squeezed into the target ('inhabited') pattern. * @name inhabit + * @tags combiners * @synonyms pickSqueeze * @param {Pattern} pat * @param {*} xs @@ -180,6 +191,7 @@ export const { inhabit, pickSqueeze } = register(['inhabit', 'pickSqueeze'], fun * second one. * @name inhabitmod * @synonyms pickmodSqueeze + * @tags combiners * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -192,6 +204,7 @@ export const { inhabitmod, pickmodSqueeze } = register(['inhabitmod', 'pickmodSq /** * Pick from the list of values (or patterns of values) via the index using the given * pattern of integers. The selected pattern will be compressed to fit the duration of the selecting event + * @tags combiners * @param {Pattern} pat * @param {*} xs * @returns {Pattern} diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index 53562f216..b8bab3d4b 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -102,6 +102,7 @@ export function repl({ * Changes the global tempo to the given cycles per minute * * @name setcpm + * @tags temporal * @alias setCpm * @param {number} cpm cycles per minute * @example @@ -127,6 +128,8 @@ export function repl({ * $: sound("hh*8") * all(x => x.pianoroll()) * ``` + * + * @tags combiners */ let allTransforms = []; const all = function (transform) { @@ -134,11 +137,13 @@ export function repl({ return silence; }; /** Applies a function to each of the running patterns separately. This is intended for future use with upcoming 'stepwise' features. See `all` for a version that applies the function to all the patterns stacked together into a single pattern. + * * ``` * $: sound("bd - cp sd") * $: sound("hh*8") * each(fast("<2 3>")) * ``` + * @tags combiners */ const each = function (transform) { eachTransform = transform; diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 03dd641a5..2c3a63324 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -337,6 +337,7 @@ export const scramble = register('scramble', (n, pat) => { export const rand = signal(timeToRand); /** * A continuous pattern of random numbers, between -1 and 1 + * @tags generators */ export const rand2 = rand.toBipolar(); diff --git a/packages/csound/index.mjs b/packages/csound/index.mjs index e44765227..e459f3873 100644 --- a/packages/csound/index.mjs +++ b/packages/csound/index.mjs @@ -135,6 +135,8 @@ export async function loadOrc(url) { * p4 -- MIDI key number (as a real number, not an integer but in [0, 127]. * p5 -- MIDI velocity (as a real number, not an integer but in [0, 127]. * p6 -- Strudel controls, as a string. + * + * @tags external_io */ export const csoundm = register('csoundm', (instrument, pat) => { let p1 = instrument; diff --git a/packages/draw/pianoroll.mjs b/packages/draw/pianoroll.mjs index 1cf218fa0..6fb1dfc76 100644 --- a/packages/draw/pianoroll.mjs +++ b/packages/draw/pianoroll.mjs @@ -42,6 +42,7 @@ const getValue = (e) => { * * @name pianoroll * @synonyms punchcard + * @tags visualization * @param {Object} options Object containing all the optional following parameters as key value pairs: * @param {integer} cycles number of cycles to be displayed at the same time - defaults to 4 * @param {number} playhead location of the active notes on the time axis - 0 to 1, defaults to 0.5 @@ -299,6 +300,7 @@ Pattern.prototype.punchcard = function (options) { * Supports all the same options as pianoroll. * * @name wordfall + * @tags visualization */ Pattern.prototype.wordfall = function (options) { return this.punchcard({ vertical: 1, labels: 1, stroke: 0, fillActive: 1, active: 'white', ...options }); diff --git a/packages/draw/pitchwheel.mjs b/packages/draw/pitchwheel.mjs index ba76df079..f9dacc53f 100644 --- a/packages/draw/pitchwheel.mjs +++ b/packages/draw/pitchwheel.mjs @@ -116,6 +116,7 @@ export function pitchwheel({ /** * Renders a pitch circle to visualize frequencies within one octave * @name pitchwheel + * @tags visualization * @param {number} hapcircles * @param {number} circle * @param {number} edo diff --git a/packages/draw/spiral.mjs b/packages/draw/spiral.mjs index cebf3d374..c18c66c59 100644 --- a/packages/draw/spiral.mjs +++ b/packages/draw/spiral.mjs @@ -129,6 +129,7 @@ function drawSpiral(options) { * Displays a spiral visual. * * @name spiral + * @tags visualization * @param {Object} options Object containing all the optional following parameters as key value pairs: * @param {number} stretch controls the rotations per cycle ratio, where 1 = 1 cycle / 360 degrees * @param {number} size the diameter of the spiral diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index ae983d200..bff36c870 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -123,6 +123,7 @@ function githubPath(base, subpath = '') { /** * configures the default midimap, which is used when no "midimap" port is set + * @tags external_io * @example * defaultmidimap({ lpf: 74 }) * $: note("c a f e").midi(); @@ -136,6 +137,7 @@ let loadCache = {}; /** * Adds midimaps to the registry. Inside each midimap, control names (e.g. lpf) are mapped to cc numbers. + * @tags external_io * @example * midimaps({ mymap: { lpf: 74 } }) * $: note("c a f e") @@ -278,6 +280,7 @@ function sendNote(note, velocity, duration, device, midichan, timeOffsetString) /** * MIDI output: Opens a MIDI output port. + * @tags external_io * @param {string | number} midiport MIDI device name or index defaulting to 0 * @param {object} options Additional MIDI configuration options * @example @@ -465,6 +468,7 @@ const refs = {}; /** * MIDI input: Opens a MIDI input port to receive MIDI control change messages. + * @tags external_io * @param {string | number} input MIDI device name or index defaulting to 0 * @returns {Function} * @example diff --git a/packages/osc/osc.mjs b/packages/osc/osc.mjs index fe0691522..1358ad2db 100644 --- a/packages/osc/osc.mjs +++ b/packages/osc/osc.mjs @@ -78,6 +78,7 @@ export async function oscTrigger(hap, currentTime, cps = 1, targetTime) { * For more info, read [MIDI & OSC in the docs](https://strudel.cc/learn/input-output/) * * @name osc + * @tags external_io * @memberof Pattern * @returns Pattern */ diff --git a/packages/superdough/ola-processor.js b/packages/superdough/ola-processor.js index 38d45a25d..0000d1bbb 100644 --- a/packages/superdough/ola-processor.js +++ b/packages/superdough/ola-processor.js @@ -35,7 +35,9 @@ class OLAProcessor extends AudioWorkletProcessor { } /** Handles dynamic reallocation of input/output channels buffer - (channel numbers may lety during lifecycle) **/ + * (channel numbers may vary during lifecycle) + * @tags internals + **/ reallocateChannelsIfNeeded(inputs, outputs) { for (let i = 0; i < this.nbInputs; i++) { let nbChannels = inputs[i].length; @@ -88,7 +90,10 @@ class OLAProcessor extends AudioWorkletProcessor { } } - /** Read next web audio block to input buffers **/ + /** + * Read next web audio block to input buffers + * @tags internals + **/ readInputs(inputs) { // when playback is paused, we may stop receiving new samples if (inputs[0].length && inputs[0][0].length == 0) { @@ -108,7 +113,9 @@ class OLAProcessor extends AudioWorkletProcessor { } } - /** Write next web audio block from output buffers **/ + /** Write next web audio block from output buffers + * @tags internals + **/ writeOutputs(outputs) { for (let i = 0; i < this.nbInputs; i++) { for (let j = 0; j < this.inputBuffers[i].length; j++) { @@ -118,7 +125,9 @@ class OLAProcessor extends AudioWorkletProcessor { } } - /** Shift left content of input buffers to receive new web audio block **/ + /** Shift left content of input buffers to receive new web audio block + * @tags internals + **/ shiftInputBuffers() { for (let i = 0; i < this.nbInputs; i++) { for (let j = 0; j < this.inputBuffers[i].length; j++) { @@ -127,7 +136,9 @@ class OLAProcessor extends AudioWorkletProcessor { } } - /** Shift left content of output buffers to receive new web audio block **/ + /** Shift left content of output buffers to receive new web audio block + * @tags internals + **/ shiftOutputBuffers() { for (let i = 0; i < this.nbOutputs; i++) { for (let j = 0; j < this.outputBuffers[i].length; j++) { @@ -137,7 +148,9 @@ class OLAProcessor extends AudioWorkletProcessor { } } - /** Copy contents of input buffers to buffer actually sent to process **/ + /** Copy contents of input buffers to buffer actually sent to process + * @tags internals + **/ prepareInputBuffersToSend() { for (let i = 0; i < this.nbInputs; i++) { for (let j = 0; j < this.inputBuffers[i].length; j++) { @@ -146,7 +159,9 @@ class OLAProcessor extends AudioWorkletProcessor { } } - /** Add contents of output buffers just processed to output buffers **/ + /** Add contents of output buffers just processed to output buffers + * @tags internals + **/ handleOutputBuffersToRetrieve() { for (let i = 0; i < this.nbOutputs; i++) { for (let j = 0; j < this.outputBuffers[i].length; j++) { diff --git a/packages/superdough/reverbGen.mjs b/packages/superdough/reverbGen.mjs index 42da7c6d5..c52188b08 100644 --- a/packages/superdough/reverbGen.mjs +++ b/packages/superdough/reverbGen.mjs @@ -16,6 +16,7 @@ var reverbGen = {}; /** Generates a reverb impulse response. + @tags internals @param {!Object} params TODO: Document the properties. @param {!function(!AudioBuffer)} callback Function to call when the impulse response has been generated. The impulse response @@ -48,7 +49,7 @@ reverbGen.generateReverb = function (params, callback) { /** Creates a canvas element showing a graph of the given data. - + @tags internals @param {!Float32Array} data An array of numbers, or a Float32Array. @param {number} width Width in pixels of the canvas. @param {number} height Height in pixels of the canvas. @@ -80,7 +81,7 @@ reverbGen.generateGraph = function (data, width, height, min, max) { @param {number} lpFreqEndAt @param {!function(!AudioBuffer)} callback May be called immediately within the current execution context, or later. - @tags effects + @tags internals */ var applyGradualLowpass = function (input, lpFreqStart, lpFreqEnd, lpFreqEndAt, callback) { if (lpFreqStart == 0) { diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index b195ba79c..69a949ad1 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -221,6 +221,7 @@ export async function fetchSampleMap(url) { /** * Loads a collection of samples to use with `s` + * @tags samples * @example * samples('github:tidalcycles/dirt-samples'); * s("[bd ~]*2, [~ hh]*2, ~ sd") diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 52ed9bff8..873fdfa5d 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -91,6 +91,8 @@ async function aliasBankPath(path) { * Optionally accepts a single argument string of a path to a JSON file containing bank aliases. * @param {string} bank - The bank to alias * @param {string} alias - The alias to use for the bank + * + * @tags samples */ export async function aliasBank(...args) { switch (args.length) { @@ -109,6 +111,7 @@ export async function aliasBank(...args) { /** * Register an alias for a sound. + * @tags samples * @param {string} original - The original sound name * @param {string} alias - The alias to use for the sound */ diff --git a/packages/superdough/wavetable.mjs b/packages/superdough/wavetable.mjs index 44367f786..cfc669c83 100644 --- a/packages/superdough/wavetable.mjs +++ b/packages/superdough/wavetable.mjs @@ -180,6 +180,7 @@ export function registerWaveTable(key, tables, params) { * Loads a collection of wavetables to use with `s` * * @name tables + * @tags effects */ export const tables = async (url, frameLen, json, options = {}) => { if (json !== undefined) return _processTables(json, url, frameLen); diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index ccfaf5107..3e8276225 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -635,14 +635,18 @@ class PhaseVocoderProcessor extends OLAProcessor { this.timeCursor += this.hopSize; } - /** Apply Hann window in-place */ + /** Apply Hann window in-place + * @tags internals + */ applyHannWindow(input) { for (var i = 0; i < this.blockSize; i++) { input[i] = input[i] * this.hannWindow[i] * 1.62; } } - /** Compute squared magnitudes for peak finding **/ + /** Compute squared magnitudes for peak finding + * @tags internals + **/ computeMagnitudes() { var i = 0, j = 0; @@ -656,7 +660,9 @@ class PhaseVocoderProcessor extends OLAProcessor { } } - /** Find peaks in spectrum magnitudes **/ + /** Find peaks in spectrum magnitudes + * @tags internals + **/ findPeaks() { this.nbPeaks = 0; var i = 2; @@ -680,7 +686,9 @@ class PhaseVocoderProcessor extends OLAProcessor { } } - /** Shift peaks and regions of influence by pitchFactor into new specturm */ + /** Shift peaks and regions of influence by pitchFactor into new specturm + * @tags internals + */ shiftPeaks(pitchFactor) { // zero-fill new spectrum this.freqComplexBufferShifted.fill(0); @@ -843,7 +851,9 @@ class PulseOscillatorProcessor extends AudioWorkletProcessor { registerProcessor('pulse-oscillator', PulseOscillatorProcessor); -/** BYTE BEATS */ +/** BYTE BEATS + * @tags internals + */ const chyx = { /*bit*/ bitC: function (x, y, z) { return x & y ? z : 0; diff --git a/packages/tonal/tonal.mjs b/packages/tonal/tonal.mjs index ae75ab690..613ba51e0 100644 --- a/packages/tonal/tonal.mjs +++ b/packages/tonal/tonal.mjs @@ -92,6 +92,7 @@ function scaleOffset(scale, offset, note) { * - 5P = perfect fifth * - 5d = diminished fifth * + * @tags music_theory * @param {string | number} amount Either number of semitones or interval string. * @returns Pattern * @memberof Pattern @@ -146,6 +147,7 @@ export const { transpose, trans } = register(['transpose', 'trans'], function tr * * @memberof Pattern * @name scaleTranspose + * @tags music_theory * @param {offset} offset number of steps inside the scale * @returns Pattern * @synonyms scaleTrans, strans @@ -235,6 +237,7 @@ function _getNearestScaleNote(scaleName, note, preferHigher = true) { * The root note defaults to octave 3, if no octave number is given. * * @name scale + * @tags music_theory * @param {string} scale Name of scale * @returns Pattern * @example diff --git a/packages/tonal/voicings.mjs b/packages/tonal/voicings.mjs index d81911e01..6dce6ca8b 100644 --- a/packages/tonal/voicings.mjs +++ b/packages/tonal/voicings.mjs @@ -90,6 +90,7 @@ export const setVoicingRange = (name, range) => addVoicings(name, voicingRegistr * Adds a new custom voicing dictionary. * * @name addVoicings + * @tags music_theory * @memberof Pattern * @param {string} name identifier for the voicing dictionary * @param {Object} dictionary maps chord symbol to possible voicings @@ -133,6 +134,7 @@ const getVoicing = (chord, dictionaryName, lastVoicing) => { * Uses [chord-voicings package](https://github.com/felixroos/chord-voicings#chord-voicings). * * @name voicings + * @tags music_theory * @memberof Pattern * @param {string} dictionary which voicing dictionary to use. * @returns Pattern @@ -157,6 +159,7 @@ export const voicings = register('voicings', function (dictionary, pat) { * Maps the chords of the incoming pattern to root notes in the given octave. * * @name rootNotes + * @tags music_theory * @memberof Pattern * @param {octave} octave octave to use * @returns Pattern @@ -189,6 +192,7 @@ export const rootNotes = register('rootNotes', function (octave, pat) { * If you pass a pattern of strings to voicing, they will be interpreted as chords. * * @name voicing + * @tags music_theory * @returns Pattern * @example * n("0 1 2 3").chord("").voicing() diff --git a/packages/webaudio/scope.mjs b/packages/webaudio/scope.mjs index e423b20fc..866e0a435 100644 --- a/packages/webaudio/scope.mjs +++ b/packages/webaudio/scope.mjs @@ -98,6 +98,7 @@ function clearScreen(smear = 0, smearRGB = `0,0,0`, ctx = getDrawContext()) { /** * Renders an oscilloscope for the frequency domain of the audio signal. * @name fscope + * @tags visualization * @param {string} color line color as hex or color name. defaults to white. * @param {number} scale scales the y-axis. Defaults to 0.25 * @param {number} pos y-position relative to screen height. 0 = top, 1 = bottom of screen @@ -122,6 +123,7 @@ Pattern.prototype.fscope = function (config = {}) { * Renders an oscilloscope for the time domain of the audio signal. * @name scope * @synonyms tscope + * @tags visualization * @param {object} config optional config with options: * @param {boolean} align if 1, the scope will be aligned to the first zero crossing. defaults to 1 * @param {string} color line color as hex or color name. defaults to white. diff --git a/packages/webaudio/spectrum.mjs b/packages/webaudio/spectrum.mjs index 2ddd214fa..c67e321b5 100644 --- a/packages/webaudio/spectrum.mjs +++ b/packages/webaudio/spectrum.mjs @@ -5,6 +5,7 @@ import { analysers, getAnalyzerData } from 'superdough'; /** * Renders a spectrum analyzer for the incoming audio signal. * @name spectrum + * @tags visualization * @param {object} config optional config with options: * @param {integer} thickness line thickness in px (default 3) * @param {integer} speed scroll speed (default 1) From ee1a894f3ba2dc468fbf6510afc50c3a7387e2c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sun, 12 Oct 2025 16:28:05 +0200 Subject: [PATCH 008/124] Show tags in reference, more compact functions list --- .../src/repl/components/panel/Reference.jsx | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index 3faa5c6f1..d1157ba26 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -11,6 +11,7 @@ const availableFunctions = (() => { for (const doc of jsdocJson.docs) { if (!isValid(doc)) continue; if (seen.has(doc.name)) continue; + doc.tags = doc.tags?.filter((t) => t && typeof t === 'string') || []; functions.push(doc); const synonyms = doc.synonyms || []; seen.add(doc.name); @@ -113,7 +114,7 @@ export function Reference() { return (
-
+
@@ -134,19 +135,21 @@ export function Reference() { ))}
- @@ -161,8 +164,17 @@ export function Reference() { that you can already make music with a small set of functions!

{visibleFunctions.map((entry, i) => ( -
-

{entry.name}

+
+
+

+ {entry.name} +

+ {entry.tags && ( + + {entry.tags.join(', ')} + + )} +
{!!entry.synonyms_text && (

Synonyms: {entry.synonyms_text} From a4212589e5e0a2d3d6e9a31bf196f30e557b24e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sun, 12 Oct 2025 16:33:07 +0200 Subject: [PATCH 009/124] Recategorize --- packages/core/controls.mjs | 36 ++++++++++++++-------------- packages/core/pattern.mjs | 20 ++++++++-------- packages/core/signal.mjs | 48 +++++++++++++++++++------------------- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 4e60fc487..db689e1a2 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1621,7 +1621,7 @@ export const { fadeInTime } = registerControl('fadeInTime'); * Set frequency of sound. * * @name freq - * @tags transforms + * @tags temporal * @param {number | Pattern} frequency in Hz. the audible range is between 20 and 20000 Hz * @example * freq("220 110 440 110").s("superzow").osc() @@ -2205,7 +2205,7 @@ export const { cps } = registerControl('cps'); * Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration. * * @name clip - * @tags transforms + * @tags temporal * @synonyms legato * @param {number | Pattern} factor >= 0 * @example @@ -2218,7 +2218,7 @@ export const { clip, legato } = registerControl('clip', 'legato'); * Sets the duration of the event in cycles. Similar to clip / legato, it also cuts samples off at the end if they exceed the duration. * * @name duration - * @tags transforms + * @tags temporal * @synonyms dur * @param {number | Pattern} seconds >= 0 * @example @@ -2297,7 +2297,7 @@ export const ar = register('ar', (t, pat) => { * MIDI channel: Sets the MIDI channel for the event. * * @name midichan - * @tags examples + * @tags external_io * @param {number | Pattern} channel MIDI channel number (0-15) * @example * note("c4").midichan(1).midi() @@ -2310,7 +2310,7 @@ export const { midimap } = registerControl('midimap'); * MIDI port: Sets the MIDI port for the event. * * @name midiport - * @tags examples + * @tags external_io * @param {number | Pattern} port MIDI port * @example * note("c a f e").midiport("<0 1 2 3>").midi() @@ -2321,7 +2321,7 @@ export const { midiport } = registerControl('midiport'); * MIDI command: Sends a MIDI command message. * * @name midicmd - * @tags examples + * @tags external_io * @param {number | Pattern} command MIDI command * @example * midicmd("clock*48,/2").midi() @@ -2332,7 +2332,7 @@ export const { midicmd } = registerControl('midicmd'); * MIDI control: Sends a MIDI control change message. * * @name control - * @tags examples + * @tags external_io * @param {number | Pattern} MIDI control number (0-127) * @param {number | Pattern} MIDI controller value (0-127) */ @@ -2348,7 +2348,7 @@ export const control = register('control', (args, pat) => { * MIDI control number: Sends a MIDI control change message. * * @name ccn - * @tags examples + * @tags external_io * @param {number | Pattern} MIDI control number (0-127) */ export const { ccn } = registerControl('ccn'); @@ -2356,7 +2356,7 @@ export const { ccn } = registerControl('ccn'); * MIDI control value: Sends a MIDI control change message. * * @name ccv - * @tags examples + * @tags external_io * @param {number | Pattern} MIDI control value (0-127) */ export const { ccv } = registerControl('ccv'); @@ -2366,7 +2366,7 @@ export const { ctlNum } = registerControl('ctlNum'); /** * MIDI NRPN non-registered parameter number: Sends a MIDI NRPN non-registered parameter number message. * @name nrpnn - * @tags examples + * @tags external_io * @param {number | Pattern} nrpnn MIDI NRPN non-registered parameter number (0-127) * @example * note("c4").nrpnn("1:8").nrpv("123").midichan(1).midi() @@ -2375,7 +2375,7 @@ export const { nrpnn } = registerControl('nrpnn'); /** * MIDI NRPN non-registered parameter value: Sends a MIDI NRPN non-registered parameter value message. * @name nrpv - * @tags examples + * @tags external_io * @param {number | Pattern} nrpv MIDI NRPN non-registered parameter value (0-127) * @example * note("c4").nrpnn("1:8").nrpv("123").midichan(1).midi() @@ -2386,7 +2386,7 @@ export const { nrpv } = registerControl('nrpv'); * MIDI program number: Sends a MIDI program change message. * * @name progNum - * @tags examples + * @tags external_io * @param {number | Pattern} program MIDI program number (0-127) * @example * note("c4").progNum(10).midichan(1).midi() @@ -2396,7 +2396,7 @@ export const { progNum } = registerControl('progNum'); /** * MIDI sysex: Sends a MIDI sysex message. * @name sysex - * @tags examples + * @tags external_io * @param {number | Pattern} id Sysex ID * @param {number | Pattern} data Sysex data * @example @@ -2412,7 +2412,7 @@ export const sysex = register('sysex', (args, pat) => { /** * MIDI sysex ID: Sends a MIDI sysex identifier message. * @name sysexid - * @tags examples + * @tags external_io * @param {number | Pattern} id Sysex ID * @example * note("c4").sysexid("0x77").sysexdata("0x01:0x02:0x03:0x04").midichan(1).midi() @@ -2421,7 +2421,7 @@ export const { sysexid } = registerControl('sysexid'); /** * MIDI sysex data: Sends a MIDI sysex message. * @name sysexdata - * @tags examples + * @tags external_io * @param {number | Pattern} data Sysex data * @example * note("c4").sysexid("0x77").sysexdata("0x01:0x02:0x03:0x04").midichan(1).midi() @@ -2431,7 +2431,7 @@ export const { sysexdata } = registerControl('sysexdata'); /** * MIDI pitch bend: Sends a MIDI pitch bend message. * @name midibend - * @tags examples + * @tags external_io * @param {number | Pattern} midibend MIDI pitch bend (-1 - 1) * @example * note("c4").midibend(sine.slow(4).range(-0.4,0.4)).midi() @@ -2440,7 +2440,7 @@ export const { midibend } = registerControl('midibend'); /** * MIDI key after touch: Sends a MIDI key after touch message. * @name miditouch - * @tags examples + * @tags external_io * @param {number | Pattern} miditouch MIDI key after touch (0-1) * @example * note("c4").miditouch(sine.slow(4).range(0,1)).midi() @@ -2461,7 +2461,7 @@ export const getControlName = (alias) => { * Sets properties in a batch. * * @name as - * @tags transforms + * @tags temporal * @param {String | Array} mapping the control names that are set * @example * "c:.5 a:1 f:.25 e:.8".as("note:clip") diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 2d6459a89..4a0dbe58f 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -978,7 +978,7 @@ Pattern.prototype.collect = function () { /** * Selects indices in in stacked notes. - * @tags transforms + * @tags temporal * @example * note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>") * .arpWith(haps => haps[2]) @@ -993,7 +993,7 @@ export const arpWith = register('arpWith', (func, pat) => { /** * Selects indices in in stacked notes. - * @tags transforms + * @tags temporal * @example * note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>") * .arp("0 [0,2] 1 [0,2]") @@ -1342,7 +1342,7 @@ export function reify(thing) { /** * Takes a list of patterns, and returns a pattern of lists. * - * @tags transforms + * @tags temporal */ export function sequenceP(pats) { let result = pure([]); @@ -3354,7 +3354,7 @@ Pattern.prototype.steps = Pattern.prototype.pace; * Cuts each sample into the given number of parts, allowing you to explore a technique known as 'granular synthesis'. * It turns a pattern of samples into a pattern of parts of samples. * @name chop - * @tags transforms + * @tags temporal * @memberof Pattern * @returns Pattern * @example @@ -3385,7 +3385,7 @@ export const chop = register('chop', function (n, pat) { /** * Cuts each sample into the given number of parts, triggering progressive portions of each sample at each loop. * @name striate - * @tags transforms + * @tags temporal * @memberof Pattern * @returns Pattern * @example @@ -3404,7 +3404,7 @@ export const striate = register('striate', function (n, pat) { /** * Makes the sample fit the given number of cycles by changing the speed. * @name loopAt - * @tags transforms + * @tags temporal * @memberof Pattern * @returns Pattern * @example @@ -3423,7 +3423,7 @@ const _loopAt = function (factor, pat, cps = 0.5) { * Chops samples into the given number of slices, triggering those slices with a given pattern of slice numbers. * Instead of a number, it also accepts a list of numbers from 0 to 1 to slice at specific points. * @name slice - * @tags transforms + * @tags temporal * @memberof Pattern * @returns Pattern * @example @@ -3477,7 +3477,7 @@ Pattern.prototype.onTriggerTime = function (func) { /** * Works the same as slice, but changes the playback speed of each slice to match the duration of its step. * @name splice - * @tags transforms + * @tags temporal * @example * samples('github:tidalcycles/dirt-samples') * s("breaks165") @@ -3515,7 +3515,7 @@ export const { loopAt, loopat } = register(['loopAt', 'loopat'], function (facto * Makes the sample fit its event duration. Good for rhythmical loops like drum breaks. * Similar to `loopAt`. * @name fit - * @tags transforms + * @tags temporal * @example * samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' }) * s("rhodes/2").fit() @@ -3541,7 +3541,7 @@ export const fit = register('fit', (pat) => * given by a global clock and this function will be * deprecated/removed. * @name loopAtCps - * @tags transforms + * @tags temporal * @memberof Pattern * @returns Pattern * @example diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 2c3a63324..ce17fdeae 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -300,7 +300,7 @@ const _rearrangeWith = (ipat, n, pat) => { * Slices a pattern into the given number of parts, then plays those parts in random order. * Each part will be played exactly once per cycle. * @name shuffle - * @tags transforms + * @tags temporal * @example * note("c d e f").sound("piano").shuffle(4) * @example @@ -314,7 +314,7 @@ export const shuffle = register('shuffle', (n, pat) => { * Slices a pattern into the given number of parts, then plays those parts at random. Similar to `shuffle`, * but parts might be played more than once, or not at all, per cycle. * @name scramble - * @tags transforms + * @tags temporal * @example * note("c d e f").sound("piano").scramble(4) * @example @@ -393,7 +393,7 @@ export const __chooseWith = (pat, xs) => { /** * Choose from the list of values (or patterns of values) using the given * pattern of numbers, which should be in the range of 0..1 - * @tags transforms + * @tags temporal * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -407,7 +407,7 @@ export const chooseWith = (pat, xs) => { /** * As with {chooseWith}, but the structure comes from the chosen values, rather * than the pattern you're using to choose with. - * @tags transforms + * @tags temporal * @param {Pattern} pat * @param {*} xs * @returns {Pattern} @@ -418,7 +418,7 @@ export const chooseInWith = (pat, xs) => { /** * Chooses randomly from the given list of elements. - * @tags transforms + * @tags temporal * @param {...any} xs values / patterns to choose from. * @returns {Pattern} - a continuous pattern. * @example @@ -434,7 +434,7 @@ export const chooseOut = choose; * Chooses from the given list of values (or patterns of values), according * to the pattern that the method is called on. The pattern should be in * the range 0 .. 1. - * @tags transforms + * @tags temporal * @param {...any} xs * @returns {Pattern} */ @@ -445,7 +445,7 @@ Pattern.prototype.choose = function (...xs) { /** * As with choose, but the pattern that this method is called on should be * in the range -1 .. 1 - * @tags transforms + * @tags temporal * @param {...any} xs * @returns {Pattern} */ @@ -455,7 +455,7 @@ Pattern.prototype.choose2 = function (...xs) { /** * Picks one of the elements at random each cycle. - * @tags transforms + * @tags temporal * @synonyms randcat * @returns {Pattern} * @example @@ -498,7 +498,7 @@ const wchooseWith = (...args) => _wchooseWith(...args).outerJoin(); /** * Chooses randomly from the given list of elements by giving a probability to each element - * @tags transforms + * @tags temporal * @param {...any} pairs arrays of value and weight * @returns {Pattern} - a continuous pattern. * @example @@ -508,7 +508,7 @@ export const wchoose = (...pairs) => wchooseWith(rand, ...pairs); /** * Picks one of the elements at random each cycle by giving a probability to each element - * @tags transforms + * @tags temporal * @synonyms wrandcat * @returns {Pattern} * @example @@ -590,7 +590,7 @@ export const degradeByWith = register( * 0 = 0% chance of removal * 1 = 100% chance of removal * - * @tags transforms + * @tags temporal * @name degradeBy * @memberof Pattern * @param {number} amount - a number between 0 and 1 @@ -616,7 +616,7 @@ export const degradeBy = register( * * Randomly removes 50% of events from the pattern. Shorthand for `.degradeBy(0.5)` * - * @tags transforms + * @tags temporal * @name degrade * @memberof Pattern * @returns Pattern @@ -633,7 +633,7 @@ export const degrade = register('degrade', (pat) => pat._degradeBy(0.5), true, t * 1 = 0% chance of removal * Events that would be removed by degradeBy are let through by undegradeBy and vice versa (see second example). * - * @tags transforms + * @tags temporal * @name undegradeBy * @memberof Pattern * @param {number} amount - a number between 0 and 1 @@ -662,7 +662,7 @@ export const undegradeBy = register( * Inverse of `degrade`: Randomly removes 50% of events from the pattern. Shorthand for `.undegradeBy(0.5)` * Events that would be removed by degrade are let through by undegrade and vice versa (see second example). * - * @tags transforms + * @tags temporal * @name undegrade * @memberof Pattern * @returns Pattern @@ -681,7 +681,7 @@ export const undegrade = register('undegrade', (pat) => pat._undegradeBy(0.5), t * Randomly applies the given function by the given probability. * Similar to `someCyclesBy` * - * @tags transforms + * @tags temporal * @name sometimesBy * @memberof Pattern * @param {number | Pattern} probability - a number between 0 and 1 @@ -701,7 +701,7 @@ export const sometimesBy = register('sometimesBy', function (patx, func, pat) { * * Applies the given function with a 50% chance * - * @tags transforms + * @tags temporal * @name sometimes * @memberof Pattern * @param {function} function - the transformation to apply @@ -723,7 +723,7 @@ export const sometimes = register('sometimes', function (func, pat) { * @param {number | Pattern} probability - a number between 0 and 1 * @param {function} function - the transformation to apply * @returns Pattern - * @tags transforms + * @tags temporal * @example * s("bd,hh*8").someCyclesBy(.3, x=>x.speed("0.5")) */ @@ -746,7 +746,7 @@ export const someCyclesBy = register('someCyclesBy', function (patx, func, pat) * @name someCycles * @memberof Pattern * @returns Pattern - * @tags transforms + * @tags temporal * @example * s("bd,hh*8").someCycles(x=>x.speed("0.5")) */ @@ -761,7 +761,7 @@ export const someCycles = register('someCycles', function (func, pat) { * @name often * @memberof Pattern * @returns Pattern - * @tags transforms + * @tags temporal * @example * s("hh*8").often(x=>x.speed("0.5")) */ @@ -776,7 +776,7 @@ export const often = register('often', function (func, pat) { * @name rarely * @memberof Pattern * @returns Pattern - * @tags transforms + * @tags temporal * @example * s("hh*8").rarely(x=>x.speed("0.5")) */ @@ -788,7 +788,7 @@ export const rarely = register('rarely', function (func, pat) { * * Shorthand for `.sometimesBy(0.1, fn)` * - * @tags transforms + * @tags temporal * @name almostNever * @memberof Pattern * @returns Pattern @@ -803,7 +803,7 @@ export const almostNever = register('almostNever', function (func, pat) { * * Shorthand for `.sometimesBy(0.9, fn)` * - * @tags transforms + * @tags temporal * @name almostAlways * @memberof Pattern * @returns Pattern @@ -818,7 +818,7 @@ export const almostAlways = register('almostAlways', function (func, pat) { * * Shorthand for `.sometimesBy(0, fn)` (never calls fn) * - * @tags transforms + * @tags temporal * @name never * @memberof Pattern * @returns Pattern @@ -833,7 +833,7 @@ export const never = register('never', function (_, pat) { * * Shorthand for `.sometimesBy(1, fn)` (always calls fn) * - * @tags transforms + * @tags temporal * @name always * @memberof Pattern * @returns Pattern From b952e4c735a9f77592824e5542a6e3e93e9530aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sun, 19 Oct 2025 20:54:22 +0200 Subject: [PATCH 010/124] Add superdough tags --- packages/core/controls.mjs | 132 ++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index db689e1a2..2add9e416 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -351,7 +351,7 @@ export const { warpsync } = registerControl('warpsync'); * Define a custom webaudio node to use as a sound source. * * @name source - * @tags external_io + * @tags external_io, superdough * @synonyms src * @param {function} getSource * @synonyms src @@ -411,7 +411,7 @@ export const { accelerate } = registerControl('accelerate'); * Sets the velocity from 0 to 1. Is multiplied together with gain. * * @name velocity - * @tags effects + * @tags effects, superdough * @example * s("hh*8") * .gain(".4!2 1 .4!2 1 .4 1") @@ -422,7 +422,7 @@ export const { velocity } = registerControl('velocity'); * Controls the gain by an exponential amount. * * @name gain - * @tags effects + * @tags effects, superdough * @param {number | Pattern} amount gain. * @example * s("hh*8").gain(".4!2 1 .4!2 1 .4 1").fast(2) @@ -433,7 +433,7 @@ export const { gain } = registerControl('gain'); * Gain applied after all effects have been processed. * * @name postgain - * @tags effects + * @tags effects, superdough * @example * s("bd sd [~ bd] sd,hh*8") * .compressor("-20:20:10:.002:.02").postgain(1.5) @@ -581,7 +581,7 @@ export const { fmvelocity } = registerControl('fmvelocity'); * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`. * * @name bank - * @tags samples + * @tags samples, superdough * @param {string | Pattern} bank the name of the bank * @example * s("bd sd [~ bd] sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd") @@ -635,7 +635,7 @@ export const { sustain, sus } = registerControl('sustain', 'sus'); * Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero. * * @name release - * @tags effects + * @tags effects, superdough * @param {number | Pattern} time release time in seconds * @synonyms rel * @example @@ -650,7 +650,7 @@ export const { hold } = registerControl('hold'); * can also optionally supply the 'bpq' parameter separated by ':'. * * @name bpf - * @tags effects + * @tags effects, superdough * @param {number | Pattern} frequency center frequency * @synonyms bandf, bp * @example @@ -663,7 +663,7 @@ export const { bandf, bpf, bp } = registerControl(['bandf', 'bandq', 'bpenv'], ' * Sets the **b**and-**p**ass **q**-factor (resonance). * * @name bpq - * @tags effects + * @tags effects, superdough * @param {number | Pattern} q q factor * @synonyms bandq * @example @@ -743,7 +743,7 @@ export const { loopEnd, loope } = registerControl('loopEnd', 'loope'); * Bit crusher effect. * * @name crush - * @tags effects + * @tags effects, superdough * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). * @example * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") @@ -755,7 +755,7 @@ export const { crush } = registerControl('crush'); * Fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers * * @name coarse - * @tags effects + * @tags effects, superdough * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. * @example * s("bd sd [~ bd] sd,hh*8").coarse("<1 4 8 16 32>") @@ -767,7 +767,7 @@ export const { coarse } = registerControl('coarse'); * Modulate the amplitude of a sound with a continuous waveform * * @name tremolo - * @tags effects + * @tags effects, superdough * @synonyms trem * @param {number | Pattern} speed modulation speed in HZ * @example @@ -780,7 +780,7 @@ export const { tremolo } = registerControl(['tremolo', 'tremolodepth', 'tremolos * Modulate the amplitude of a sound with a continuous waveform * * @name tremolosync - * @tags effects + * @tags effects, superdough * @synonyms tremsync * @param {number | Pattern} cycles modulation speed in cycles * @example @@ -796,7 +796,7 @@ export const { tremolosync } = registerControl( * Depth of amplitude modulation * * @name tremolodepth - * @tags effects + * @tags effects, superdough * @synonyms tremdepth * @param {number | Pattern} depth * @example @@ -808,7 +808,7 @@ export const { tremolodepth } = registerControl('tremolodepth', 'tremdepth'); * Alter the shape of the modulation waveform * * @name tremoloskew - * @tags effects + * @tags effects, superdough * @synonyms tremskew * @param {number | Pattern} amount between 0 & 1, the shape of the waveform * @example @@ -821,7 +821,7 @@ export const { tremoloskew } = registerControl('tremoloskew', 'tremskew'); * Alter the phase of the modulation waveform * * @name tremolophase - * @tags effects + * @tags effects, superdough * @synonyms tremphase * @param {number | Pattern} offset the offset in cycles of the modulation * @example @@ -834,7 +834,7 @@ export const { tremolophase } = registerControl('tremolophase', 'tremphase'); * Shape of amplitude modulation * * @name tremoloshape - * @tags effects + * @tags effects, superdough * @synonyms tremshape * @param {number | Pattern} shape tri | square | sine | saw | ramp * @example @@ -846,7 +846,7 @@ export const { tremoloshape } = registerControl('tremoloshape', 'tremshape'); * Filter overdrive for supported filter types * * @name drive - * @tags effects + * @tags effects, superdough * @param {number | Pattern} amount * @example * note("{f g g c d a a#}%16".sub(17)).s("supersaw").lpenv(8).lpf(150).lpq(.8).ftype('ladder').drive("<.5 4>") @@ -860,7 +860,7 @@ export const { drive } = registerControl('drive'); * Can be applied to multiple orbits with the ':' mininotation, e.g. `duckorbit("2:3")` * * @name duckorbit - * @tags effects + * @tags effects, superdough * @synonyms duck * @param {number | Pattern} orbit target orbit * @example @@ -881,7 +881,7 @@ export const { duck } = registerControl('duckorbit', 'duck'); * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`. * * @name duckdepth - * @tags effects + * @tags effects, superdough * @param {number | Pattern} depth depth of modulation from 0 to 1 * @example * stack( n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2), s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack(0.2).duckdepth("<1 .9 .6 0>")) @@ -901,7 +901,7 @@ export const { duckdepth } = registerControl('duckdepth'); * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`. * * @name duckonset - * @tags effects + * @tags effects, superdough * @synonyms duckons * * @param {number | Pattern} time The onset time in seconds @@ -929,7 +929,7 @@ export const { duckonset } = registerControl('duckonset', 'duckons'); * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`. * * @name duckattack - * @tags effects + * @tags effects, superdough * @synonyms duckatt * * @param {number | Pattern} time The attack time in seconds @@ -1028,7 +1028,7 @@ export const { pwsweep } = registerControl('pwsweep'); * Phaser audio effect that approximates popular guitar pedals. * * @name phaser - * @tags effects + * @tags effects, superdough * @synonyms ph * @param {number | Pattern} speed speed of modulation * @example @@ -1046,7 +1046,7 @@ export const { phaserrate, ph, phaser } = registerControl( * The frequency sweep range of the lfo for the phaser effect. Defaults to 2000 * * @name phasersweep - * @tags effects + * @tags effects, superdough * @synonyms phs * @param {number | Pattern} phasersweep most useful values are between 0 and 4000 * @example @@ -1060,7 +1060,7 @@ export const { phasersweep, phs } = registerControl('phasersweep', 'phs'); * The center frequency of the phaser in HZ. Defaults to 1000 * * @name phasercenter - * @tags effects + * @tags effects, superdough * @synonyms phc * @param {number | Pattern} centerfrequency in HZ * @example @@ -1075,7 +1075,7 @@ export const { phasercenter, phc } = registerControl('phasercenter', 'phc'); * The amount the signal is affected by the phaser effect. Defaults to 0.75 * * @name phaserdepth - * @tags effects + * @tags effects, superdough * @synonyms phd, phasdp * @param {number | Pattern} depth number between 0 and 1 * @example @@ -1112,7 +1112,7 @@ export const { cut } = registerControl('cut'); * When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'. * * @name lpf - * @tags effects + * @tags effects, superdough * @param {number | Pattern} frequency audible between 0 and 20000 * @synonyms cutoff, ctf, lp * @example @@ -1126,7 +1126,7 @@ export const { cutoff, ctf, lpf, lp } = registerControl(['cutoff', 'resonance', /** * Sets the lowpass filter envelope modulation depth. * @name lpenv - * @tags effects + * @tags effects, superdough * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_ * @synonyms lpe * @example @@ -1140,7 +1140,7 @@ export const { lpenv, lpe } = registerControl('lpenv', 'lpe'); /** * Sets the highpass filter envelope modulation depth. * @name hpenv - * @tags effects + * @tags effects, superdough * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_ * @synonyms hpe * @example @@ -1154,7 +1154,7 @@ export const { hpenv, hpe } = registerControl('hpenv', 'hpe'); /** * Sets the bandpass filter envelope modulation depth. * @name bpenv - * @tags effects + * @tags effects, superdough * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_ * @synonyms bpe * @example @@ -1168,7 +1168,7 @@ export const { bpenv, bpe } = registerControl('bpenv', 'bpe'); /** * Sets the attack duration for the lowpass filter envelope. * @name lpattack - * @tags effects + * @tags effects, superdough * @param {number | Pattern} attack time of the filter envelope * @synonyms lpa * @example @@ -1182,7 +1182,7 @@ export const { lpattack, lpa } = registerControl('lpattack', 'lpa'); /** * Sets the attack duration for the highpass filter envelope. * @name hpattack - * @tags effects + * @tags effects, superdough * @param {number | Pattern} attack time of the highpass filter envelope * @synonyms hpa * @example @@ -1196,7 +1196,7 @@ export const { hpattack, hpa } = registerControl('hpattack', 'hpa'); /** * Sets the attack duration for the bandpass filter envelope. * @name bpattack - * @tags effects + * @tags effects, superdough * @param {number | Pattern} attack time of the bandpass filter envelope * @synonyms bpa * @example @@ -1210,7 +1210,7 @@ export const { bpattack, bpa } = registerControl('bpattack', 'bpa'); /** * Sets the decay duration for the lowpass filter envelope. * @name lpdecay - * @tags effects + * @tags effects, superdough * @param {number | Pattern} decay time of the filter envelope * @synonyms lpd * @example @@ -1224,7 +1224,7 @@ export const { lpdecay, lpd } = registerControl('lpdecay', 'lpd'); /** * Sets the decay duration for the highpass filter envelope. * @name hpdecay - * @tags effects + * @tags effects, superdough * @param {number | Pattern} decay time of the highpass filter envelope * @synonyms hpd * @example @@ -1239,7 +1239,7 @@ export const { hpdecay, hpd } = registerControl('hpdecay', 'hpd'); /** * Sets the decay duration for the bandpass filter envelope. * @name bpdecay - * @tags effects + * @tags effects, superdough * @param {number | Pattern} decay time of the bandpass filter envelope * @synonyms bpd * @example @@ -1254,7 +1254,7 @@ export const { bpdecay, bpd } = registerControl('bpdecay', 'bpd'); /** * Sets the sustain amplitude for the lowpass filter envelope. * @name lpsustain - * @tags effects + * @tags effects, superdough * @param {number | Pattern} sustain amplitude of the lowpass filter envelope * @synonyms lps * @example @@ -1269,7 +1269,7 @@ export const { lpsustain, lps } = registerControl('lpsustain', 'lps'); /** * Sets the sustain amplitude for the highpass filter envelope. * @name hpsustain - * @tags effects + * @tags effects, superdough * @param {number | Pattern} sustain amplitude of the highpass filter envelope * @synonyms hps * @example @@ -1284,7 +1284,7 @@ export const { hpsustain, hps } = registerControl('hpsustain', 'hps'); /** * Sets the sustain amplitude for the bandpass filter envelope. * @name bpsustain - * @tags effects + * @tags effects, superdough * @param {number | Pattern} sustain amplitude of the bandpass filter envelope * @synonyms bps * @example @@ -1299,7 +1299,7 @@ export const { bpsustain, bps } = registerControl('bpsustain', 'bps'); /** * Sets the release time for the lowpass filter envelope. * @name lprelease - * @tags effects + * @tags effects, superdough * @param {number | Pattern} release time of the filter envelope * @synonyms lpr * @example @@ -1315,7 +1315,7 @@ export const { lprelease, lpr } = registerControl('lprelease', 'lpr'); /** * Sets the release time for the highpass filter envelope. * @name hprelease - * @tags effects + * @tags effects, superdough * @param {number | Pattern} release time of the highpass filter envelope * @synonyms hpr * @example @@ -1331,7 +1331,7 @@ export const { hprelease, hpr } = registerControl('hprelease', 'hpr'); /** * Sets the release time for the bandpass filter envelope. * @name bprelease - * @tags effects + * @tags effects, superdough * @param {number | Pattern} release time of the bandpass filter envelope * @synonyms bpr * @example @@ -1363,7 +1363,7 @@ export const { ftype } = registerControl('ftype'); /** * controls the center of the filter envelope. 0 is unipolar positive, .5 is bipolar, 1 is unipolar negative * @name fanchor - * @tags effects + * @tags effects, superdough * @param {number | Pattern} center 0 to 1 * @example * note("{f g g c d a a#}%8").s("sawtooth").lpf("{1000}%2") @@ -1376,7 +1376,7 @@ export const { fanchor } = registerControl('fanchor'); * When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'. * * @name hpf - * @tags effects + * @tags effects, superdough * @param {number | Pattern} frequency audible between 0 and 20000 * @synonyms hp, hcutoff * @example @@ -1438,7 +1438,7 @@ export const { hcutoff, hpf, hp } = registerControl(['hcutoff', 'hresonance', 'h * Controls the **h**igh-**p**ass **q**-value. * * @name hpq - * @tags effects + * @tags effects, superdough * @param {number | Pattern} q resonance factor between 0 and 50 * @synonyms hresonance * @example @@ -1450,7 +1450,7 @@ export const { hresonance, hpq } = registerControl('hresonance', 'hpq'); * Controls the **l**ow-**p**ass **q**-value. * * @name lpq - * @tags effects + * @tags effects, superdough * @param {number | Pattern} q resonance factor between 0 and 50 * @synonyms resonance * @example @@ -1463,7 +1463,7 @@ export const { resonance, lpq } = registerControl('resonance', 'lpq'); * DJ filter, below 0.5 is low pass filter, above is high pass filter. * * @name djf - * @tags effects + * @tags effects, superdough * @param {number | Pattern} cutoff below 0.5 is low pass filter, above is high pass filter * @example * n(irand(16).seg(8)).scale("d:phrygian").s("supersaw").djf("<.5 .3 .2 .75>") @@ -1480,7 +1480,7 @@ export const { djf } = registerControl('djf'); * * * @name delay - * @tags effects + * @tags effects, superdough * @param {number | Pattern} level between 0 and 1 * @example * s("bd bd").delay("<0 .25 .5 1>") @@ -1494,7 +1494,7 @@ export const { delay } = registerControl(['delay', 'delaytime', 'delayfeedback'] * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it * * @name delayfeedback - * @tags effects + * @tags effects, superdough * @param {number | Pattern} feedback between 0 and 1 * @synonyms delayfb, dfb * @example @@ -1508,7 +1508,7 @@ export const { delayfeedback, delayfb, dfb } = registerControl('delayfeedback', * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it * * @name delayfeedback - * @tags effects + * @tags effects, superdough * @param {number | Pattern} feedback between 0 and 1 * @synonyms delayfb, dfb * @example @@ -1533,7 +1533,7 @@ export const { delaytime, delayt, dt } = registerControl('delaytime', 'delayt', * Sets the time of the delay effect in cycles. * * @name delaysync - * @tags effects + * @tags effects, superdough * @param {number | Pattern} cycles delay length in cycles * @synonyms delayt, dt * @example @@ -1595,7 +1595,7 @@ export const { spread } = registerControl('spread'); * Set dryness of reverb. See `room` and `size` for more information about reverb. * * @name dry - * @tags effects + * @tags effects, superdough * @param {number | Pattern} dry 0 = wet, 1 = dry * @example * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() @@ -1814,7 +1814,7 @@ export const { octave } = registerControl('octave'); * An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share the same global effects. * * @name orbit - * @tags effects + * @tags effects, superdough * @param {number | Pattern} number * @example * stack( @@ -1831,7 +1831,7 @@ export const { overshape } = registerControl('overshape'); * Sets position in stereo. * * @name pan - * @tags effects + * @tags effects, superdough * @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel) * @example * s("[bd hh]*2").pan("<.5 1 .5 0>") @@ -1897,7 +1897,7 @@ export const { mode } = registerControl(['mode', 'anchor']); * When using mininotation, you can also optionally add the 'size' parameter, separated by ':'. * * @name room - * @tags effects + * @tags effects, superdough * @param {number | Pattern} level between 0 and 1 * @example * s("bd sd [~ bd] sd").room("<0 .2 .4 .6 .8 1>") @@ -1911,7 +1911,7 @@ export const { room } = registerControl(['room', 'size']); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomlp - * @tags effects + * @tags effects, superdough * @synonyms rlp * @param {number} frequency between 0 and 20000hz * @example @@ -1925,7 +1925,7 @@ export const { roomlp, rlp } = registerControl('roomlp', 'rlp'); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomdim - * @tags effects + * @tags effects, superdough * @synonyms rdim * @param {number} frequency between 0 and 20000hz * @example @@ -1940,7 +1940,7 @@ export const { roomdim, rdim } = registerControl('roomdim', 'rdim'); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomfade - * @tags effects + * @tags effects, superdough * @synonyms rfade * @param {number} seconds for the reverb to fade * @example @@ -1953,7 +1953,7 @@ export const { roomfade, rfade } = registerControl('roomfade', 'rfade'); /** * Sets the sample to use as an impulse response for the reverb. * @name iresponse - * @tags effects + * @tags effects, superdough * @param {string | Pattern} sample to use as an impulse response * @synonyms ir * @example @@ -1965,7 +1965,7 @@ export const { ir, iresponse } = registerControl(['ir', 'i'], 'iresponse'); /** * Sets speed of the sample for the impulse response. * @name irspeed - * @tags effects + * @tags effects, superdough * @param {string | Pattern} speed * @example * samples('github:switchangel/pad') @@ -1977,7 +1977,7 @@ export const { irspeed } = registerControl('irspeed'); /** * Sets the beginning of the IR response sample * @name irbegin - * @tags effects + * @tags effects, superdough * @param {string | Pattern} begin between 0 and 1 * @synonyms ir * @example @@ -1991,7 +1991,7 @@ export const { irbegin } = registerControl('irbegin'); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomsize - * @tags effects + * @tags effects, superdough * @param {number | Pattern} size between 0 and 10 * @synonyms rsize, sz, size * @example @@ -2015,7 +2015,7 @@ export const { roomsize, size, sz, rsize } = registerControl('roomsize', 'size', * * * @name shape - * @tags effects + * @tags effects, superdough * @param {number | Pattern} distortion between 0 and 1 * @example * s("bd sd [~ bd] sd,hh*8").shape("<0 .2 .4 .6 .8>") @@ -2028,7 +2028,7 @@ export const { shape } = registerControl(['shape', 'shapevol']); * Most useful values are usually between 0 and 10 (depending on source gain). If you are feeling adventurous, you can turn it up to 11 and beyond ;) * * @name distort - * @tags effects + * @tags effects, superdough * @synonyms dist * @param {number | Pattern} distortion * @example @@ -2043,7 +2043,7 @@ export const { distort, dist } = registerControl(['distort', 'distortvol'], 'dis * More info [here](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode?retiredLocale=de#instance_properties) * * @name compressor - * @tags effects + * @tags effects, superdough * @example * s("bd sd [~ bd] sd,hh*8") * .compressor("-20:20:10:.002:.02") @@ -2129,7 +2129,7 @@ export const { squiz } = registerControl('squiz'); * Formant filter to make things sound like vowels. * * @name vowel - * @tags effects + * @tags effects, superdough * @param {string | Pattern} vowel You can use a e i o u ae aa oe ue y uh un en an on, corresponding to [a] [e] [i] [o] [u] [æ] [ɑ] [ø] [y] [ɯ] [ʌ] [œ̃] [ɛ̃] [ɑ̃] [ɔ̃]. Aliases: aa = å = ɑ, oe = ø = ö, y = ı, ae = æ. * @example * note("[c2 >]*2").s('sawtooth') @@ -2154,7 +2154,7 @@ export const { waveloss } = registerControl('waveloss'); * Noise crackle density * * @name density - * @tags effects + * @tags effects, superdough * @param {number | Pattern} density between 0 and x * @example * s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(4)) From 96d0f2053e7fc0afbf515668945f3a4f3346fca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sun, 19 Oct 2025 21:13:14 +0200 Subject: [PATCH 011/124] Add supradough controls --- packages/core/controls.mjs | 96 +++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 2add9e416..dda12767d 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -411,7 +411,7 @@ export const { accelerate } = registerControl('accelerate'); * Sets the velocity from 0 to 1. Is multiplied together with gain. * * @name velocity - * @tags effects, superdough + * @tags effects, superdough, supradough * @example * s("hh*8") * .gain(".4!2 1 .4!2 1 .4 1") @@ -422,7 +422,7 @@ export const { velocity } = registerControl('velocity'); * Controls the gain by an exponential amount. * * @name gain - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} amount gain. * @example * s("hh*8").gain(".4!2 1 .4!2 1 .4 1").fast(2) @@ -433,7 +433,7 @@ export const { gain } = registerControl('gain'); * Gain applied after all effects have been processed. * * @name postgain - * @tags effects, superdough + * @tags effects, superdough, supradough * @example * s("bd sd [~ bd] sd,hh*8") * .compressor("-20:20:10:.002:.02").postgain(1.5) @@ -456,7 +456,7 @@ export const { amp } = registerControl('amp'); * Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset. * * @name attack - * @tags effects + * @tags effects, superdough, supradough * @param {number | Pattern} attack time in seconds. * @synonyms att * @example @@ -472,7 +472,7 @@ export const { attack, att } = registerControl('attack', 'att'); * while decimal numbers and complex ratios sound metallic. * * @name fmh - * @tags effects + * @tags effects, superdough, supradough * @param {number | Pattern} harmonicity * @example * note("c e g b g e") @@ -487,7 +487,7 @@ export const { fmh } = registerControl(['fmh', 'fmi'], 'fmh'); * Controls the modulation index, which defines the brightness of the sound. * * @name fm - * @tags effects + * @tags effects, superdough, supradough * @param {number | Pattern} brightness modulation index * @synonyms fmi * @example @@ -502,7 +502,7 @@ export const { fmi, fm } = registerControl(['fmi', 'fmh'], 'fm'); * Ramp type of fm envelope. Exp might be a bit broken.. * * @name fmenv - * @tags effects + * @tags effects, superdough, supradough * @param {number | Pattern} type lin | exp * @example * note("c e g b g e") @@ -518,7 +518,7 @@ export const { fmenv } = registerControl('fmenv'); * Attack time for the FM envelope: time it takes to reach maximum modulation * * @name fmattack - * @tags effects + * @tags effects, superdough, supradough * @param {number | Pattern} time attack time * @example * note("c e g b g e") @@ -533,7 +533,7 @@ export const { fmattack } = registerControl('fmattack'); * Waveform of the fm modulator * * @name fmwave - * @tags effects + * @tags effects, superdough, supradough * @param {number | Pattern} wave waveform * @example * n("0 1 2 3".fast(4)).scale("d:minor").s("sine").fmwave("").fm(4).fmh(2.01) @@ -547,7 +547,7 @@ export const { fmwave } = registerControl('fmwave'); * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase. * * @name fmdecay - * @tags effects + * @tags effects, superdough, supradough * @param {number | Pattern} time decay time * @example * note("c e g b g e") @@ -562,7 +562,7 @@ export const { fmdecay } = registerControl('fmdecay'); * Sustain level for the FM envelope: how much modulation is applied after the decay phase * * @name fmsustain - * @tags effects + * @tags effects, superdough, supradough * @param {number | Pattern} level sustain level * @example * note("c e g b g e") @@ -611,7 +611,7 @@ export const { fft } = registerControl('fft'); * Note that the decay is only audible if the sustain value is lower than 1. * * @name decay - * @tags effects + * @tags effects, superdough, supradough * @param {number | Pattern} time decay time in seconds * @synonyms dec * @example @@ -623,7 +623,7 @@ export const { decay, dec } = registerControl('decay', 'dec'); * Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset. * * @name sustain - * @tags effects + * @tags effects, superdough, supradough * @param {number | Pattern} gain sustain level between 0 and 1 * @synonyms sus * @example @@ -635,7 +635,7 @@ export const { sustain, sus } = registerControl('sustain', 'sus'); * Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero. * * @name release - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} time release time in seconds * @synonyms rel * @example @@ -650,7 +650,7 @@ export const { hold } = registerControl('hold'); * can also optionally supply the 'bpq' parameter separated by ':'. * * @name bpf - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} frequency center frequency * @synonyms bandf, bp * @example @@ -663,7 +663,7 @@ export const { bandf, bpf, bp } = registerControl(['bandf', 'bandq', 'bpenv'], ' * Sets the **b**and-**p**ass **q**-factor (resonance). * * @name bpq - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} q q factor * @synonyms bandq * @example @@ -743,7 +743,7 @@ export const { loopEnd, loope } = registerControl('loopEnd', 'loope'); * Bit crusher effect. * * @name crush - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). * @example * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") @@ -755,7 +755,7 @@ export const { crush } = registerControl('crush'); * Fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers * * @name coarse - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. * @example * s("bd sd [~ bd] sd,hh*8").coarse("<1 4 8 16 32>") @@ -1112,7 +1112,7 @@ export const { cut } = registerControl('cut'); * When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'. * * @name lpf - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} frequency audible between 0 and 20000 * @synonyms cutoff, ctf, lp * @example @@ -1126,7 +1126,7 @@ export const { cutoff, ctf, lpf, lp } = registerControl(['cutoff', 'resonance', /** * Sets the lowpass filter envelope modulation depth. * @name lpenv - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_ * @synonyms lpe * @example @@ -1140,7 +1140,7 @@ export const { lpenv, lpe } = registerControl('lpenv', 'lpe'); /** * Sets the highpass filter envelope modulation depth. * @name hpenv - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_ * @synonyms hpe * @example @@ -1154,7 +1154,7 @@ export const { hpenv, hpe } = registerControl('hpenv', 'hpe'); /** * Sets the bandpass filter envelope modulation depth. * @name bpenv - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_ * @synonyms bpe * @example @@ -1168,7 +1168,7 @@ export const { bpenv, bpe } = registerControl('bpenv', 'bpe'); /** * Sets the attack duration for the lowpass filter envelope. * @name lpattack - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} attack time of the filter envelope * @synonyms lpa * @example @@ -1182,7 +1182,7 @@ export const { lpattack, lpa } = registerControl('lpattack', 'lpa'); /** * Sets the attack duration for the highpass filter envelope. * @name hpattack - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} attack time of the highpass filter envelope * @synonyms hpa * @example @@ -1196,7 +1196,7 @@ export const { hpattack, hpa } = registerControl('hpattack', 'hpa'); /** * Sets the attack duration for the bandpass filter envelope. * @name bpattack - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} attack time of the bandpass filter envelope * @synonyms bpa * @example @@ -1210,7 +1210,7 @@ export const { bpattack, bpa } = registerControl('bpattack', 'bpa'); /** * Sets the decay duration for the lowpass filter envelope. * @name lpdecay - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} decay time of the filter envelope * @synonyms lpd * @example @@ -1224,7 +1224,7 @@ export const { lpdecay, lpd } = registerControl('lpdecay', 'lpd'); /** * Sets the decay duration for the highpass filter envelope. * @name hpdecay - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} decay time of the highpass filter envelope * @synonyms hpd * @example @@ -1239,7 +1239,7 @@ export const { hpdecay, hpd } = registerControl('hpdecay', 'hpd'); /** * Sets the decay duration for the bandpass filter envelope. * @name bpdecay - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} decay time of the bandpass filter envelope * @synonyms bpd * @example @@ -1254,7 +1254,7 @@ export const { bpdecay, bpd } = registerControl('bpdecay', 'bpd'); /** * Sets the sustain amplitude for the lowpass filter envelope. * @name lpsustain - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} sustain amplitude of the lowpass filter envelope * @synonyms lps * @example @@ -1269,7 +1269,7 @@ export const { lpsustain, lps } = registerControl('lpsustain', 'lps'); /** * Sets the sustain amplitude for the highpass filter envelope. * @name hpsustain - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} sustain amplitude of the highpass filter envelope * @synonyms hps * @example @@ -1284,7 +1284,7 @@ export const { hpsustain, hps } = registerControl('hpsustain', 'hps'); /** * Sets the sustain amplitude for the bandpass filter envelope. * @name bpsustain - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} sustain amplitude of the bandpass filter envelope * @synonyms bps * @example @@ -1299,7 +1299,7 @@ export const { bpsustain, bps } = registerControl('bpsustain', 'bps'); /** * Sets the release time for the lowpass filter envelope. * @name lprelease - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} release time of the filter envelope * @synonyms lpr * @example @@ -1315,7 +1315,7 @@ export const { lprelease, lpr } = registerControl('lprelease', 'lpr'); /** * Sets the release time for the highpass filter envelope. * @name hprelease - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} release time of the highpass filter envelope * @synonyms hpr * @example @@ -1331,7 +1331,7 @@ export const { hprelease, hpr } = registerControl('hprelease', 'hpr'); /** * Sets the release time for the bandpass filter envelope. * @name bprelease - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} release time of the bandpass filter envelope * @synonyms bpr * @example @@ -1376,7 +1376,7 @@ export const { fanchor } = registerControl('fanchor'); * When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'. * * @name hpf - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} frequency audible between 0 and 20000 * @synonyms hp, hcutoff * @example @@ -1391,7 +1391,7 @@ export const { fanchor } = registerControl('fanchor'); * Applies a vibrato to the frequency of the oscillator. * * @name vib - * @tags effects + * @tags effects, superdough, supradough * @synonyms vibrato, v * @param {number | Pattern} frequency of the vibrato in hertz * @example @@ -1419,7 +1419,7 @@ export const { noise } = registerControl('noise'); * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set * * @name vibmod - * @tags effects + * @tags effects, superdough, supradough * @synonyms vmod * @param {number | Pattern} depth of vibrato (in semitones) * @example @@ -1438,7 +1438,7 @@ export const { hcutoff, hpf, hp } = registerControl(['hcutoff', 'hresonance', 'h * Controls the **h**igh-**p**ass **q**-value. * * @name hpq - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} q resonance factor between 0 and 50 * @synonyms hresonance * @example @@ -1450,7 +1450,7 @@ export const { hresonance, hpq } = registerControl('hresonance', 'hpq'); * Controls the **l**ow-**p**ass **q**-value. * * @name lpq - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} q resonance factor between 0 and 50 * @synonyms resonance * @example @@ -1480,7 +1480,7 @@ export const { djf } = registerControl('djf'); * * * @name delay - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} level between 0 and 1 * @example * s("bd bd").delay("<0 .25 .5 1>") @@ -1494,7 +1494,7 @@ export const { delay } = registerControl(['delay', 'delaytime', 'delayfeedback'] * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it * * @name delayfeedback - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} feedback between 0 and 1 * @synonyms delayfb, dfb * @example @@ -1508,7 +1508,7 @@ export const { delayfeedback, delayfb, dfb } = registerControl('delayfeedback', * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it * * @name delayfeedback - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} feedback between 0 and 1 * @synonyms delayfb, dfb * @example @@ -1635,7 +1635,7 @@ export const { freq } = registerControl('freq'); * Attack time of pitch envelope. * * @name pattack - * @tags effects + * @tags effects, superdough, supradough * @synonyms patt * @param {number | Pattern} time time in seconds * @example @@ -1647,7 +1647,7 @@ export const { pattack, patt } = registerControl('pattack', 'patt'); * Decay time of pitch envelope. * * @name pdecay - * @tags effects + * @tags effects, superdough, supradough * @synonyms pdec * @param {number | Pattern} time time in seconds * @example @@ -1661,7 +1661,7 @@ export const { psustain, psus } = registerControl('psustain', 'psus'); * Release time of pitch envelope * * @name prelease - * @tags effects + * @tags effects, superdough, supradough * @synonyms prel * @param {number | Pattern} time time in seconds * @example @@ -1676,7 +1676,7 @@ export const { prelease, prel } = registerControl('prelease', 'prel'); * If you don't set other pitch envelope controls, `pattack:.2` will be the default. * * @name penv - * @tags effects + * @tags effects, superdough, supradough * @param {number | Pattern} semitones change in semitones * @example * note("c") @@ -1831,7 +1831,7 @@ export const { overshape } = registerControl('overshape'); * Sets position in stereo. * * @name pan - * @tags effects, superdough + * @tags effects, superdough, supradough * @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel) * @example * s("[bd hh]*2").pan("<.5 1 .5 0>") @@ -2028,7 +2028,7 @@ export const { shape } = registerControl(['shape', 'shapevol']); * Most useful values are usually between 0 and 10 (depending on source gain). If you are feeling adventurous, you can turn it up to 11 and beyond ;) * * @name distort - * @tags effects, superdough + * @tags effects, superdough, supradough * @synonyms dist * @param {number | Pattern} distortion * @example From 69360b3ec8731e75045f44b9f08840739fbc7e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sun, 19 Oct 2025 21:17:25 +0200 Subject: [PATCH 012/124] Also tag "noise" --- packages/core/controls.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index dda12767d..c6ff8d79b 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1409,7 +1409,7 @@ export const { vib, vibrato, v } = registerControl(['vib', 'vibmod'], 'vibrato', * Adds pink noise to the mix * * @name noise - * @tags generators + * @tags generators, superdough, supradough * @param {number | Pattern} wet wet amount * @example * sound("/2") From 2483c673a4ae08ac33fc60eb2155fdcd4ba091cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sun, 19 Oct 2025 21:19:42 +0200 Subject: [PATCH 013/124] Effects -> fx, untag "crossfade" --- packages/core/controls.mjs | 262 +++++++++++++++--------------- packages/superdough/wavetable.mjs | 2 +- packages/supradough/dough.mjs | 2 +- 3 files changed, 133 insertions(+), 133 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index c6ff8d79b..1bfd5a892 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -92,7 +92,7 @@ export const { s, sound } = registerControl(['s', 'n', 'gain'], 'sound'); * Position in the wavetable of the wavetable oscillator * * @name wt - * @tags effects + * @tags fx * @param {number | Pattern} position Position in the wavetable from 0 to 1 * @synonyms wavetablePosition * @example @@ -104,7 +104,7 @@ export const { wt, wavetablePosition } = registerControl('wt', 'wavetablePositio * Amount of envelope applied wavetable oscillator's position envelope * * @name wtenv - * @tags effects + * @tags fx * @param {number | Pattern} amount between 0 and 1 */ export const { wtenv } = registerControl('wtenv'); @@ -112,7 +112,7 @@ export const { wtenv } = registerControl('wtenv'); * Attack time of the wavetable oscillator's position envelope * * @name wtattack - * @tags effects + * @tags fx * @synonyms wtatt * @param {number | Pattern} time attack time in seconds */ @@ -122,7 +122,7 @@ export const { wtattack, wtatt } = registerControl('wtattack', 'wtatt'); * Decay time of the wavetable oscillator's position envelope * * @name wtdecay - * @tags effects + * @tags fx * @synonyms wtdec * @param {number | Pattern} time decay time in seconds */ @@ -132,7 +132,7 @@ export const { wtdecay, wtdec } = registerControl('wtdecay', 'wtdec'); * Sustain time of the wavetable oscillator's position envelope * * @name wtsustain - * @tags effects + * @tags fx * @synonyms wtsus * @param {number | Pattern} gain sustain level (0 to 1) */ @@ -142,7 +142,7 @@ export const { wtsustain, wtsus } = registerControl('wtsustain', 'wtsus'); * Release time of the wavetable oscillator's position envelope * * @name wtrelease - * @tags effects + * @tags fx * @synonyms wtrel * @param {number | Pattern} time release time in seconds */ @@ -152,7 +152,7 @@ export const { wtrelease, wtrel } = registerControl('wtrelease', 'wtrel'); * Rate of the LFO for the wavetable oscillator's position * * @name wtrate - * @tags effects + * @tags fx * @param {number | Pattern} rate rate in hertz */ export const { wtrate } = registerControl('wtrate'); @@ -160,7 +160,7 @@ export const { wtrate } = registerControl('wtrate'); * cycle synced rate of the LFO for the wavetable oscillator's position * * @name wtsync - * @tags effects + * @tags fx * @param {number | Pattern} rate rate in cycles */ export const { wtsync } = registerControl('wtsync'); @@ -169,7 +169,7 @@ export const { wtsync } = registerControl('wtsync'); * Depth of the LFO for the wavetable oscillator's position * * @name wtdepth - * @tags effects + * @tags fx * @param {number | Pattern} depth depth of modulation */ export const { wtdepth } = registerControl('wtdepth'); @@ -178,7 +178,7 @@ export const { wtdepth } = registerControl('wtdepth'); * Shape of the LFO for the wavetable oscillator's position * * @name wtshape - * @tags effects + * @tags fx * @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..) */ export const { wtshape } = registerControl('wtshape'); @@ -187,7 +187,7 @@ export const { wtshape } = registerControl('wtshape'); * DC offset of the LFO for the wavetable oscillator's position * * @name wtdc - * @tags effects + * @tags fx * @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar */ export const { wtdc } = registerControl('wtdc'); @@ -196,7 +196,7 @@ export const { wtdc } = registerControl('wtdc'); * Skew of the LFO for the wavetable oscillator's position * * @name wtskew - * @tags effects + * @tags fx * @param {number | Pattern} skew How much to bend the LFO shape */ export const { wtskew } = registerControl('wtskew'); @@ -205,7 +205,7 @@ export const { wtskew } = registerControl('wtskew'); * Amount of warp (alteration of the waveform) to apply to the wavetable oscillator * * @name warp - * @tags effects + * @tags fx * @param {number | Pattern} amount Warp of the wavetable from 0 to 1 * @synonyms wavetableWarp * @example @@ -218,7 +218,7 @@ export const { warp, wavetableWarp } = registerControl('warp', 'wavetableWarp'); * Attack time of the wavetable oscillator's warp envelope * * @name warpattack - * @tags effects + * @tags fx * @synonyms warpatt * @param {number | Pattern} time attack time in seconds */ @@ -228,7 +228,7 @@ export const { warpattack, warpatt } = registerControl('warpattack', 'warpatt'); * Decay time of the wavetable oscillator's warp envelope * * @name warpdecay - * @tags effects + * @tags fx * @synonyms warpdec * @param {number | Pattern} time decay time in seconds */ @@ -238,7 +238,7 @@ export const { warpdecay, warpdec } = registerControl('warpdecay', 'warpdec'); * Sustain time of the wavetable oscillator's warp envelope * * @name warpsustain - * @tags effects + * @tags fx * @synonyms warpsus * @param {number | Pattern} gain sustain level (0 to 1) */ @@ -248,7 +248,7 @@ export const { warpsustain, warpsus } = registerControl('warpsustain', 'warpsus' * Release time of the wavetable oscillator's warp envelope * * @name warprelease - * @tags effects + * @tags fx * @synonyms warprel * @param {number | Pattern} time release time in seconds */ @@ -258,7 +258,7 @@ export const { warprelease, warprel } = registerControl('warprelease', 'warprel' * Rate of the LFO for the wavetable oscillator's warp * * @name warprate - * @tags effects + * @tags fx * @param {number | Pattern} rate rate in hertz */ export const { warprate } = registerControl('warprate'); @@ -267,7 +267,7 @@ export const { warprate } = registerControl('warprate'); * Depth of the LFO for the wavetable oscillator's warp * * @name warpdepth - * @tags effects + * @tags fx * @param {number | Pattern} depth depth of modulation */ export const { warpdepth } = registerControl('warpdepth'); @@ -276,7 +276,7 @@ export const { warpdepth } = registerControl('warpdepth'); * Shape of the LFO for the wavetable oscillator's warp * * @name warpshape - * @tags effects + * @tags fx * @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..) */ export const { warpshape } = registerControl('warpshape'); @@ -285,7 +285,7 @@ export const { warpshape } = registerControl('warpshape'); * DC offset of the LFO for the wavetable oscillator's warp * * @name warpdc - * @tags effects + * @tags fx * @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar */ export const { warpdc } = registerControl('warpdc'); @@ -294,7 +294,7 @@ export const { warpdc } = registerControl('warpdc'); * Skew of the LFO for the wavetable oscillator's warp * * @name warpskew - * @tags effects + * @tags fx * @param {number | Pattern} skew How much to bend the LFO shape */ export const { warpskew } = registerControl('warpskew'); @@ -306,7 +306,7 @@ export const { warpskew } = registerControl('warpskew'); * spin, chaos, primes, binary, brownian, reciprocal, wormhole, logistic, sigmoid, fractal, flip * * @name warpmode - * @tags effects + * @tags fx * @param {number | string | Pattern} mode Warp mode * @synonyms wavetableWarpMode * @example @@ -320,7 +320,7 @@ export const { warpmode, wavetableWarpMode } = registerControl('warpmode', 'wave * Amount of randomness of the initial phase of the wavetable oscillator. * * @name wtphaserand - * @tags effects + * @tags fx * @param {number | Pattern} amount Randomness of the initial phase. Between 0 (not random) and 1 (fully random) * @synonyms wavetablePhaseRand * @example @@ -333,7 +333,7 @@ export const { wtphaserand, wavetablePhaseRand } = registerControl('wtphaserand' * Amount of envelope applied wavetable oscillator's position envelope * * @name warpenv - * @tags effects + * @tags fx * @param {number | Pattern} amount between 0 and 1 */ export const { warpenv } = registerControl('warpenv'); @@ -342,7 +342,7 @@ export const { warpenv } = registerControl('warpenv'); * cycle synced rate of the LFO for the wavetable warp position * * @name warpsync - * @tags effects + * @tags fx * @param {number | Pattern} rate rate in cycles */ export const { warpsync } = registerControl('warpsync'); @@ -411,7 +411,7 @@ export const { accelerate } = registerControl('accelerate'); * Sets the velocity from 0 to 1. Is multiplied together with gain. * * @name velocity - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @example * s("hh*8") * .gain(".4!2 1 .4!2 1 .4 1") @@ -422,7 +422,7 @@ export const { velocity } = registerControl('velocity'); * Controls the gain by an exponential amount. * * @name gain - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} amount gain. * @example * s("hh*8").gain(".4!2 1 .4!2 1 .4 1").fast(2) @@ -433,7 +433,7 @@ export const { gain } = registerControl('gain'); * Gain applied after all effects have been processed. * * @name postgain - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @example * s("bd sd [~ bd] sd,hh*8") * .compressor("-20:20:10:.002:.02").postgain(1.5) @@ -444,7 +444,7 @@ export const { postgain } = registerControl('postgain'); * Like `gain`, but linear. * * @name amp - * @tags effects + * @tags fx * @param {number | Pattern} amount gain. * @superdirtOnly * @example @@ -456,7 +456,7 @@ export const { amp } = registerControl('amp'); * Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset. * * @name attack - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} attack time in seconds. * @synonyms att * @example @@ -472,7 +472,7 @@ export const { attack, att } = registerControl('attack', 'att'); * while decimal numbers and complex ratios sound metallic. * * @name fmh - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} harmonicity * @example * note("c e g b g e") @@ -487,7 +487,7 @@ export const { fmh } = registerControl(['fmh', 'fmi'], 'fmh'); * Controls the modulation index, which defines the brightness of the sound. * * @name fm - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} brightness modulation index * @synonyms fmi * @example @@ -502,7 +502,7 @@ export const { fmi, fm } = registerControl(['fmi', 'fmh'], 'fm'); * Ramp type of fm envelope. Exp might be a bit broken.. * * @name fmenv - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} type lin | exp * @example * note("c e g b g e") @@ -518,7 +518,7 @@ export const { fmenv } = registerControl('fmenv'); * Attack time for the FM envelope: time it takes to reach maximum modulation * * @name fmattack - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} time attack time * @example * note("c e g b g e") @@ -533,7 +533,7 @@ export const { fmattack } = registerControl('fmattack'); * Waveform of the fm modulator * * @name fmwave - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} wave waveform * @example * n("0 1 2 3".fast(4)).scale("d:minor").s("sine").fmwave("").fm(4).fmh(2.01) @@ -547,7 +547,7 @@ export const { fmwave } = registerControl('fmwave'); * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase. * * @name fmdecay - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} time decay time * @example * note("c e g b g e") @@ -562,7 +562,7 @@ export const { fmdecay } = registerControl('fmdecay'); * Sustain level for the FM envelope: how much modulation is applied after the decay phase * * @name fmsustain - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} level sustain level * @example * note("c e g b g e") @@ -593,7 +593,7 @@ export const { bank } = registerControl('bank'); * mix control for the chorus effect * * @name chorus - * @tags effects + * @tags fx * @param {string | Pattern} chorus mix amount between 0 and 1 * @example * note("d d a# a").s("sawtooth").chorus(.5) @@ -611,7 +611,7 @@ export const { fft } = registerControl('fft'); * Note that the decay is only audible if the sustain value is lower than 1. * * @name decay - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} time decay time in seconds * @synonyms dec * @example @@ -623,7 +623,7 @@ export const { decay, dec } = registerControl('decay', 'dec'); * Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset. * * @name sustain - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} gain sustain level between 0 and 1 * @synonyms sus * @example @@ -635,7 +635,7 @@ export const { sustain, sus } = registerControl('sustain', 'sus'); * Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero. * * @name release - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} time release time in seconds * @synonyms rel * @example @@ -650,7 +650,7 @@ export const { hold } = registerControl('hold'); * can also optionally supply the 'bpq' parameter separated by ':'. * * @name bpf - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} frequency center frequency * @synonyms bandf, bp * @example @@ -663,7 +663,7 @@ export const { bandf, bpf, bp } = registerControl(['bandf', 'bandq', 'bpenv'], ' * Sets the **b**and-**p**ass **q**-factor (resonance). * * @name bpq - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} q q factor * @synonyms bandq * @example @@ -743,7 +743,7 @@ export const { loopEnd, loope } = registerControl('loopEnd', 'loope'); * Bit crusher effect. * * @name crush - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). * @example * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") @@ -755,7 +755,7 @@ export const { crush } = registerControl('crush'); * Fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers * * @name coarse - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. * @example * s("bd sd [~ bd] sd,hh*8").coarse("<1 4 8 16 32>") @@ -767,7 +767,7 @@ export const { coarse } = registerControl('coarse'); * Modulate the amplitude of a sound with a continuous waveform * * @name tremolo - * @tags effects, superdough + * @tags fx, superdough * @synonyms trem * @param {number | Pattern} speed modulation speed in HZ * @example @@ -780,7 +780,7 @@ export const { tremolo } = registerControl(['tremolo', 'tremolodepth', 'tremolos * Modulate the amplitude of a sound with a continuous waveform * * @name tremolosync - * @tags effects, superdough + * @tags fx, superdough * @synonyms tremsync * @param {number | Pattern} cycles modulation speed in cycles * @example @@ -796,7 +796,7 @@ export const { tremolosync } = registerControl( * Depth of amplitude modulation * * @name tremolodepth - * @tags effects, superdough + * @tags fx, superdough * @synonyms tremdepth * @param {number | Pattern} depth * @example @@ -808,7 +808,7 @@ export const { tremolodepth } = registerControl('tremolodepth', 'tremdepth'); * Alter the shape of the modulation waveform * * @name tremoloskew - * @tags effects, superdough + * @tags fx, superdough * @synonyms tremskew * @param {number | Pattern} amount between 0 & 1, the shape of the waveform * @example @@ -821,7 +821,7 @@ export const { tremoloskew } = registerControl('tremoloskew', 'tremskew'); * Alter the phase of the modulation waveform * * @name tremolophase - * @tags effects, superdough + * @tags fx, superdough * @synonyms tremphase * @param {number | Pattern} offset the offset in cycles of the modulation * @example @@ -834,7 +834,7 @@ export const { tremolophase } = registerControl('tremolophase', 'tremphase'); * Shape of amplitude modulation * * @name tremoloshape - * @tags effects, superdough + * @tags fx, superdough * @synonyms tremshape * @param {number | Pattern} shape tri | square | sine | saw | ramp * @example @@ -846,7 +846,7 @@ export const { tremoloshape } = registerControl('tremoloshape', 'tremshape'); * Filter overdrive for supported filter types * * @name drive - * @tags effects, superdough + * @tags fx, superdough * @param {number | Pattern} amount * @example * note("{f g g c d a a#}%16".sub(17)).s("supersaw").lpenv(8).lpf(150).lpq(.8).ftype('ladder').drive("<.5 4>") @@ -860,7 +860,7 @@ export const { drive } = registerControl('drive'); * Can be applied to multiple orbits with the ':' mininotation, e.g. `duckorbit("2:3")` * * @name duckorbit - * @tags effects, superdough + * @tags fx, superdough * @synonyms duck * @param {number | Pattern} orbit target orbit * @example @@ -881,7 +881,7 @@ export const { duck } = registerControl('duckorbit', 'duck'); * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`. * * @name duckdepth - * @tags effects, superdough + * @tags fx, superdough * @param {number | Pattern} depth depth of modulation from 0 to 1 * @example * stack( n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2), s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack(0.2).duckdepth("<1 .9 .6 0>")) @@ -901,7 +901,7 @@ export const { duckdepth } = registerControl('duckdepth'); * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`. * * @name duckonset - * @tags effects, superdough + * @tags fx, superdough * @synonyms duckons * * @param {number | Pattern} time The onset time in seconds @@ -929,7 +929,7 @@ export const { duckonset } = registerControl('duckonset', 'duckons'); * Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`. * * @name duckattack - * @tags effects, superdough + * @tags fx, superdough * @synonyms duckatt * * @param {number | Pattern} time The attack time in seconds @@ -949,7 +949,7 @@ export const { duckattack } = registerControl('duckattack', 'duckatt'); * * @name byteBeatExpression * @synonyms bbexpr - * @tags effects + * @tags fx * * @param {number | Pattern} byteBeatExpression bitwise expression for creating bytebeat * @example @@ -963,7 +963,7 @@ export const { byteBeatExpression, bbexpr } = registerControl('byteBeatExpressio * * @name byteBeatStartTime * @synonyms bbst - * @tags effects + * @tags fx * * @param {number | Pattern} byteBeatStartTime in samples (t) * @example @@ -990,7 +990,7 @@ export const { channels, ch } = registerControl('channels', 'ch'); * Controls the pulsewidth of the pulse oscillator * * @name pw - * @tags effects + * @tags fx * @param {number | Pattern} pulsewidth * @example * note("{f a c e}%16").s("pulse").pw(".8:1:.2") @@ -1003,7 +1003,7 @@ export const { pw } = registerControl(['pw', 'pwrate', 'pwsweep']); * Controls the lfo rate for the pulsewidth of the pulse oscillator * * @name pwrate - * @tags effects + * @tags fx * @param {number | Pattern} rate * @example * n(run(8)).scale("D:pentatonic").s("pulse").pw("0.5").pwrate("<5 .1 25>").pwsweep("<0.3 .8>") @@ -1016,7 +1016,7 @@ export const { pwrate } = registerControl('pwrate'); * Controls the lfo sweep for the pulsewidth of the pulse oscillator * * @name pwsweep - * @tags effects + * @tags fx * @param {number | Pattern} sweep * @example * n(run(8)).scale("D:pentatonic").s("pulse").pw("0.5").pwrate("<5 .1 25>").pwsweep("<0.3 .8>") @@ -1028,7 +1028,7 @@ export const { pwsweep } = registerControl('pwsweep'); * Phaser audio effect that approximates popular guitar pedals. * * @name phaser - * @tags effects, superdough + * @tags fx, superdough * @synonyms ph * @param {number | Pattern} speed speed of modulation * @example @@ -1046,7 +1046,7 @@ export const { phaserrate, ph, phaser } = registerControl( * The frequency sweep range of the lfo for the phaser effect. Defaults to 2000 * * @name phasersweep - * @tags effects, superdough + * @tags fx, superdough * @synonyms phs * @param {number | Pattern} phasersweep most useful values are between 0 and 4000 * @example @@ -1060,7 +1060,7 @@ export const { phasersweep, phs } = registerControl('phasersweep', 'phs'); * The center frequency of the phaser in HZ. Defaults to 1000 * * @name phasercenter - * @tags effects, superdough + * @tags fx, superdough * @synonyms phc * @param {number | Pattern} centerfrequency in HZ * @example @@ -1075,7 +1075,7 @@ export const { phasercenter, phc } = registerControl('phasercenter', 'phc'); * The amount the signal is affected by the phaser effect. Defaults to 0.75 * * @name phaserdepth - * @tags effects, superdough + * @tags fx, superdough * @synonyms phd, phasdp * @param {number | Pattern} depth number between 0 and 1 * @example @@ -1090,7 +1090,7 @@ export const { phaserdepth, phd, phasdp } = registerControl('phaserdepth', 'phd' * Choose the channel the pattern is sent to in superdirt * * @name channel - * @tags effects + * @tags fx * @param {number | Pattern} channel channel number * */ @@ -1099,7 +1099,7 @@ export const { channel } = registerControl('channel'); * In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open. * * @name cut - * @tags effects + * @tags fx * @param {number | Pattern} group cut group number * @example * s("[oh hh]*4").cut(1) @@ -1112,7 +1112,7 @@ export const { cut } = registerControl('cut'); * When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'. * * @name lpf - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} frequency audible between 0 and 20000 * @synonyms cutoff, ctf, lp * @example @@ -1126,7 +1126,7 @@ export const { cutoff, ctf, lpf, lp } = registerControl(['cutoff', 'resonance', /** * Sets the lowpass filter envelope modulation depth. * @name lpenv - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_ * @synonyms lpe * @example @@ -1140,7 +1140,7 @@ export const { lpenv, lpe } = registerControl('lpenv', 'lpe'); /** * Sets the highpass filter envelope modulation depth. * @name hpenv - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_ * @synonyms hpe * @example @@ -1154,7 +1154,7 @@ export const { hpenv, hpe } = registerControl('hpenv', 'hpe'); /** * Sets the bandpass filter envelope modulation depth. * @name bpenv - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_ * @synonyms bpe * @example @@ -1168,7 +1168,7 @@ export const { bpenv, bpe } = registerControl('bpenv', 'bpe'); /** * Sets the attack duration for the lowpass filter envelope. * @name lpattack - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} attack time of the filter envelope * @synonyms lpa * @example @@ -1182,7 +1182,7 @@ export const { lpattack, lpa } = registerControl('lpattack', 'lpa'); /** * Sets the attack duration for the highpass filter envelope. * @name hpattack - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} attack time of the highpass filter envelope * @synonyms hpa * @example @@ -1196,7 +1196,7 @@ export const { hpattack, hpa } = registerControl('hpattack', 'hpa'); /** * Sets the attack duration for the bandpass filter envelope. * @name bpattack - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} attack time of the bandpass filter envelope * @synonyms bpa * @example @@ -1210,7 +1210,7 @@ export const { bpattack, bpa } = registerControl('bpattack', 'bpa'); /** * Sets the decay duration for the lowpass filter envelope. * @name lpdecay - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} decay time of the filter envelope * @synonyms lpd * @example @@ -1224,7 +1224,7 @@ export const { lpdecay, lpd } = registerControl('lpdecay', 'lpd'); /** * Sets the decay duration for the highpass filter envelope. * @name hpdecay - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} decay time of the highpass filter envelope * @synonyms hpd * @example @@ -1239,7 +1239,7 @@ export const { hpdecay, hpd } = registerControl('hpdecay', 'hpd'); /** * Sets the decay duration for the bandpass filter envelope. * @name bpdecay - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} decay time of the bandpass filter envelope * @synonyms bpd * @example @@ -1254,7 +1254,7 @@ export const { bpdecay, bpd } = registerControl('bpdecay', 'bpd'); /** * Sets the sustain amplitude for the lowpass filter envelope. * @name lpsustain - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} sustain amplitude of the lowpass filter envelope * @synonyms lps * @example @@ -1269,7 +1269,7 @@ export const { lpsustain, lps } = registerControl('lpsustain', 'lps'); /** * Sets the sustain amplitude for the highpass filter envelope. * @name hpsustain - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} sustain amplitude of the highpass filter envelope * @synonyms hps * @example @@ -1284,7 +1284,7 @@ export const { hpsustain, hps } = registerControl('hpsustain', 'hps'); /** * Sets the sustain amplitude for the bandpass filter envelope. * @name bpsustain - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} sustain amplitude of the bandpass filter envelope * @synonyms bps * @example @@ -1299,7 +1299,7 @@ export const { bpsustain, bps } = registerControl('bpsustain', 'bps'); /** * Sets the release time for the lowpass filter envelope. * @name lprelease - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} release time of the filter envelope * @synonyms lpr * @example @@ -1315,7 +1315,7 @@ export const { lprelease, lpr } = registerControl('lprelease', 'lpr'); /** * Sets the release time for the highpass filter envelope. * @name hprelease - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} release time of the highpass filter envelope * @synonyms hpr * @example @@ -1331,7 +1331,7 @@ export const { hprelease, hpr } = registerControl('hprelease', 'hpr'); /** * Sets the release time for the bandpass filter envelope. * @name bprelease - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} release time of the bandpass filter envelope * @synonyms bpr * @example @@ -1347,7 +1347,7 @@ export const { bprelease, bpr } = registerControl('bprelease', 'bpr'); /** * Sets the filter type. The ladder filter is more aggressive. More types might be added in the future. * @name ftype - * @tags effects + * @tags fx * @param {number | Pattern} type 12db (0), ladder (1), or 24db (2) * @example * note("{f g g c d a a#}%8").s("sawtooth").lpenv(4).lpf(500).ftype("<0 1 2>").lpq(1) @@ -1363,7 +1363,7 @@ export const { ftype } = registerControl('ftype'); /** * controls the center of the filter envelope. 0 is unipolar positive, .5 is bipolar, 1 is unipolar negative * @name fanchor - * @tags effects, superdough + * @tags fx, superdough * @param {number | Pattern} center 0 to 1 * @example * note("{f g g c d a a#}%8").s("sawtooth").lpf("{1000}%2") @@ -1376,7 +1376,7 @@ export const { fanchor } = registerControl('fanchor'); * When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'. * * @name hpf - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} frequency audible between 0 and 20000 * @synonyms hp, hcutoff * @example @@ -1391,7 +1391,7 @@ export const { fanchor } = registerControl('fanchor'); * Applies a vibrato to the frequency of the oscillator. * * @name vib - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @synonyms vibrato, v * @param {number | Pattern} frequency of the vibrato in hertz * @example @@ -1419,7 +1419,7 @@ export const { noise } = registerControl('noise'); * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set * * @name vibmod - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @synonyms vmod * @param {number | Pattern} depth of vibrato (in semitones) * @example @@ -1438,7 +1438,7 @@ export const { hcutoff, hpf, hp } = registerControl(['hcutoff', 'hresonance', 'h * Controls the **h**igh-**p**ass **q**-value. * * @name hpq - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} q resonance factor between 0 and 50 * @synonyms hresonance * @example @@ -1450,7 +1450,7 @@ export const { hresonance, hpq } = registerControl('hresonance', 'hpq'); * Controls the **l**ow-**p**ass **q**-value. * * @name lpq - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} q resonance factor between 0 and 50 * @synonyms resonance * @example @@ -1463,7 +1463,7 @@ export const { resonance, lpq } = registerControl('resonance', 'lpq'); * DJ filter, below 0.5 is low pass filter, above is high pass filter. * * @name djf - * @tags effects, superdough + * @tags fx, superdough * @param {number | Pattern} cutoff below 0.5 is low pass filter, above is high pass filter * @example * n(irand(16).seg(8)).scale("d:phrygian").s("supersaw").djf("<.5 .3 .2 .75>") @@ -1480,7 +1480,7 @@ export const { djf } = registerControl('djf'); * * * @name delay - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} level between 0 and 1 * @example * s("bd bd").delay("<0 .25 .5 1>") @@ -1494,7 +1494,7 @@ export const { delay } = registerControl(['delay', 'delaytime', 'delayfeedback'] * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it * * @name delayfeedback - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} feedback between 0 and 1 * @synonyms delayfb, dfb * @example @@ -1508,7 +1508,7 @@ export const { delayfeedback, delayfb, dfb } = registerControl('delayfeedback', * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it * * @name delayfeedback - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} feedback between 0 and 1 * @synonyms delayfb, dfb * @example @@ -1520,7 +1520,7 @@ export const { delayspeed } = registerControl('delayspeed'); * Sets the time of the delay effect. * * @name delayspeed - * @tags effects + * @tags fx * @param {number | Pattern} delayspeed controls the pitch of the delay feedback * @synonyms delayt, dt * @example @@ -1533,7 +1533,7 @@ export const { delaytime, delayt, dt } = registerControl('delaytime', 'delayt', * Sets the time of the delay effect in cycles. * * @name delaysync - * @tags effects, superdough + * @tags fx, superdough * @param {number | Pattern} cycles delay length in cycles * @synonyms delayt, dt * @example @@ -1546,7 +1546,7 @@ export const { delaysync } = registerControl('delaysync'); * Specifies whether delaytime is calculated relative to cps. * * @name lock - * @tags effects + * @tags fx * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. * @superdirtOnly * @example @@ -1560,7 +1560,7 @@ export const { lock } = registerControl('lock'); * Set detune for stacked voices of supported oscillators * * @name detune - * @tags effects + * @tags fx * @param {number | Pattern} amount * @synonyms det * @example @@ -1572,7 +1572,7 @@ export const { detune, det } = registerControl('detune', 'det'); * Set number of stacked voices for supported oscillators * * @name unison - * @tags effects + * @tags fx * @param {number | Pattern} numvoices * @example * note("d f a a# a d3").fast(2).s("supersaw").unison("<1 2 7>") @@ -1584,7 +1584,7 @@ export const { unison } = registerControl('unison'); * Set the stereo pan spread for supported oscillators * * @name spread - * @tags effects + * @tags fx * @param {number | Pattern} spread between 0 and 1 * @example * note("d f a a# a d3").fast(2).s("supersaw").spread("<0 .3 1>") @@ -1595,7 +1595,7 @@ export const { spread } = registerControl('spread'); * Set dryness of reverb. See `room` and `size` for more information about reverb. * * @name dry - * @tags effects, superdough + * @tags fx, superdough * @param {number | Pattern} dry 0 = wet, 1 = dry * @example * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() @@ -1635,7 +1635,7 @@ export const { freq } = registerControl('freq'); * Attack time of pitch envelope. * * @name pattack - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @synonyms patt * @param {number | Pattern} time time in seconds * @example @@ -1647,7 +1647,7 @@ export const { pattack, patt } = registerControl('pattack', 'patt'); * Decay time of pitch envelope. * * @name pdecay - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @synonyms pdec * @param {number | Pattern} time time in seconds * @example @@ -1661,7 +1661,7 @@ export const { psustain, psus } = registerControl('psustain', 'psus'); * Release time of pitch envelope * * @name prelease - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @synonyms prel * @param {number | Pattern} time time in seconds * @example @@ -1676,7 +1676,7 @@ export const { prelease, prel } = registerControl('prelease', 'prel'); * If you don't set other pitch envelope controls, `pattack:.2` will be the default. * * @name penv - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} semitones change in semitones * @example * note("c") @@ -1688,7 +1688,7 @@ export const { penv } = registerControl('penv'); * Curve of envelope. Defaults to linear. exponential is good for kicks * * @name pcurve - * @tags effects + * @tags fx * @param {number | Pattern} type 0 = linear, 1 = exponential * @example * note("g1*4") @@ -1705,7 +1705,7 @@ export const { pcurve } = registerControl('pcurve'); * If you don't set an anchor, the value will default to the psustain value. * * @name panchor - * @tags effects + * @tags fx * @param {number | Pattern} anchor anchor offset * @example * note("c c4").penv(12).panchor("<0 .5 1 .5>") @@ -1727,7 +1727,7 @@ export const { gate, gat } = registerControl('gate', 'gat'); * Emulation of a Leslie speaker: speakers rotating in a wooden amplified cabinet. * * @name leslie - * @tags effects + * @tags fx * @param {number | Pattern} wet between 0 and 1 * @example * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() @@ -1739,7 +1739,7 @@ export const { leslie } = registerControl('leslie'); * Rate of modulation / rotation for leslie effect * * @name lrate - * @tags effects + * @tags fx * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow * @example * n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() @@ -1752,7 +1752,7 @@ export const { lrate } = registerControl('lrate'); * Physical size of the cabinet in meters. Be careful, it might be slightly larger than your computer. Affects the Doppler amount (pitch warble) * * @name lsize - * @tags effects + * @tags fx * @param {number | Pattern} meters somewhere between 0 and 1 * @example * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() @@ -1814,7 +1814,7 @@ export const { octave } = registerControl('octave'); * An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share the same global effects. * * @name orbit - * @tags effects, superdough + * @tags fx, superdough * @param {number | Pattern} number * @example * stack( @@ -1831,7 +1831,7 @@ export const { overshape } = registerControl('overshape'); * Sets position in stereo. * * @name pan - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel) * @example * s("[bd hh]*2").pan("<.5 1 .5 0>") @@ -1897,7 +1897,7 @@ export const { mode } = registerControl(['mode', 'anchor']); * When using mininotation, you can also optionally add the 'size' parameter, separated by ':'. * * @name room - * @tags effects, superdough + * @tags fx, superdough * @param {number | Pattern} level between 0 and 1 * @example * s("bd sd [~ bd] sd").room("<0 .2 .4 .6 .8 1>") @@ -1911,7 +1911,7 @@ export const { room } = registerControl(['room', 'size']); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomlp - * @tags effects, superdough + * @tags fx, superdough * @synonyms rlp * @param {number} frequency between 0 and 20000hz * @example @@ -1925,7 +1925,7 @@ export const { roomlp, rlp } = registerControl('roomlp', 'rlp'); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomdim - * @tags effects, superdough + * @tags fx, superdough * @synonyms rdim * @param {number} frequency between 0 and 20000hz * @example @@ -1940,7 +1940,7 @@ export const { roomdim, rdim } = registerControl('roomdim', 'rdim'); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomfade - * @tags effects, superdough + * @tags fx, superdough * @synonyms rfade * @param {number} seconds for the reverb to fade * @example @@ -1953,7 +1953,7 @@ export const { roomfade, rfade } = registerControl('roomfade', 'rfade'); /** * Sets the sample to use as an impulse response for the reverb. * @name iresponse - * @tags effects, superdough + * @tags fx, superdough * @param {string | Pattern} sample to use as an impulse response * @synonyms ir * @example @@ -1965,7 +1965,7 @@ export const { ir, iresponse } = registerControl(['ir', 'i'], 'iresponse'); /** * Sets speed of the sample for the impulse response. * @name irspeed - * @tags effects, superdough + * @tags fx, superdough * @param {string | Pattern} speed * @example * samples('github:switchangel/pad') @@ -1977,7 +1977,7 @@ export const { irspeed } = registerControl('irspeed'); /** * Sets the beginning of the IR response sample * @name irbegin - * @tags effects, superdough + * @tags fx, superdough * @param {string | Pattern} begin between 0 and 1 * @synonyms ir * @example @@ -1991,7 +1991,7 @@ export const { irbegin } = registerControl('irbegin'); * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomsize - * @tags effects, superdough + * @tags fx, superdough * @param {number | Pattern} size between 0 and 10 * @synonyms rsize, sz, size * @example @@ -2015,7 +2015,7 @@ export const { roomsize, size, sz, rsize } = registerControl('roomsize', 'size', * * * @name shape - * @tags effects, superdough + * @tags fx, superdough * @param {number | Pattern} distortion between 0 and 1 * @example * s("bd sd [~ bd] sd,hh*8").shape("<0 .2 .4 .6 .8>") @@ -2028,7 +2028,7 @@ export const { shape } = registerControl(['shape', 'shapevol']); * Most useful values are usually between 0 and 10 (depending on source gain). If you are feeling adventurous, you can turn it up to 11 and beyond ;) * * @name distort - * @tags effects, superdough, supradough + * @tags fx, superdough, supradough * @synonyms dist * @param {number | Pattern} distortion * @example @@ -2043,7 +2043,7 @@ export const { distort, dist } = registerControl(['distort', 'distortvol'], 'dis * More info [here](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode?retiredLocale=de#instance_properties) * * @name compressor - * @tags effects, superdough + * @tags fx, superdough * @example * s("bd sd [~ bd] sd,hh*8") * .compressor("-20:20:10:.002:.02") @@ -2064,7 +2064,7 @@ export const { compressorRelease } = registerControl('compressorRelease'); * Changes the speed of sample playback, i.e. a cheap way of changing pitch. * * @name speed - * @tags effects + * @tags fx * @param {number | Pattern} speed -inf to inf, negative numbers play the sample backwards. * @example * s("bd*6").speed("1 2 4 1 -2 -4") @@ -2078,7 +2078,7 @@ export const { speed } = registerControl('speed'); * Changes the speed of sample playback, i.e. a cheap way of changing pitch. * * @name stretch - * @tags effects + * @tags fx * @param {number | Pattern} factor -inf to inf, negative numbers play the sample backwards. * @example * s("gm_flute").stretch("1 2 .5") @@ -2089,7 +2089,7 @@ export const { stretch } = registerControl('stretch'); * Used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`. * * @name unit - * @tags effects + * @tags fx * @param {number | string | Pattern} unit see description above * @example * speed("1 2 .5 3").s("bd").unit("c").osc() @@ -2104,7 +2104,7 @@ export const { unit } = registerControl('unit'); * "A simplistic pitch-raising algorithm. It's not meant to sound natural; its sound is reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter, depending on the input. The algorithm works by cutting the signal into fragments (delimited by upwards-going zero-crossings) and squeezing those fragments in the time domain (i.e. simply playing them back faster than they came in), leaving silences inbetween. All the parameters apart from memlen can be modulated." * * @name squiz - * @tags effects + * @tags fx * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc. * @example * squiz("2 4/2 6 [8 16]").s("bd").osc() @@ -2129,7 +2129,7 @@ export const { squiz } = registerControl('squiz'); * Formant filter to make things sound like vowels. * * @name vowel - * @tags effects, superdough + * @tags fx, superdough * @param {string | Pattern} vowel You can use a e i o u ae aa oe ue y uh un en an on, corresponding to [a] [e] [i] [o] [u] [æ] [ɑ] [ø] [y] [ɯ] [ʌ] [œ̃] [ɛ̃] [ɑ̃] [ɔ̃]. Aliases: aa = å = ɑ, oe = ø = ö, y = ı, ae = æ. * @example * note("[c2 >]*2").s('sawtooth') @@ -2154,7 +2154,7 @@ export const { waveloss } = registerControl('waveloss'); * Noise crackle density * * @name density - * @tags effects, superdough + * @tags fx, superdough * @param {number | Pattern} density between 0 and x * @example * s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(4)) @@ -2262,7 +2262,7 @@ export let createParams = (...names) => * ADSR envelope: Combination of Attack, Decay, Sustain, and Release. * * @name adsr - * @tags effects + * @tags fx * @param {number | Pattern} time attack time in seconds * @param {number | Pattern} time decay time in seconds * @param {number | Pattern} gain sustain level (0 to 1) diff --git a/packages/superdough/wavetable.mjs b/packages/superdough/wavetable.mjs index cfc669c83..677d83628 100644 --- a/packages/superdough/wavetable.mjs +++ b/packages/superdough/wavetable.mjs @@ -180,7 +180,7 @@ export function registerWaveTable(key, tables, params) { * Loads a collection of wavetables to use with `s` * * @name tables - * @tags effects + * @tags fx */ export const tables = async (url, frameLen, json, options = {}) => { if (json !== undefined) return _processTables(json, url, frameLen); diff --git a/packages/supradough/dough.mjs b/packages/supradough/dough.mjs index 23dfbee09..9b5bf189e 100644 --- a/packages/supradough/dough.mjs +++ b/packages/supradough/dough.mjs @@ -18,7 +18,7 @@ function applyGainCurve(val) { * @param {number} a - Signal A (can be a single value or an array value in buffer processing). * @param {number} b - Signal B (can be a single value or an array value in buffer processing). * @param {number} m - Crossfade parameter (0.0 = all A, 1.0 = all B, 0.5 = equal mix). - * @tags effects + * @noAutocomplete * @returns {number} Crossfaded output value. */ function crossfade(a, b, m) { From 7e4cbefc8fa812769f35eb9da29d3f50839518e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sun, 19 Oct 2025 21:22:57 +0200 Subject: [PATCH 014/124] Tag crossfade as internals --- packages/supradough/dough.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/supradough/dough.mjs b/packages/supradough/dough.mjs index 9b5bf189e..a31097339 100644 --- a/packages/supradough/dough.mjs +++ b/packages/supradough/dough.mjs @@ -18,7 +18,7 @@ function applyGainCurve(val) { * @param {number} a - Signal A (can be a single value or an array value in buffer processing). * @param {number} b - Signal B (can be a single value or an array value in buffer processing). * @param {number} m - Crossfade parameter (0.0 = all A, 1.0 = all B, 0.5 = equal mix). - * @noAutocomplete + * @tags internals * @returns {number} Crossfaded output value. */ function crossfade(a, b, m) { From 466bf819d883b7f2e35a5dc617727c53765d71cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sun, 19 Oct 2025 21:38:46 +0200 Subject: [PATCH 015/124] Fix formatting (trailing space) --- packages/superdough/worklets.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 3e8276225..b99b9caf6 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -852,7 +852,7 @@ class PulseOscillatorProcessor extends AudioWorkletProcessor { registerProcessor('pulse-oscillator', PulseOscillatorProcessor); /** BYTE BEATS - * @tags internals + * @tags internals */ const chyx = { /*bit*/ bitC: function (x, y, z) { From 0830a9df69ddd49bcec08f25b58b390a103bbb74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sat, 1 Nov 2025 20:19:57 +0100 Subject: [PATCH 016/124] Add superdirt tags --- packages/core/controls.mjs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 1bfd5a892..da5337ecf 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -399,7 +399,7 @@ export const { note } = registerControl(['note', 'n']); * A pattern of numbers that speed up (or slow down) samples while they play. Currently only supported by osc / superdirt. * * @name accelerate - * @tags samples + * @tags samples, superdirt * @param {number | Pattern} amount acceleration. * @superdirtOnly * @example @@ -444,7 +444,7 @@ export const { postgain } = registerControl('postgain'); * Like `gain`, but linear. * * @name amp - * @tags fx + * @tags fx, superdirt * @param {number | Pattern} amount gain. * @superdirtOnly * @example @@ -1075,7 +1075,7 @@ export const { phasercenter, phc } = registerControl('phasercenter', 'phc'); * The amount the signal is affected by the phaser effect. Defaults to 0.75 * * @name phaserdepth - * @tags fx, superdough + * @tags fx, superdough, superdirt * @synonyms phd, phasdp * @param {number | Pattern} depth number between 0 and 1 * @example @@ -1546,7 +1546,7 @@ export const { delaysync } = registerControl('delaysync'); * Specifies whether delaytime is calculated relative to cps. * * @name lock - * @tags fx + * @tags fx, superdirt * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. * @superdirtOnly * @example @@ -1595,7 +1595,7 @@ export const { spread } = registerControl('spread'); * Set dryness of reverb. See `room` and `size` for more information about reverb. * * @name dry - * @tags fx, superdough + * @tags fx, superdough, superdirt * @param {number | Pattern} dry 0 = wet, 1 = dry * @example * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() @@ -1727,7 +1727,7 @@ export const { gate, gat } = registerControl('gate', 'gat'); * Emulation of a Leslie speaker: speakers rotating in a wooden amplified cabinet. * * @name leslie - * @tags fx + * @tags fx, superdirt * @param {number | Pattern} wet between 0 and 1 * @example * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() @@ -1739,7 +1739,7 @@ export const { leslie } = registerControl('leslie'); * Rate of modulation / rotation for leslie effect * * @name lrate - * @tags fx + * @tags fx, superdirt * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow * @example * n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() @@ -1752,7 +1752,7 @@ export const { lrate } = registerControl('lrate'); * Physical size of the cabinet in meters. Be careful, it might be slightly larger than your computer. Affects the Doppler amount (pitch warble) * * @name lsize - * @tags fx + * @tags fx, superdirt * @param {number | Pattern} meters somewhere between 0 and 1 * @example * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() @@ -1801,6 +1801,7 @@ export const { nudge } = registerControl('nudge'); * Sets the default octave of a synth. * * @name octave + * @tags fx, superdirt * @param {number | Pattern} octave octave number * @example * n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc() @@ -2089,7 +2090,7 @@ export const { stretch } = registerControl('stretch'); * Used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`. * * @name unit - * @tags fx + * @tags fx, superdirt * @param {number | string | Pattern} unit see description above * @example * speed("1 2 .5 3").s("bd").unit("c").osc() @@ -2104,7 +2105,7 @@ export const { unit } = registerControl('unit'); * "A simplistic pitch-raising algorithm. It's not meant to sound natural; its sound is reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter, depending on the input. The algorithm works by cutting the signal into fragments (delimited by upwards-going zero-crossings) and squeezing those fragments in the time domain (i.e. simply playing them back faster than they came in), leaving silences inbetween. All the parameters apart from memlen can be modulated." * * @name squiz - * @tags fx + * @tags fx, superdirt * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc. * @example * squiz("2 4/2 6 [8 16]").s("bd").osc() From b7827cb89f16624773f9c5717d64419d1a729154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sat, 1 Nov 2025 21:11:57 +0100 Subject: [PATCH 017/124] Tag new functions --- packages/core/controls.mjs | 4 ++ packages/core/pattern.mjs | 9 ++++ .../src/repl/components/panel/Reference.jsx | 42 ++++--------------- 3 files changed, 20 insertions(+), 35 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 50a494864..7c66b4002 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -2049,6 +2049,7 @@ export const { distort, dist } = registerControl(['distort', 'distortvol', 'dist * * @name distortvol * @synonyms distvol + * @tags fx, superdough, supradough * @param {number | Pattern} volume linear postgain of the distortion * @example * s("bd*4").bank("tr909").distort(2).distortvol(0.8) @@ -2059,6 +2060,7 @@ export const { distortvol } = registerControl('distortvol', 'distvol'); * Type of waveshaping distortion to apply. * * @name distorttype + * @tags fx, superdough, supradough * @synonyms disttype * @param {number | string | Pattern} type type of distortion to apply * @example @@ -2487,6 +2489,7 @@ export const { polyTouch } = registerControl('polyTouch'); /** * The host to send open sound control messages to. Requires running the OSC bridge. * @name oschost + * @tags external_io * @param {string | Pattern} oschost e.g. 'localhost' * @example * note("c4").oschost('127.0.0.1').oscport(57120).osc(); @@ -2496,6 +2499,7 @@ export const { oschost } = registerControl('oschost'); /** * The port to send open sound control messages to. Requires running the OSC bridge. * @name oscport + * @tags external_io * @param {number | Pattern} oscport e.g. 57120 * @example * note("c4").oschost('127.0.0.1').oscport(57120).osc(); diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 8cb30e155..e8cd3d23f 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -3128,6 +3128,7 @@ export const extend = stepRegister('extend', function (factor, pat) { * `stepcat("a b".fast(2), "c d")` would be the same as `"[a b] [a b] c d"`. * * TODO: find out how this function differs from extend + * @tags temporal * @example * stepcat( * sound("bd bd - cp").replicate(2), @@ -3697,6 +3698,7 @@ export const morph = (frompat, topat, bypat) => { * Soft-clipping distortion * * @name soft + * @tags fx * @param {number | Pattern} distortion amount of distortion to apply * @param {number | Pattern} volume linear postgain of the distortion * @@ -3705,6 +3707,7 @@ export const morph = (frompat, topat, bypat) => { * Hard-clipping distortion * * @name hard + * @tags fx * @param {number | Pattern} distortion amount of distortion to apply * @param {number | Pattern} volume linear postgain of the distortion * @@ -3713,6 +3716,7 @@ export const morph = (frompat, topat, bypat) => { * Cubic polynomial distortion * * @name cubic + * @tags fx * @param {number | Pattern} distortion amount of distortion to apply * @param {number | Pattern} volume linear postgain of the distortion * @@ -3721,6 +3725,7 @@ export const morph = (frompat, topat, bypat) => { * Diode-emulating distortion * * @name diode + * @tags fx * @param {number | Pattern} distortion amount of distortion to apply * @param {number | Pattern} volume linear postgain of the distortion * @@ -3729,6 +3734,7 @@ export const morph = (frompat, topat, bypat) => { * Asymmetrical diode distortion * * @name asym + * @tags fx * @param {number | Pattern} distortion amount of distortion to apply * @param {number | Pattern} volume linear postgain of the distortion * @@ -3737,6 +3743,7 @@ export const morph = (frompat, topat, bypat) => { * Wavefolding distortion * * @name fold + * @tags fx * @param {number | Pattern} distortion amount of distortion to apply * @param {number | Pattern} volume linear postgain of the distortion * @@ -3745,6 +3752,7 @@ export const morph = (frompat, topat, bypat) => { * Wavefolding distortion composed with sinusoid * * @name sinefold + * @tags fx * @param {number | Pattern} distortion amount of distortion to apply * @param {number | Pattern} volume linear postgain of the distortion * @@ -3753,6 +3761,7 @@ export const morph = (frompat, topat, bypat) => { * Distortion via Chebyshev polynomials * * @name chebyshev + * @tags fx * @param {number | Pattern} distortion amount of distortion to apply * @param {number | Pattern} volume linear postgain of the distortion * diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index 2a7dcc2a8..3053f03dc 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -11,8 +11,13 @@ const availableFunctions = (() => { for (const doc of jsdocJson.docs) { if (!isValid(doc)) continue; if (seen.has(doc.name)) continue; - doc.tags = doc.tags?.filter((t) => t && typeof t === 'string') || []; + + // jsdoc also uses "tags" for when you use @something in the comments and it doesn't know what + // @something is. We only want data from comments like `@tags fx, superdough` here. + // If nothing is specified, we default to "untagged" for debugging + doc.tags = doc.tags?.filter((t) => t && typeof t === 'string') || ['untagged']; functions.push(doc); + const synonyms = doc.synonyms || []; seen.add(doc.name); for (const s of synonyms) { @@ -38,16 +43,6 @@ const getInnerText = (html) => { return div.textContent || div.innerText || ''; }; -const GROUP_DISPLAY_NAMES = { - external_io: 'External I/O', - effects: 'Effects', - untagged: 'Untagged', - structure: 'Structure', - transforms: 'Transforms', -}; - -const GROUP_ORDER = ['effects', 'transforms', 'structure', 'untagged', 'external_io']; - export function Reference() { const [search, setSearch] = useState(''); const [selectedTag, setSelectedTag] = useState(null); @@ -80,29 +75,6 @@ export function Reference() { }); }, [search, selectedTag]); - const visibleFunctionsByGroup = (() => { - const groups = {}; - for (const doc of visibleFunctions) { - const group = (doc.tags || ['untagged'])[0]; - if (!groups[group]) { - groups[group] = []; - } - groups[group].push(doc); - } - return groups; - })(); - // console.log(visibleFunctionsByGroup); - - // Sort and map group entries - const sortedGroups = Object.entries(visibleFunctionsByGroup).sort(([a], [b]) => { - const ai = GROUP_ORDER.indexOf(a); - const bi = GROUP_ORDER.indexOf(b); - if (ai === -1 && bi === -1) return a.localeCompare(b); - if (ai === -1) return 1; - if (bi === -1) return -1; - return ai - bi; - }); - const tagCounts = {}; for (const doc of availableFunctions) { (doc.tags || ['untagged']).forEach((t) => { @@ -114,7 +86,7 @@ export function Reference() { return (

-
+
From 2fa8ed14245b45cce91708495befa82afe50745b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sun, 9 Nov 2025 20:21:19 +0100 Subject: [PATCH 018/124] Revert seq synonym refactor --- packages/core/pattern.mjs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index e8cd3d23f..b8376c8ff 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1566,6 +1566,11 @@ export function fastcat(...pats) { return result; } +/** See `fastcat` */ +export function sequence(...pats) { + return fastcat(...pats); +} + /** Like **cat**, but the items are crammed into one cycle. * @tags combiners * @synonyms seq, fastcat @@ -1579,9 +1584,6 @@ export function fastcat(...pats) { * note("c4(5,8)") * ) */ -export function sequence(...pats) { - return fastcat(...pats); -} export function seq(...pats) { return fastcat(...pats); From 0bb89d1a9f26b6bcecae3bf4694731bc47965437 Mon Sep 17 00:00:00 2001 From: scrappy_fiddler Date: Wed, 19 Nov 2025 20:09:14 +0100 Subject: [PATCH 019/124] New page FAQ in "More" --- website/src/config.ts | 1 + website/src/pages/learn/faq.mdx | 403 ++++++++++++++++++++++++++++++++ 2 files changed, 404 insertions(+) create mode 100644 website/src/pages/learn/faq.mdx diff --git a/website/src/config.ts b/website/src/config.ts index f335b272e..eb39d2958 100644 --- a/website/src/config.ts +++ b/website/src/config.ts @@ -76,6 +76,7 @@ export const SIDEBAR: Sidebar = { { text: 'MIDI & OSC', link: 'learn/input-output' }, ], More: [ + { text: 'FAQ', link: 'learn/faq' }, { text: 'Recipes', link: 'recipes/recipes' }, { text: 'Mini-Notation', link: 'learn/mini-notation' }, { text: 'Visual Feedback', link: 'learn/visual-feedback' }, diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx new file mode 100644 index 000000000..30f2986bd --- /dev/null +++ b/website/src/pages/learn/faq.mdx @@ -0,0 +1,403 @@ +--- +title: Frequently Asked Questions +layout: ../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '../../docs/MiniRepl'; +import { JsDoc } from '../../docs/JsDoc'; + +# Frequently Asked Questions + +This page contains frequently asked questions. Usually, the topic is explained in more detail in a section which is linked in the answer. This page's aim is to give an overview over topics which are important to the users of strudel. + +## Is Strudel/Tidal free? + +Yes - there is no charge, this is a collective open source project, and the music you make with it is your own. + +However there are some caveats - the source code must stay free, i.e. you cannot distribute strudel or tidal as part of projects with incompatible licenses - see the [license]() for details. The contributed examples and tracks are also separately licensed, and must not e.g. be used to train AI models without permission. + +## How to record or export audio? + +There are multiple ways to record the audio -- and video -- output of Strudel: + - capture the raw stereo signal coming out of your web browser. + - use the alternative SuperDirt audio engine. Read [this page](/learn/input-output/#oscsuperdirtstrudeldirt) to know more about it. + - capture the audio/video stream using a capture tool such as [OBS](). + - don't record anything and code it again in front of your friends. + +You will need an external audio editor/DAW such as Reaper/Audacity/Ardour, etc. Strudel itself does not have functionality for exporting stems / individual tracks to an audio or midi file. Strudel is not a digital audio workstation and does not operate following the same principles shared by most traditional audio softwares. + +## Can I use strudel with my IDE? + +Yes you can. There are experimental modes, made by community members, for several IDEs such as: + - VS Code: [Strudel VS](): an experimental mode for Microsoft VSCode. A revived version of [TidalStrudel](), which is defunct. + - nvim: [strudel.nvim](https://github.com/gruvw/strudel.nvim) + +## How can I record samples? + +You can use your own samples with Strudel. There are multiple ways to load your sample collection. Some methods are good for quick experimentation, some others are good to share your audio collection with other musicians: + + - Import folders [from the interface](/learn/samples/#from-disk-via-import-sounds-folder) + - Host your sound library e.g. on github and [load them from an URL](/learn/samples/#loading-custom-samples) + +## Can I use Strudel with AI/LLM tools? + +You are free to do what you like with Strudel, within the terms of the free/open source AGPLv3 license. However as a community we are interested in exploring human creativity. AI is *way* over-hyped right now, including by people with very shady motives. Many in the community are very wary of people training models on their tunes that they've poured their love into. So please keep discussion and questions around AI and LLMs to channels dedicated to the topic and be fully respectful of other people's work. + +Furthermore, tools like ChatGPT generally give wrong answers. Please don't ask the community to fix those answers for you, as generally they will be timewasting nonsense. + +Human questions only! + +## How to run offline? + +Strudel works offline just fine! There are multiple techniques to run it yourself, see [this explanation](learn/pwa/#using-strudel-offline). + +## How to change tempo? How do I translate BPM to cpm? + +If you have your tempo in beats per minute and use 4 beats per cycle (e.g. if your track is in 4/4ths) then you can do `setcpm(BPM/4)` where BPM is your beats per minute. + +If you have a different number of beats per bar or are using more or less beats per cycle (e.g. If you want to put only half a bar or two bars into one cycle), adjust accordingly. + +## Where can I see all the functions? + +If you pop open the sidetab of strudel.cc (small white < on the right hand side), there is a tab "reference" which lists all the functions of strudel. + +## Where can I see all the samples and synths? + +If you pop open the sidetab of strudel.cc (small white < on the right hand side), there is a tab "sounds" which lists all the drum machines, samples and synths currently loaded. + +## How do I use this exactly like a DAW? + +Short answer: you don't. + +Long answer: you can use Strudel to work along your creative work in a DAW. There are many ways to do so. + +If you want to emulate the functionality of a DAW in a live coding language, you'll have to identify the operations executed by the DAW (sequencing, repeating, applying filters and envelopes) and write code that is equivalent to these operations. You might then find that the typical DAW workflow is not really adapted to live coding (because, despite both being ways of making music on the computer, they are two very different tools) and adapt your way of proceeding to the medium of code. This might mean leaving more place to serendipity and writing code that you don't predict the output of. + +## Why doesn't everyone just use a DAW? + +There is no easy answer to this question. Here are some thoughts: + - Live coding tools such as Strudel are excellent for improvising music and visuals using a computer. DAWs are valuable and robust companions for other activities such as producing, mastering and mixing audio, among other usages. Using a tool does not exclude from using any another tool, just build a toolbox. + - Live coding has been practiced for quite some time as a performative activity. Artists like to show their screens while playing in front of an audience. It is an essential part of what they do, of the way they share their activity with everybody. + - Code is a human language, it is made for other humans to read it. You can read the code and enjoy the music too. It has meaning, value, and there might even be something poetic/important about it! + - Strudel is free and open source, you can inspect the code, reshape it, contribute to it if you can/want. It is not opaque and this matters for many people. There is no black box, no obscure abstractions, no business model or hidden features. We need open tools in the arts! + - Live coders don't shy away from using DAWs. They use them all of the time, especially when it makes their life easier for... live coding! + - Code is an artistic material like any other. There is something valuable in the process of making music through code. More generally speaking, it is nice to tackle creative problems through the use of a programming language: creative thinking, building up your own solutions, DIY approach to music-making, unexpected outcome of algorithms, funny human errors, etc. + - There are pianos and trumpets in your DAW: why do people continue playing the piano or the trumpet? Think of live coding tools as instruments that you activate through the act of programming. + +## Is it more efficient to use Strudel than a DAW? + +Strudel was not build to be a DAW, yet it can still be used to make covers, arrange tracks, or prepare patterns for jamming. When playing concerts or jamming, some livecoders prepare their code, some perform from scratch. + +It might be interesting for you to check out for yourself how strudel can be used to express yourself creatively. Also you are free to combine a language like Strudel with a DAW. + +## How can I interface Strudel with my favorite music software? What can I do with it? + +Strudel can send [MIDI and OSC](/learn/input-output/), which are protocols for communicating musical information. + +Another music software (or hardware!) can then listen to these messages and process them according to its capabilities. + +A simple example would be to send livecoded audio to Ableton on different tracks and then use it to mix them. + +You could also send the MIDI of a sequenced pattern to Musescore and then have it transcribe your livecoded work as a musical score. + +You could also send MIDI to your hardware synths because you think they sound better than the software synths built-in Strudel. + +## How do I use this in my closed source webgame? + +You don't. You would need to re-license your game to AGPLv3 to fulfill the license Strudel is distributed under. + +## Where can I download loads of patterns to train my LLM? + +You cannot, as there is no such place. For details regarding our stance towards AI/LLM, see [above](/learn/faq/#can-i-use-strudel-with-aillm-tools) + +## How to play different patterns simultaneously? + +Using the $ operator, several patterns can be played at once: + + +See also [stack](intro/#combining-patterns) + +## Is it possible to mute a pattern? + +With an additional underscore, a pattern can be muted. + + + +See also [hush](/learn/conditional-modifiers/#hush) + +## How can I arrange in Strudel using `mask`? + +With mini-notation, using the `<>` and `!` operators, you can try something like +``` +.mask("<0!24 1!40>") +``` +It mutes a pattern for 24 cycles and plays it for 40. You would gain 64 cycles total, a multiple of 2/4/8 commonly used in western music. + +If each cycle is a bar, as a starting point, you could write a mask like that for any pattern: +``` +.mask("<0!16 0!16 0!16 0!16 0!16 0!10>") +``` +It mutes it throughout. + +For arranging, you could add the same mask to each part and replace some zeroes with ones in your different masks to make parts play. + +If you use `.mask()` on different patterns mess up your counting, then patterns do not align anymore. +On the other hand, doing that on purpose is one of the things that could be considered a strength of tidalcycles and Strudel. +You can make things quite lively and more organic with a little (controlled) interference, according to your own taste. +And you are free to arrange in cycles like 3, 6 or 9 too. + +To modify everything at once, you could try all and when, for example: + +``` +all(x=>x.when("<0!7 1>", x=>x.lpf(saw.range(200, 2000)))) +``` + +This would lowpass filter sweep everything every 8 cycles. + +## How can I arrange in Strudel using `arrange` or `pick`? + +Take [Pachelbel's Canon in D](https://en.wikipedia.org/wiki/Pachelbel%27s_Canon#Analysis) as an example which has 4 voices (one cello and 3 violins) which have repeating patterns, as seen in the link above. + +The following snipped defines the patterns as constants which can then be used for the different voices. `arrange` takes multiple arguments, which are each a number of cycles and a pattern which is played for the number of cycles, wrapped in `[]` If the pattern is shorter than the number, it is repeated. +") + .color("grey").sound("gm_tremolo_strings:3") + const violin_p1 = note( + "<[f#5 e5 d5 c#5] [b4 a4 b4 c#5]>") + .color("blue") + const violin_p2 = note( + "<[d5 c#5 b4 a4] [ g4 f#4 g4 f#4]>") + .color("green") + const violin_p3 = note( + "<[d4 f#4 a4 g4 f#4 d4 f#4 e4] [d4 b3 d4 a4 g4 b4 a4 g4]>") + .color("purple") + const violin_p4 = note( + "<[f#4 d4 e4 c#5 d5 f#5 a5 a4] [b4 g4 a4 f#4 d4 d5 [d5@3 c#5]@2]>") + .color("red") + + + cello$: arrange( + [2, silence], + [18,cello]) + violin1$: arrange( + [4,silence], + [2,violin_p1], [2,violin_p2], + [2,violin_p3], [2,violin_p4], + [2,violin_p1], [2,violin_p2], + [2,violin_p3], [2,violin_p4] + ).sound("gm_tremolo_strings:0") + violin2$: arrange( + [6,silence], [2,violin_p1], + [2,violin_p2], [2,violin_p3], + [2,violin_p4], [2,violin_p1], + [2,violin_p2], [2,violin_p3] + ).sound("gm_tremolo_strings:1") + violin3$: arrange( + [8,silence], + [2,violin_p1], [2,violin_p2], + [2,violin_p3], [2,violin_p4], + [2,violin_p1], [2,violin_p2] + ).sound("gm_tremolo_strings:2") + + all(x => x.release(.2)) +`} /> + +Alternatively, you can also put the different patterns for the violins into one single array (`const violins = [violin_p1, violin_p2, violin_p3, violin_p4]`) and use a pattern as an index to `pick` the nth element of that array. This replaces the voices defined above. Here you use `0@2` to specifiy that the first item (i.e. with index `0`) is played for `2` cycles. + +`pick` has better highlighting than `arrange`: + +") + .color("grey").sound("gm_tremolo_strings:3") + const violin_p1 = note( + "<[f#5 e5 d5 c#5] [b4 a4 b4 c#5]>") + .color("blue") + const violin_p2 = note( + "<[d5 c#5 b4 a4] [ g4 f#4 g4 f#4]>") + .color("green") + const violin_p3 = note( + "<[d4 f#4 a4 g4 f#4 d4 f#4 e4] [d4 b3 d4 a4 g4 b4 a4 g4]>") + .color("purple") + const violin_p4 = note( + "<[f#4 d4 e4 c#5 d5 f#5 a5 a4] [b4 g4 a4 f#4 d4 d5 [d5@3 c#5]@2]>") + .color("red") + +const violins = [violin_p1, violin_p2, violin_p3, violin_p4] + +cello$: "<~@2 0@18>".pick([cello]) +violin1$: "<~@4 0@2 1@2 2@2 3@2 0@2 1@2 2@2 3@2>".pick(violins) +.sound("gm_tremolo_strings:0") +violin2$: "<~@6 0@2 1@2 2@2 3@2 0@2 1@2 2@2>".pick(violins) +.sound("gm_tremolo_strings:1") +violin3$: "<~@8 0@2 1@2 2@2 3@2 0@2 1@2 >".pick(violins) +.sound("gm_tremolo_strings:2") +all(x => x.release(.2)) +`} /> + +The `pick` method also works with jsons which have named elements, which makes it easier to read, see the [here](/learn/conditional-modifiers/#pick). `pickRestart` restarts the pattern upon picking it which can make a difference if the duration of the pick indexes doesn't line up with the patterns which are picked - which is not the case here. + +Try adding `.punchcard()` after the `release(.2)` for a visualization. + +## I saw Switch Angel using functions which I cannot find in the reference (e.g. `trancegate`). How do I make it work? + +Methods like `trancegate()`, `rlpf()` and `acidenv()` are currently not pattern methods which come natively with strudel. + +It's part of a script for strudel which was written by Switch Angel and published [here](https://github.com/switchangel/strudel-scripts/blob/main/allscripts.js) + +Note that this script defines functions and registers methods which `trancegate()` will depend on so just pasting the `trancegate()` method from that script alone will not suffice, but you will also need the `fill` method. + +If you copy the full script into your strudel, be mindful of lines which try to load local samples - these should be deleted or prefixed with `//` as a comment. + +If you paste these functions into your strudel session, hit "Update" and delete the source of the functions, the functions will still be availabe until you do a browser refresh. + +## Is there difference between `n` and `note`? + +They are not aliases of each other, in contrast to `s` and `sound`. + +The method `note` is used to reference a certain note (either as its name, such as `c` or `b2` or the midi number `69`, for example `note("c3 e3 g3")`). + +On the other hand, `n` is a way to reference the nth index of something. This something can be a scale (eg `n("0 2 4").scale("C:major")`) , but it can also be a particular note in a chord (see https://strudel.cc/recipes/recipes/#arpeggios for an example) . + +The method `n` can also be used for something completely unrelated to notes like the nth sample from a sample map `s("hh*8").bank("RolandTR909").n("0 1 2 3")`. Note that `pick` does *not* use `n`. + +".pickRestart([ + n("0 1 2 0"), + n("2 3 4 ~"), + n("[4 5] [4 3] 2 0"), + n("0 -3 0 ~") + ]).scale("C:major").s("piano")`} /> + + +## Is there a cheat sheet for all symbols? + +Yes! + +``` +' marks start and end of strings, is different from " +" marks start and end of single line patterns in mini notation, is different from ' +` marks start and end of patterns with line breaks in mini notation, is different from ' +[] used for patterns, each item in it has the same length +<> used for patterns, alternates each item each cycle +{} historically used for polyrhythmic patterns. {a b c}%4 is the same as *4. +@3 elongates the item by a factor of 3 (other numbers work too) +@ after an item: elongates the item once (multiple @ work too c @ @ is the same as c@3) +_ after an item: also elongates an item once (multiple _ work too c _ _ is the same as c@3) +. this divides equal parts of a pattern and is called a foot. Can be used instead of [] like this: "1 6 7 8 . 2 . 3 . 4" is the same as "[1 6 7 8] 2 3 4" +- silence +~ also silence +x not silence (for the use in struct, same as 1) +s increase a note of a scale by one semitone, i.e. sharp +b decrease a note of a scale by one semitone, i.e. flat +# used in mondo notation +# also used for names of chords, like F# +*3 play the sample or pattern at thrice the speed, fast(3) +!3 play the sample or pattern three times +/2 play the sample or pattern at half speed, slow(2) +? play the pattern sometimes +| once per cycle, choose randomly a pattern of those separated by i.e. chooseCycles() +, play all items separated by it at the same time, i.e. stack() +: is used to separate multiple parameters, such as adsr(".1:.1:.5:.2") +$: at the start of a line, defines a member of the stack. is the only stack name that should occur multiple names +_ before a stack name: mutes the stack, i.e. hush(), for example _$: s("bd") +``` + +## Are there more FAQ items? + +These pages have been taken from [this pad](https://doc.patternclub.org/_CgofWouTciXXHexUP9AVg?both). Some of the items there have not been brushed up and brought here. + +These include the following items: 9, 11, 12 and 19 + +# Coding Syntax (old Content from code) + +Let's take a step back and understand how the syntax in Strudel works. + +Take a look at this simple example: + + + +- We have a word `note` which is followed by some brackets `()` with some words/letters/numbers inside, surrounded by quotes `"c a f e"` +- Then we have a dot `.` followed by another similar piece of code `s("piano")`. +- We can also see these texts are _highlighted_ using colours: word `note` is purple, the brackets `()` are grey, and the content inside the `""` are green. (The colors could be different if you've changed the default theme) + +What happens if we try to 'break' this pattern in different ways? + + + + + + + +Ok, none of these seem to work... + + + +This one does work, but now we only hear the first note... + +So what is going on here? + +# Functions, arguments and chaining + +So far, we've seen the following syntax: + +``` +xxx("foo").yyy("bar") +``` + +Generally, `xxx` and `yyy` are called [_functions_](), while `foo` and `bar` are called function [_arguments_ or _parameters_](). +So far, we've used the functions to declare which aspect of the sound we want to control, and their arguments for the actual data. +The `yyy` function is called a [_chained_ function](https://en.wikipedia.org/wiki/Method_chaining), because it is preceded with a dot (`.`). + +Generally, the idea with chaining is that code such as `a("this").b("that").c("other")` allows `a`, `b` and `c` functions to happen in a specified order, without needing to write them as three separate lines of code. +You can think of this as being similar to chaining audio effects together using guitar pedals or digital audio effects. + +Strudel makes heavy use of chained functions. Here is a more sophisticated example: + + + +## Write your own chained function + +You can write your own chained function using `register`. Here's the above chain but registered as a reusable, chained function. + + pat + .s("sawtooth") + .cutoff(500) + //.delay(0.5) + .room(0.5) + ) +note("a3 c#4 e4 a4").effectChain()`} +/> + +Try adding `.rev()` after `effectChain()` to hear further effects added. + +# Comments + +The `//` in the example above is a line comment, resulting in the `delay` function being ignored. +It is a handy way to quickly turn code on and off. +Try uncommenting this line by deleting `//` and refreshing the pattern. +You can also use the keyboard shortcut `cmd-/` to toggle comments on and off. + +You might noticed that some comments in the REPL samples include some words starting with a "@", like `@by` or `@license`. +Those are just a convention to define some information about the music. We will talk about it in the [Music metadata](/learn/metadata) section. + +# Strings + +Ok, so what about the content inside the quotes (e.g. `"c a f e"`)? +In JavaScript, as in most programming languages, this content is referred to as being a [_string_](). +A string is simply a sequence of individual characters. +In TidalCycles, double quoted strings are used to write _patterns_ using the mini-notation, and you may hear the phrase _pattern string_ from time to time. +If you want to create a regular string and not a pattern, you can use single quotes, e.g. `'C minor'` will not be parsed as Mini Notation. + +The good news is, that this covers most of the JavaScript syntax needed for Strudel! + +
From 371e6c7d6d4f1fdd780c6a07302049ae069c1a08 Mon Sep 17 00:00:00 2001 From: scrappy_fiddler Date: Wed, 19 Nov 2025 20:17:19 +0100 Subject: [PATCH 020/124] tidy up FAQ --- website/src/pages/learn/faq.mdx | 93 --------------------------------- 1 file changed, 93 deletions(-) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index 30f2986bd..dbce4c701 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -308,96 +308,3 @@ _ before a stack name: mutes the stack, i.e. hush(), for example _$: s("bd") These pages have been taken from [this pad](https://doc.patternclub.org/_CgofWouTciXXHexUP9AVg?both). Some of the items there have not been brushed up and brought here. These include the following items: 9, 11, 12 and 19 - -# Coding Syntax (old Content from code) - -Let's take a step back and understand how the syntax in Strudel works. - -Take a look at this simple example: - - - -- We have a word `note` which is followed by some brackets `()` with some words/letters/numbers inside, surrounded by quotes `"c a f e"` -- Then we have a dot `.` followed by another similar piece of code `s("piano")`. -- We can also see these texts are _highlighted_ using colours: word `note` is purple, the brackets `()` are grey, and the content inside the `""` are green. (The colors could be different if you've changed the default theme) - -What happens if we try to 'break' this pattern in different ways? - - - - - - - -Ok, none of these seem to work... - - - -This one does work, but now we only hear the first note... - -So what is going on here? - -# Functions, arguments and chaining - -So far, we've seen the following syntax: - -``` -xxx("foo").yyy("bar") -``` - -Generally, `xxx` and `yyy` are called [_functions_](), while `foo` and `bar` are called function [_arguments_ or _parameters_](). -So far, we've used the functions to declare which aspect of the sound we want to control, and their arguments for the actual data. -The `yyy` function is called a [_chained_ function](https://en.wikipedia.org/wiki/Method_chaining), because it is preceded with a dot (`.`). - -Generally, the idea with chaining is that code such as `a("this").b("that").c("other")` allows `a`, `b` and `c` functions to happen in a specified order, without needing to write them as three separate lines of code. -You can think of this as being similar to chaining audio effects together using guitar pedals or digital audio effects. - -Strudel makes heavy use of chained functions. Here is a more sophisticated example: - - - -## Write your own chained function - -You can write your own chained function using `register`. Here's the above chain but registered as a reusable, chained function. - - pat - .s("sawtooth") - .cutoff(500) - //.delay(0.5) - .room(0.5) - ) -note("a3 c#4 e4 a4").effectChain()`} -/> - -Try adding `.rev()` after `effectChain()` to hear further effects added. - -# Comments - -The `//` in the example above is a line comment, resulting in the `delay` function being ignored. -It is a handy way to quickly turn code on and off. -Try uncommenting this line by deleting `//` and refreshing the pattern. -You can also use the keyboard shortcut `cmd-/` to toggle comments on and off. - -You might noticed that some comments in the REPL samples include some words starting with a "@", like `@by` or `@license`. -Those are just a convention to define some information about the music. We will talk about it in the [Music metadata](/learn/metadata) section. - -# Strings - -Ok, so what about the content inside the quotes (e.g. `"c a f e"`)? -In JavaScript, as in most programming languages, this content is referred to as being a [_string_](). -A string is simply a sequence of individual characters. -In TidalCycles, double quoted strings are used to write _patterns_ using the mini-notation, and you may hear the phrase _pattern string_ from time to time. -If you want to create a regular string and not a pattern, you can use single quotes, e.g. `'C minor'` will not be parsed as Mini Notation. - -The good news is, that this covers most of the JavaScript syntax needed for Strudel! - -
From 69c5df67f5cb01ca372afc3d42f3cd23f2985d62 Mon Sep 17 00:00:00 2001 From: scrappy_fiddler Date: Wed, 19 Nov 2025 21:40:48 +0100 Subject: [PATCH 021/124] FAQ is now prettier --- website/src/pages/learn/faq.mdx | 134 +++++++++++++++++++------------- 1 file changed, 78 insertions(+), 56 deletions(-) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index dbce4c701..923bb5f66 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -12,16 +12,20 @@ This page contains frequently asked questions. Usually, the topic is explained i ## Is Strudel/Tidal free? -Yes - there is no charge, this is a collective open source project, and the music you make with it is your own. +Yes - there is no charge, this is a collective open source project, and the music you make with it is your own. -However there are some caveats - the source code must stay free, i.e. you cannot distribute strudel or tidal as part of projects with incompatible licenses - see the [license]() for details. The contributed examples and tracks are also separately licensed, and must not e.g. be used to train AI models without permission. +However there are some caveats - the source code must stay free, i.e. you cannot distribute strudel or tidal as part of projects with incompatible licenses - see the [license](https://www.gnu.org/licenses/agpl-3.0.en.html) for details. The contributed examples and tracks are also separately licensed, and must not e.g. be used to train AI models without permission. ## How to record or export audio? There are multiple ways to record the audio -- and video -- output of Strudel: + - capture the raw stereo signal coming out of your web browser. + - use the alternative SuperDirt audio engine. Read [this page](/learn/input-output/#oscsuperdirtstrudeldirt) to know more about it. + - capture the audio/video stream using a capture tool such as [OBS](). + - don't record anything and code it again in front of your friends. You will need an external audio editor/DAW such as Reaper/Audacity/Ardour, etc. Strudel itself does not have functionality for exporting stems / individual tracks to an audio or midi file. Strudel is not a digital audio workstation and does not operate following the same principles shared by most traditional audio softwares. @@ -29,9 +33,10 @@ You will need an external audio editor/DAW such as Reaper/Audacity/Ardour, etc. ## Can I use strudel with my IDE? Yes you can. There are experimental modes, made by community members, for several IDEs such as: - - VS Code: [Strudel VS](): an experimental mode for Microsoft VSCode. A revived version of [TidalStrudel](), which is defunct. - - nvim: [strudel.nvim](https://github.com/gruvw/strudel.nvim) - + +- VS Code: [Strudel VS](https://marketplace.visualstudio.com/items?itemName=cmillsdev.strudelvs): an experimental mode for Microsoft VSCode. A revived version of [TidalStrudel](https://marketplace.visualstudio.com/items?itemName=roipoussiere.tidal-strudel), which is defunct. +- nvim: [strudel.nvim](https://github.com/gruvw/strudel.nvim) + ## How can I record samples? You can use your own samples with Strudel. There are multiple ways to load your sample collection. Some methods are good for quick experimentation, some others are good to share your audio collection with other musicians: @@ -41,10 +46,10 @@ You can use your own samples with Strudel. There are multiple ways to load your ## Can I use Strudel with AI/LLM tools? -You are free to do what you like with Strudel, within the terms of the free/open source AGPLv3 license. However as a community we are interested in exploring human creativity. AI is *way* over-hyped right now, including by people with very shady motives. Many in the community are very wary of people training models on their tunes that they've poured their love into. So please keep discussion and questions around AI and LLMs to channels dedicated to the topic and be fully respectful of other people's work. +You are free to do what you like with Strudel, within the terms of the free/open source AGPLv3 license. However as a community we are interested in exploring human creativity. AI is _way_ over-hyped right now, including by people with very shady motives. Many in the community are very wary of people training models on their tunes that they've poured their love into. So please keep discussion and questions around AI and LLMs to channels dedicated to the topic and be fully respectful of other people's work. Furthermore, tools like ChatGPT generally give wrong answers. Please don't ask the community to fix those answers for you, as generally they will be timewasting nonsense. - + Human questions only! ## How to run offline? @@ -53,7 +58,7 @@ Strudel works offline just fine! There are multiple techniques to run it yoursel ## How to change tempo? How do I translate BPM to cpm? -If you have your tempo in beats per minute and use 4 beats per cycle (e.g. if your track is in 4/4ths) then you can do `setcpm(BPM/4)` where BPM is your beats per minute. +If you have your tempo in beats per minute and use 4 beats per cycle (e.g. if your track is in 4/4ths) then you can do `setcpm(BPM/4)` where BPM is your beats per minute. If you have a different number of beats per bar or are using more or less beats per cycle (e.g. If you want to put only half a bar or two bars into one cycle), adjust accordingly. @@ -68,21 +73,24 @@ If you pop open the sidetab of strudel.cc (small white < on the right hand side) ## How do I use this exactly like a DAW? Short answer: you don't. - + Long answer: you can use Strudel to work along your creative work in a DAW. There are many ways to do so. - + If you want to emulate the functionality of a DAW in a live coding language, you'll have to identify the operations executed by the DAW (sequencing, repeating, applying filters and envelopes) and write code that is equivalent to these operations. You might then find that the typical DAW workflow is not really adapted to live coding (because, despite both being ways of making music on the computer, they are two very different tools) and adapt your way of proceeding to the medium of code. This might mean leaving more place to serendipity and writing code that you don't predict the output of. ## Why doesn't everyone just use a DAW? -There is no easy answer to this question. Here are some thoughts: - - Live coding tools such as Strudel are excellent for improvising music and visuals using a computer. DAWs are valuable and robust companions for other activities such as producing, mastering and mixing audio, among other usages. Using a tool does not exclude from using any another tool, just build a toolbox. - - Live coding has been practiced for quite some time as a performative activity. Artists like to show their screens while playing in front of an audience. It is an essential part of what they do, of the way they share their activity with everybody. - - Code is a human language, it is made for other humans to read it. You can read the code and enjoy the music too. It has meaning, value, and there might even be something poetic/important about it! - - Strudel is free and open source, you can inspect the code, reshape it, contribute to it if you can/want. It is not opaque and this matters for many people. There is no black box, no obscure abstractions, no business model or hidden features. We need open tools in the arts! - - Live coders don't shy away from using DAWs. They use them all of the time, especially when it makes their life easier for... live coding! - - Code is an artistic material like any other. There is something valuable in the process of making music through code. More generally speaking, it is nice to tackle creative problems through the use of a programming language: creative thinking, building up your own solutions, DIY approach to music-making, unexpected outcome of algorithms, funny human errors, etc. - - There are pianos and trumpets in your DAW: why do people continue playing the piano or the trumpet? Think of live coding tools as instruments that you activate through the act of programming. +There is no easy answer to this question. Here are some thoughts: + +- Live coding tools such as Strudel are excellent for improvising music and visuals using a computer. DAWs are valuable and robust companions for other activities such as producing, mastering and mixing audio, among other usages. Using a tool does not exclude from using any another tool, just build a toolbox. + +- Live coding has been practiced for quite some time as a performative activity. Artists like to show their screens while playing in front of an audience. It is an essential part of what they do, of the way they share their activity with everybody. + +- Code is a human language, it is made for other humans to read it. You can read the code and enjoy the music too. It has meaning, value, and there might even be something poetic/important about it! - Strudel is free and open source, you can inspect the code, reshape it, contribute to it if you can/want. It is not opaque and this matters for many people. There is no black box, no obscure abstractions, no business model or hidden features. We need open tools in the arts! - Live coders don't shy away from using DAWs. They use them all of the time, especially when it makes their life easier for... live coding! + +- Code is an artistic material like any other. There is something valuable in the process of making music through code. More generally speaking, it is nice to tackle creative problems through the use of a programming language: creative thinking, building up your own solutions, DIY approach to music-making, unexpected outcome of algorithms, funny human errors, etc. + +- There are pianos and trumpets in your DAW: why do people continue playing the piano or the trumpet? Think of live coding tools as instruments that you activate through the act of programming. ## Is it more efficient to use Strudel than a DAW? @@ -96,11 +104,11 @@ Strudel can send [MIDI and OSC](/learn/input-output/), which are protocols for c Another music software (or hardware!) can then listen to these messages and process them according to its capabilities. -A simple example would be to send livecoded audio to Ableton on different tracks and then use it to mix them. +A simple example would be to send livecoded audio to Ableton on different tracks and then use it to mix them. You could also send the MIDI of a sequenced pattern to Musescore and then have it transcribe your livecoded work as a musical score. -You could also send MIDI to your hardware synths because you think they sound better than the software synths built-in Strudel. +You could also send MIDI to your hardware synths because you think they sound better than the software synths built-in Strudel. ## How do I use this in my closed source webgame? @@ -113,8 +121,12 @@ You cannot, as there is no such place. For details regarding our stance towards ## How to play different patterns simultaneously? Using the $ operator, several patterns can be played at once: - + + See also [stack](intro/#combining-patterns) @@ -122,29 +134,36 @@ See also [stack](intro/#combining-patterns) With an additional underscore, a pattern can be muted. - + See also [hush](/learn/conditional-modifiers/#hush) ## How can I arrange in Strudel using `mask`? -With mini-notation, using the `<>` and `!` operators, you can try something like +With mini-notation, using the `<>` and `!` operators, you can try something like + ``` .mask("<0!24 1!40>") ``` + It mutes a pattern for 24 cycles and plays it for 40. You would gain 64 cycles total, a multiple of 2/4/8 commonly used in western music. If each cycle is a bar, as a starting point, you could write a mask like that for any pattern: + ``` -.mask("<0!16 0!16 0!16 0!16 0!16 0!10>") +.mask("<0!16 0!16 0!16 0!16 0!16 0!10>") ``` + It mutes it throughout. For arranging, you could add the same mask to each part and replace some zeroes with ones in your different masks to make parts play. If you use `.mask()` on different patterns mess up your counting, then patterns do not align anymore. -On the other hand, doing that on purpose is one of the things that could be considered a strength of tidalcycles and Strudel. +On the other hand, doing that on purpose is one of the things that could be considered a strength of tidalcycles and Strudel. You can make things quite lively and more organic with a little (controlled) interference, according to your own taste. And you are free to arrange in cycles like 3, 6 or 9 too. @@ -159,8 +178,9 @@ This would lowpass filter sweep everything every 8 cycles. ## How can I arrange in Strudel using `arrange` or `pick`? Take [Pachelbel's Canon in D](https://en.wikipedia.org/wiki/Pachelbel%27s_Canon#Analysis) as an example which has 4 voices (one cello and 3 violins) which have repeating patterns, as seen in the link above. - + The following snipped defines the patterns as constants which can then be used for the different voices. `arrange` takes multiple arguments, which are each a number of cycles and a pattern which is played for the number of cycles, wrapped in `[]` If the pattern is shorter than the number, it is repeated. + ") .color("grey").sound("gm_tremolo_strings:3") @@ -177,34 +197,34 @@ The following snipped defines the patterns as constants which can then be used f "<[f#4 d4 e4 c#5 d5 f#5 a5 a4] [b4 g4 a4 f#4 d4 d5 [d5@3 c#5]@2]>") .color("red") - - cello$: arrange( +cello$: arrange( [2, silence], [18,cello]) violin1$: arrange( - [4,silence], - [2,violin_p1], [2,violin_p2], - [2,violin_p3], [2,violin_p4], - [2,violin_p1], [2,violin_p2], - [2,violin_p3], [2,violin_p4] - ).sound("gm_tremolo_strings:0") - violin2$: arrange( +[4,silence], +[2,violin_p1], [2,violin_p2], +[2,violin_p3], [2,violin_p4], +[2,violin_p1], [2,violin_p2], +[2,violin_p3], [2,violin_p4] +).sound("gm_tremolo_strings:0") +violin2$: arrange( [6,silence], [2,violin_p1], [2,violin_p2], [2,violin_p3], [2,violin_p4], [2,violin_p1], [2,violin_p2], [2,violin_p3] ).sound("gm_tremolo_strings:1") violin3$: arrange( - [8,silence], - [2,violin_p1], [2,violin_p2], - [2,violin_p3], [2,violin_p4], - [2,violin_p1], [2,violin_p2] - ).sound("gm_tremolo_strings:2") +[8,silence], +[2,violin_p1], [2,violin_p2], +[2,violin_p3], [2,violin_p4], +[2,violin_p1], [2,violin_p2] +).sound("gm_tremolo_strings:2") all(x => x.release(.2)) + `} /> -Alternatively, you can also put the different patterns for the violins into one single array (`const violins = [violin_p1, violin_p2, violin_p3, violin_p4]`) and use a pattern as an index to `pick` the nth element of that array. This replaces the voices defined above. Here you use `0@2` to specifiy that the first item (i.e. with index `0`) is played for `2` cycles. +Alternatively, you can also put the different patterns for the violins into one single array (`const violins = [violin_p1, violin_p2, violin_p3, violin_p4]`) and use a pattern as an index to `pick` the nth element of that array. This replaces the voices defined above. Here you use `0@2` to specifiy that the first item (i.e. with index `0`) is played for `2` cycles. `pick` has better highlighting than `arrange`: @@ -231,18 +251,18 @@ violin1$: "<~@4 0@2 1@2 2@2 3@2 0@2 1@2 2@2 3@2>".pick(violins) .sound("gm_tremolo_strings:0") violin2$: "<~@6 0@2 1@2 2@2 3@2 0@2 1@2 2@2>".pick(violins) .sound("gm_tremolo_strings:1") -violin3$: "<~@8 0@2 1@2 2@2 3@2 0@2 1@2 >".pick(violins) +violin3$: "<~@8 0@2 1@2 2@2 3@2 0@2 1@2 >".pick(violins) .sound("gm_tremolo_strings:2") all(x => x.release(.2)) -`} /> - +`} /> + The `pick` method also works with jsons which have named elements, which makes it easier to read, see the [here](/learn/conditional-modifiers/#pick). `pickRestart` restarts the pattern upon picking it which can make a difference if the duration of the pick indexes doesn't line up with the patterns which are picked - which is not the case here. Try adding `.punchcard()` after the `release(.2)` for a visualization. ## I saw Switch Angel using functions which I cannot find in the reference (e.g. `trancegate`). How do I make it work? -Methods like `trancegate()`, `rlpf()` and `acidenv()` are currently not pattern methods which come natively with strudel. +Methods like `trancegate()`, `rlpf()` and `acidenv()` are currently not pattern methods which come natively with strudel. It's part of a script for strudel which was written by Switch Angel and published [here](https://github.com/switchangel/strudel-scripts/blob/main/allscripts.js) @@ -255,20 +275,22 @@ If you paste these functions into your strudel session, hit "Update" and delete ## Is there difference between `n` and `note`? They are not aliases of each other, in contrast to `s` and `sound`. - -The method `note` is used to reference a certain note (either as its name, such as `c` or `b2` or the midi number `69`, for example `note("c3 e3 g3")`). - -On the other hand, `n` is a way to reference the nth index of something. This something can be a scale (eg `n("0 2 4").scale("C:major")`) , but it can also be a particular note in a chord (see https://strudel.cc/recipes/recipes/#arpeggios for an example) . -The method `n` can also be used for something completely unrelated to notes like the nth sample from a sample map `s("hh*8").bank("RolandTR909").n("0 1 2 3")`. Note that `pick` does *not* use `n`. +The method `note` is used to reference a certain note (either as its name, such as `c` or `b2` or the midi number `69`, for example `note("c3 e3 g3")`). -".pickRestart([ +On the other hand, `n` is a way to reference the nth index of something. This something can be a scale (eg `n("0 2 4").scale("C:major")`) , but it can also be a particular note in a chord (see https://strudel.cc/recipes/recipes/#arpeggios for an example) . + +The method `n` can also be used for something completely unrelated to notes like the nth sample from a sample map `s("hh*8").bank("RolandTR909").n("0 1 2 3")`. Note that `pick` does _not_ use `n`. + +".pickRestart([ n("0 1 2 0"), n("2 3 4 ~"), n("[4 5] [4 3] 2 0"), n("0 -3 0 ~") - ]).scale("C:major").s("piano")`} /> - + ]).scale("C:major").s("piano")`} +/>{' '} ## Is there a cheat sheet for all symbols? @@ -305,6 +327,6 @@ _ before a stack name: mutes the stack, i.e. hush(), for example _$: s("bd") ## Are there more FAQ items? -These pages have been taken from [this pad](https://doc.patternclub.org/_CgofWouTciXXHexUP9AVg?both). Some of the items there have not been brushed up and brought here. +These pages have been taken from [this pad](https://doc.patternclub.org/_CgofWouTciXXHexUP9AVg?both). Some of the items there have not been brushed up and brought here. These include the following items: 9, 11, 12 and 19 From 4504d1afd0f893f696600493c23dff122c06c6ca Mon Sep 17 00:00:00 2001 From: scrappy_fiddler Date: Wed, 19 Nov 2025 22:14:23 +0100 Subject: [PATCH 022/124] remove trailing spaces in FAQ --- website/src/pages/learn/faq.mdx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index 923bb5f66..2e4cb5c4b 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -42,6 +42,7 @@ Yes you can. There are experimental modes, made by community members, for severa You can use your own samples with Strudel. There are multiple ways to load your sample collection. Some methods are good for quick experimentation, some others are good to share your audio collection with other musicians: - Import folders [from the interface](/learn/samples/#from-disk-via-import-sounds-folder) + - - Host your sound library e.g. on github and [load them from an URL](/learn/samples/#loading-custom-samples) ## Can I use Strudel with AI/LLM tools? @@ -80,15 +81,15 @@ If you want to emulate the functionality of a DAW in a live coding language, you ## Why doesn't everyone just use a DAW? -There is no easy answer to this question. Here are some thoughts: +There is no easy answer to this question. Here are some thoughts: -- Live coding tools such as Strudel are excellent for improvising music and visuals using a computer. DAWs are valuable and robust companions for other activities such as producing, mastering and mixing audio, among other usages. Using a tool does not exclude from using any another tool, just build a toolbox. +- Live coding tools such as Strudel are excellent for improvising music and visuals using a computer. DAWs are valuable and robust companions for other activities such as producing, mastering and mixing audio, among other usages. Using a tool does not exclude from using any another tool, just build a toolbox. -- Live coding has been practiced for quite some time as a performative activity. Artists like to show their screens while playing in front of an audience. It is an essential part of what they do, of the way they share their activity with everybody. +- Live coding has been practiced for quite some time as a performative activity. Artists like to show their screens while playing in front of an audience. It is an essential part of what they do, of the way they share their activity with everybody. -- Code is a human language, it is made for other humans to read it. You can read the code and enjoy the music too. It has meaning, value, and there might even be something poetic/important about it! - Strudel is free and open source, you can inspect the code, reshape it, contribute to it if you can/want. It is not opaque and this matters for many people. There is no black box, no obscure abstractions, no business model or hidden features. We need open tools in the arts! - Live coders don't shy away from using DAWs. They use them all of the time, especially when it makes their life easier for... live coding! +- Code is a human language, it is made for other humans to read it. You can read the code and enjoy the music too. It has meaning, value, and there might even be something poetic/important about it! - Strudel is free and open source, you can inspect the code, reshape it, contribute to it if you can/want. It is not opaque and this matters for many people. There is no black box, no obscure abstractions, no business model or hidden features. We need open tools in the arts! - Live coders don't shy away from using DAWs. They use them all of the time, especially when it makes their life easier for... live coding! -- Code is an artistic material like any other. There is something valuable in the process of making music through code. More generally speaking, it is nice to tackle creative problems through the use of a programming language: creative thinking, building up your own solutions, DIY approach to music-making, unexpected outcome of algorithms, funny human errors, etc. +- Code is an artistic material like any other. There is something valuable in the process of making music through code. More generally speaking, it is nice to tackle creative problems through the use of a programming language: creative thinking, building up your own solutions, DIY approach to music-making, unexpected outcome of algorithms, funny human errors, etc. - There are pianos and trumpets in your DAW: why do people continue playing the piano or the trumpet? Think of live coding tools as instruments that you activate through the act of programming. From 4ec68fcd1de98ba0cd1d0fb22de84ecddfb15d1a Mon Sep 17 00:00:00 2001 From: scrappy_fiddler Date: Thu, 20 Nov 2025 00:07:44 +0100 Subject: [PATCH 023/124] a little prettier --- website/src/pages/learn/faq.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index 2e4cb5c4b..b16eb8cdd 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -42,7 +42,7 @@ Yes you can. There are experimental modes, made by community members, for severa You can use your own samples with Strudel. There are multiple ways to load your sample collection. Some methods are good for quick experimentation, some others are good to share your audio collection with other musicians: - Import folders [from the interface](/learn/samples/#from-disk-via-import-sounds-folder) - - + - - Host your sound library e.g. on github and [load them from an URL](/learn/samples/#loading-custom-samples) ## Can I use Strudel with AI/LLM tools? From 8529516c663ee8c34b8ef108a148c56c1b690fa6 Mon Sep 17 00:00:00 2001 From: Aria Date: Tue, 25 Nov 2025 13:05:27 -0600 Subject: [PATCH 024/124] Add ability to turn mini parsing off with midi-off decorator --- packages/transpiler/test/transpiler.test.mjs | 14 ++++++++ packages/transpiler/transpiler.mjs | 38 ++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/packages/transpiler/test/transpiler.test.mjs b/packages/transpiler/test/transpiler.test.mjs index 02970cf43..68bec9154 100644 --- a/packages/transpiler/test/transpiler.test.mjs +++ b/packages/transpiler/test/transpiler.test.mjs @@ -42,4 +42,18 @@ describe('transpiler', () => { [12, 14], ]); }); + it('allows disabling mini', () => { + const code = `/* mini-off */ + const randPrefix = Math.random() > 0.5 ? "b" : "s"; + const drumPat = \`\${randPrefix}d\`; + // mini-on + s(drumPat).lpf("5000 10000") // make sure mini still runs; + `; + const { output, miniLocations } = transpiler(code, { ...simple, emitMiniLocations: true }); + expect(output).not.toContain("m('b'"); + expect(output).not.toContain("m('s'"); + const cutoffIdx = code.indexOf('5000 10000'); + expect(miniLocations).toHaveLength(2); + expect(miniLocations[0][0]).toEqual(cutoffIdx); + }); }); diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index fea6bad4d..b394591c2 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -21,12 +21,15 @@ export function registerLanguage(type, config) { export function transpiler(input, options = {}) { const { wrapAsync = false, addReturn = true, emitMiniLocations = true, emitWidgets = true } = options; + const comments = []; let ast = parse(input, { ecmaVersion: 2022, allowAwaitOutsideFunction: true, locations: true, + onComment: comments, }); + const miniDisableRanges = findMiniDisableRanges(comments, input.length); let miniLocations = []; const collectMiniLocations = (value, node) => { const minilang = languages.get('minilang'); @@ -66,6 +69,9 @@ export function transpiler(input, options = {}) { return this.replace(tidalWithLocation(raw, offset)); } if (isBackTickString(node, parent)) { + if (isMiniDisabled(node.start, miniDisableRanges)) { + return; + } const { quasis } = node; const { raw } = quasis[0].value; this.skip(); @@ -73,6 +79,9 @@ export function transpiler(input, options = {}) { return this.replace(miniWithLocation(raw, node)); } if (isStringWithDoubleQuotes(node)) { + if (isMiniDisabled(node.start, miniDisableRanges)) { + return; + } const { value } = node; this.skip(); emitMiniLocations && collectMiniLocations(value, node); @@ -327,3 +336,32 @@ function languageWithLocation(name, value, offset) { optional: false, }; } + +function findMiniDisableRanges(comments, codeEnd) { + const ranges = []; + const stack = []; // used to track on/off pairs + for (const comment of comments) { + const value = comment.value.trim(); + if (value.startsWith('mini-off')) { + stack.push(comment.start); + } else if (value.startsWith('mini-on')) { + const start = stack.pop(); + ranges.push([start, comment.end]); + } + } + while (stack.length) { + // If no closing mini-on is found, just turn it off until `codeEnd` + const start = stack.pop(); + ranges.push([start, codeEnd]); + } + return ranges; +} + +function isMiniDisabled(offset, miniDisableRanges) { + for (const [start, end] of miniDisableRanges) { + if (offset >= start && offset < end) { + return true; + } + } + return false; +} From 39bff8900f695b80e4c5a666a62a33a186e58ff0 Mon Sep 17 00:00:00 2001 From: scrappy_fiddler Date: Mon, 1 Dec 2025 18:22:26 +0100 Subject: [PATCH 025/124] website/src/pages/learn/faq.mdx aktualisiert updated the section on switch angel's prebake. Now refers to the readme for instructions. --- website/src/pages/learn/faq.mdx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index b16eb8cdd..c22391330 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -265,13 +265,9 @@ Try adding `.punchcard()` after the `release(.2)` for a visualization. Methods like `trancegate()`, `rlpf()` and `acidenv()` are currently not pattern methods which come natively with strudel. -It's part of a script for strudel which was written by Switch Angel and published [here](https://github.com/switchangel/strudel-scripts/blob/main/allscripts.js) +They are part of a script/prebake for strudel which was written by Switch Angel and published [here](https://github.com/switchangel/strudel-scripts) -Note that this script defines functions and registers methods which `trancegate()` will depend on so just pasting the `trancegate()` method from that script alone will not suffice, but you will also need the `fill` method. - -If you copy the full script into your strudel, be mindful of lines which try to load local samples - these should be deleted or prefixed with `//` as a comment. - -If you paste these functions into your strudel session, hit "Update" and delete the source of the functions, the functions will still be availabe until you do a browser refresh. +You can find the instructions how to use that script in the readme.md there. ## Is there difference between `n` and `note`? From b9fce1504276215bd9b280b69ab1214f6d341162 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Wed, 3 Dec 2025 15:18:32 -0500 Subject: [PATCH 026/124] working --- packages/codemirror/codemirror.mjs | 9 +++++++++ packages/codemirror/labelJump.mjs | 31 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 packages/codemirror/labelJump.mjs diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 69fce4b50..6643e400d 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -24,6 +24,7 @@ import { sliderPlugin, updateSliderWidgets } from './slider.mjs'; import { activateTheme, initTheme, theme } from './themes.mjs'; import { isTooltipEnabled } from './tooltip.mjs'; import { updateWidgets, widgetPlugin } from './widget.mjs'; +import { jumpToCharacter } from './labelJump.mjs'; export { toggleBlockComment, toggleBlockCommentByLine, toggleComment, toggleLineComment } from '@codemirror/commands'; @@ -119,6 +120,14 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, roo preventDefault: true, run: () => onStop?.(), }, + { + key: 'Alt-]', + run: (view) => jumpToCharacter(view, '$', 1), + }, + { + key: 'Alt-[', + run: (view) => jumpToCharacter(view, '$', -1), + }, /* { key: 'Ctrl-Shift-.', run: () => (onPanic ? onPanic() : onStop?.()), diff --git a/packages/codemirror/labelJump.mjs b/packages/codemirror/labelJump.mjs new file mode 100644 index 000000000..fcdb437f5 --- /dev/null +++ b/packages/codemirror/labelJump.mjs @@ -0,0 +1,31 @@ +import { EditorSelection } from '@codemirror/state'; +import { SearchCursor } from '@codemirror/search'; + +export function jumpToCharacter(view, character, direction = 1) { + const { state, dispatch } = view; + const pos = state.selection.main.head; + const cursor = new SearchCursor(state.doc, character); + + let characterPositions = []; + let jumpPos; + while (!cursor.next().done) { + characterPositions.push(cursor.value.to); + } + if (!characterPositions.length) { + return false; + } + if (direction > 0) { + jumpPos = characterPositions.find((x) => x > pos + 1) ?? characterPositions.at(0); // Loop back around for convenience + } else { + jumpPos = characterPositions.reverse().find((x) => x < pos + 1) ?? characterPositions.at(0); + } + + if (!jumpPos) { + return false; + } + dispatch({ + selection: EditorSelection.cursor(jumpPos - 1), + scrollIntoView: true, + }); + return true; +} From c1a185e852433d4469e25e6b07a2eec64b36d08d Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Wed, 3 Dec 2025 15:25:05 -0500 Subject: [PATCH 027/124] fix null case --- packages/codemirror/labelJump.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/codemirror/labelJump.mjs b/packages/codemirror/labelJump.mjs index fcdb437f5..0aced4eb5 100644 --- a/packages/codemirror/labelJump.mjs +++ b/packages/codemirror/labelJump.mjs @@ -20,7 +20,7 @@ export function jumpToCharacter(view, character, direction = 1) { jumpPos = characterPositions.reverse().find((x) => x < pos + 1) ?? characterPositions.at(0); } - if (!jumpPos) { + if (jumpPos == null) { return false; } dispatch({ From f0aac2098afba2ba10d1f8d88db70c988c6b3496 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Wed, 3 Dec 2025 22:59:20 -0500 Subject: [PATCH 028/124] update_shortcut --- packages/codemirror/codemirror.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 6643e400d..ba481d9ac 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -121,11 +121,11 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, roo run: () => onStop?.(), }, { - key: 'Alt-]', + key: 'Alt-w', run: (view) => jumpToCharacter(view, '$', 1), }, { - key: 'Alt-[', + key: 'Alt-q', run: (view) => jumpToCharacter(view, '$', -1), }, /* { From 600ab0a83e4edd4a8650ad44546de15448e82276 Mon Sep 17 00:00:00 2001 From: Aria Date: Thu, 4 Dec 2025 12:17:07 -0600 Subject: [PATCH 029/124] WIP with compressors, filters, heavy worklets --- packages/superdough/audioContext.mjs | 9 +++ packages/superdough/nodePools.mjs | 70 ++++++++++++++++++++++++ packages/superdough/superdough.mjs | 7 ++- packages/superdough/superdoughoutput.mjs | 9 +++ packages/superdough/synth.mjs | 38 +++++++------ packages/superdough/wavetable.mjs | 46 +++++++++------- packages/superdough/worklets.mjs | 68 ++++++++++------------- 7 files changed, 166 insertions(+), 81 deletions(-) create mode 100644 packages/superdough/nodePools.mjs diff --git a/packages/superdough/audioContext.mjs b/packages/superdough/audioContext.mjs index 71e01d57d..3523f6dfe 100644 --- a/packages/superdough/audioContext.mjs +++ b/packages/superdough/audioContext.mjs @@ -1,3 +1,12 @@ +/* +audioContext.mjs - Audio Context manager + +Sets up a common and accessible audio context for all superdough operations + +Copyright (C) 2025 Strudel contributors - see +This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ + let audioContext; export const setDefaultAudioContext = () => { diff --git a/packages/superdough/nodePools.mjs b/packages/superdough/nodePools.mjs new file mode 100644 index 000000000..ae59a2ce9 --- /dev/null +++ b/packages/superdough/nodePools.mjs @@ -0,0 +1,70 @@ +/* +nodePools.mjs - Helper functions related to pooling and re-using audio nodes + +Copyright (C) 2025 Strudel contributors - see +This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ + +const nodePools = new Map(); +const POOL_KEY = Symbol('nodePoolKey'); +const IS_WORKLET_DEAD = Symbol('nodePoolIsWorkletDead'); +const MAX_POOL_SIZE = 64; + +export const isPoolable = (node) => !!node[POOL_KEY]; + +const getParams = (node) => { + const params = new Set(); + node.parameters?.forEach((param) => params.add(param)); + const visited = new Set(); // prioritize deepest definition + let proto = node; + // Move up the prototype chain + while (proto !== Object.prototype) { + for (const key of Object.getOwnPropertyNames(proto)) { + if (visited.has(key)) continue; + visited.add(key); + const value = node[key]; + if (value instanceof AudioParam) { + params.add(value); + } + } + proto = Object.getPrototypeOf(proto); + } + return params; +}; + +export const releaseNodeToPool = (node) => { + node.disconnect(); + if (node instanceof AudioScheduledSourceNode) { + // not reusable + return; + } + // Fallback to a type-based key if the node was not created via getNodeFromPool + const key = node[POOL_KEY]; + if (key == null) return; + const now = node.context?.currentTime ?? 0; + getParams(node).forEach((param) => param.cancelScheduledValues(now)); + const pool = nodePools.get(key) ?? []; + if (pool.length < MAX_POOL_SIZE) { + pool.push(new WeakRef(node)); + nodePools.set(key, pool); + } +}; + +export const markWorkletAsDead = (worklet) => (worklet[IS_WORKLET_DEAD] = true); + +// Attempt to get node from the pool. If this fails, fall back +// to building it with the factory +export const getNodeFromPool = (key, factory) => { + const pool = nodePools.get(key) ?? []; + let node; + while (pool.length) { + const ref = pool.pop(); + node = ref?.deref(); + if (node != null && !node[IS_WORKLET_DEAD]) break; + } + if (node == null || node[IS_WORKLET_DEAD]) { + node = factory(); + } + node[POOL_KEY] = key; + return node; +}; diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 6f610d514..57279aac2 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -9,7 +9,8 @@ import './reverb.mjs'; import './vowel.mjs'; import { nanFallback, _mod, cycleToSeconds, pickAndRename } from './util.mjs'; import workletsUrl from './worklets.mjs?audioworklet'; -import { createFilter, gainNode, getCompressor, getDistortion, getLfo, getWorklet, effectSend } from './helpers.mjs'; +import { createFilter, effectSend, gainNode, getCompressor, getDistortion, getLfo, getWorklet } from './helpers.mjs'; +import { getNodeFromPool, isPoolable, releaseNodeToPool } from './nodePools.mjs'; import { map } from 'nanostores'; import { logger } from './logger.mjs'; import { loadBuffer } from './sampler.mjs'; @@ -294,7 +295,7 @@ function getPhaser(time, end, frequency = 1, depth = 0.5, centerFrequency = 1000 let fOffset = 0; const filterChain = []; for (let i = 0; i < numStages; i++) { - const filter = ac.createBiquadFilter(); + const filter = getNodeFromPool('filter', () => ac.createBiquadFilter()); filter.type = 'notch'; filter.gain.value = 1; filter.frequency.value = centerFrequency + fOffset; @@ -506,7 +507,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) } else if (getSound(s)) { const { onTrigger } = getSound(s); const onEnded = () => { - audioNodes.forEach((n) => n?.disconnect()); + audioNodes.forEach((n) => (isPoolable(n) ? releaseNodeToPool(n) : n?.disconnect())); activeSoundSources.delete(chainID); }; const soundHandle = await onTrigger(t, value, onEnded, cps); diff --git a/packages/superdough/superdoughoutput.mjs b/packages/superdough/superdoughoutput.mjs index d8ead7d06..7bb4864f3 100644 --- a/packages/superdough/superdoughoutput.mjs +++ b/packages/superdough/superdoughoutput.mjs @@ -1,3 +1,12 @@ +/* +superdoughoutput.mjs - Output controller for superdough + +Handles setting up and mixing to the outputs as well as all global (orbit) effects + +Copyright (C) 2025 Strudel contributors - see +This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ + import { effectSend, getWorklet, webAudioTimeout } from './helpers.mjs'; import { errorLogger } from './logger.mjs'; import { clamp } from './util.mjs'; diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index b02354465..87b25ff22 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -17,6 +17,7 @@ import { } from './helpers.mjs'; import { logger } from './logger.mjs'; import { getNoiseMix, getNoiseOscillator } from './noise.mjs'; +import { getNodeFromPool, markWorkletAsDead, releaseNodeToPool } from './nodePools.mjs'; const waveforms = ['triangle', 'square', 'sawtooth', 'sine', 'user']; const waveformAliases = [ @@ -164,22 +165,25 @@ export function registerSynthSounds() { const end = holdend + release + 0.01; const voices = clamp(unison, 1, 100); let panspread = voices > 1 ? clamp(spread, 0, 1) : 0; - let o = getWorklet( - ac, - 'supersaw-oscillator', - { - frequency, - begin, - end, - freqspread: detune, - voices, - panspread, - }, - { - outputChannelCount: [2], - }, - ); - + const params = { + frequency, + begin, + end: end + 0.5, // add a grace period for pooling + freqspread: detune, + voices, + panspread, + }; + const factory = () => new AudioWorkletNode(ac, 'supersaw-oscillator', { outputChannelCount: [2] }); + const o = getNodeFromPool('supersaw', factory); + Object.entries(params).forEach(([key, value]) => { + if (value !== undefined) { + o.parameters.get(key).value = value; + } + }); + o.port.postMessage({ type: 'initialize' }); + o.port.onMessage = (e) => { + if (e.data.type === 'died') markWorkletAsDead(o); + }; const gainAdjustment = 1 / Math.sqrt(voices); getPitchEnvelope(o.parameters.get('detune'), value, begin, holdend); const vibratoOscillator = getVibratoOscillator(o.parameters.get('detune'), value, begin); @@ -192,7 +196,7 @@ export function registerSynthSounds() { let timeoutNode = webAudioTimeout( ac, () => { - releaseAudioNode(o); + releaseNodeToPool(o); onended(); fm?.stop(); vibratoOscillator?.stop(); diff --git a/packages/superdough/wavetable.mjs b/packages/superdough/wavetable.mjs index e9d810414..9545fa1bb 100644 --- a/packages/superdough/wavetable.mjs +++ b/packages/superdough/wavetable.mjs @@ -8,10 +8,9 @@ import { getParamADSR, getPitchEnvelope, getVibratoOscillator, - getWorklet, - releaseAudioNode, webAudioTimeout, } from './helpers.mjs'; +import { getNodeFromPool, markWorkletAsDead, releaseNodeToPool } from './nodePools.mjs'; import { logger } from './logger.mjs'; export const Warpmode = Object.freeze({ @@ -225,24 +224,29 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) { } const endWithRelease = holdEnd + release; const envEnd = endWithRelease + 0.01; - const source = getWorklet( - ac, - 'wavetable-oscillator-processor', - { - begin: t, - end: envEnd, - frequency, - freqspread: value.detune, - position: value.wt, - warp: value.warp, - warpMode: warpmode, - voices: Math.max(value.unison ?? 1, 1), - panspread: value.spread, - phaserand: (value.wtphaserand ?? value.unison > 1) ? 1 : 0, - }, - { outputChannelCount: [2] }, - ); - source.port.postMessage({ type: 'table', payload }); + const params = { + begin: t, + end: envEnd + 0.5, // add a grace period for pooling + frequency, + freqspread: value.detune, + position: value.wt, + warp: value.warp, + warpMode: warpmode, + voices: Math.max(value.unison ?? 1, 1), + panspread: value.spread, + phaserand: (value.wtphaserand ?? value.unison > 1) ? 1 : 0, + }; + const factory = () => new AudioWorkletNode(ac, 'wavetable-oscillator-processor', { outputChannelCount: [2] }); + const source = getNodeFromPool('wavetable', factory); + Object.entries(params).forEach(([key, value]) => { + if (value !== undefined) { + source.parameters.get(key).value = value; + } + }); + source.port.postMessage({ type: 'initialize', payload }); + source.port.onMessage = (e) => { + if (e.data.type === 'died') markWorkletAsDead(source); + }; if (ac.currentTime > t) { logger(`[wavetable] still loading sound "${s}:${n}"`, 'highlight'); return; @@ -319,7 +323,7 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) { const timeoutNode = webAudioTimeout( ac, () => { - releaseAudioNode(source); + releaseNodeToPool(source); vibratoOscillator?.stop(); fm?.stop(); wtPosModulators?.disconnect(); diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 2f75453b9..2a07e56ce 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -458,6 +458,15 @@ registerProcessor('distort-processor', DistortProcessor); class SuperSawOscillatorProcessor extends AudioWorkletProcessor { constructor() { super(); + this.port.onmessage = (e) => { + const { type, payload } = e.data || {}; + if (type === 'initialize') { + this.initialize(payload); + } + }; + this.initialize(); + } + initialize(_options) { this.phase = []; } static get parameterDescriptors() { @@ -1135,37 +1144,27 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor { constructor(options) { super(options); + this.port.onmessage = (e) => { + const { type, payload } = e.data || {}; + if (type === 'initialize') { + this.initialize(payload); + } + }; + this.initialize(); + } + initialize(options) { this.frameLen = 0; this.numFrames = 0; this.phase = []; - - this.port.onmessage = (e) => { - const { type, payload } = e.data || {}; - if (type === 'table') { - const key = payload.key; - this.frameLen = payload.frameLen; - if (!tablesCache[key]) { - const tables = [payload.frames]; - let table = tables[0]; - for (let level = 1; level < 1; level++) { - const nextLen = table.length >> 1; - const nextTable = table.map((frame) => { - const avg = new Float32Array(nextLen); - for (let i = 0; i < nextLen; i++) { - avg[i] = (frame[2 * i] + frame[2 * i + 1]) / 2; - } - return avg; - }); - tables.push(nextTable); - table = nextTable; - if (nextLen <= 32) break; - } - tablesCache[key] = tables; - } - this.tables = tablesCache[key]; - this.numFrames = this.tables[0].length; + if (options?.frames) { + const key = options.key; + this.frameLen = options.frameLen; + if (!tablesCache[key]) { + tablesCache[key] = options.frames; } - }; + this.table = tablesCache[key]; + this.numFrames = this.table.length; + } } _mirror(x) { @@ -1310,15 +1309,6 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor { return a + (b - a) * frac; } - _chooseMip(dphi) { - const approxHarm = clamp(dphi, 1e-6, 64); - let level = 0; - while (level + 1 < (this.tables?.length || 1) && approxHarm < this.tables[level][0].length / 8) { - level++; - } - return level; - } - process(_inputs, outputs, parameters) { if (currentTime >= parameters.end[0]) { return false; @@ -1362,14 +1352,12 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor { } const fVoice = applySemitoneDetuneToFrequency(f, detuner(n)); // voice detune const dPhase = fVoice * INVSR; - const level = this._chooseMip(dPhase); - const table = this.tables[level]; // warp phase then sample this.phase[n] = this.phase[n] ?? Math.random() * phaseRand; const ph = this._warpPhase(this.phase[n], warpAmount, warpMode); - const s0 = this._sampleFrame(table[fIdx], ph); - const s1 = this._sampleFrame(table[Math.min(this.numFrames - 1, fIdx + 1)], ph); + const s0 = this._sampleFrame(this.table[fIdx], ph); + const s1 = this._sampleFrame(this.table[Math.min(this.numFrames - 1, fIdx + 1)], ph); let s = lerp(s0, s1, interpT); if (warpMode === WarpMode.FLIP && this.phase[n] < warpAmount) { s = -s; From 66107130187ba248360a8381a5f1824fad5cd428 Mon Sep 17 00:00:00 2001 From: Aria Date: Thu, 4 Dec 2025 12:40:28 -0600 Subject: [PATCH 030/124] Working version of supersaw, almost on wavetable --- packages/superdough/nodePools.mjs | 4 ++++ packages/superdough/synth.mjs | 3 ++- packages/superdough/wavetable.mjs | 3 ++- packages/superdough/worklets.mjs | 17 ++++++++++++++--- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/superdough/nodePools.mjs b/packages/superdough/nodePools.mjs index ae59a2ce9..08ec9ce51 100644 --- a/packages/superdough/nodePools.mjs +++ b/packages/superdough/nodePools.mjs @@ -38,6 +38,10 @@ export const releaseNodeToPool = (node) => { // not reusable return; } + if (node[IS_WORKLET_DEAD]) { + // Worklet already terminated, don't pool it + return; + } // Fallback to a type-based key if the node was not created via getNodeFromPool const key = node[POOL_KEY]; if (key == null) return; diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 87b25ff22..b10136abf 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -181,8 +181,9 @@ export function registerSynthSounds() { } }); o.port.postMessage({ type: 'initialize' }); - o.port.onMessage = (e) => { + o.port.onmessage = (e) => { if (e.data.type === 'died') markWorkletAsDead(o); + o.port.onmessage = null; }; const gainAdjustment = 1 / Math.sqrt(voices); getPitchEnvelope(o.parameters.get('detune'), value, begin, holdend); diff --git a/packages/superdough/wavetable.mjs b/packages/superdough/wavetable.mjs index 9545fa1bb..a74b3747c 100644 --- a/packages/superdough/wavetable.mjs +++ b/packages/superdough/wavetable.mjs @@ -244,8 +244,9 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) { } }); source.port.postMessage({ type: 'initialize', payload }); - source.port.onMessage = (e) => { + source.port.onmessage = (e) => { if (e.data.type === 'died') markWorkletAsDead(source); + source.port.onmessage = null; }; if (ac.currentTime > t) { logger(`[wavetable] still loading sound "${s}:${n}"`, 'highlight'); diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 2a07e56ce..73fbe9456 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -458,6 +458,7 @@ registerProcessor('distort-processor', DistortProcessor); class SuperSawOscillatorProcessor extends AudioWorkletProcessor { constructor() { super(); + this.isAlive = true; // used internally to prevent multiple death messages this.port.onmessage = (e) => { const { type, payload } = e.data || {}; if (type === 'initialize') { @@ -519,6 +520,10 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor { process(_input, outputs, params) { if (currentTime >= params.end[0]) { // should terminate + if (this.isAlive) { + this.port.postMessage({ type: 'died' }); + this.isAlive = false; + } return false; } if (currentTime <= params.begin[0]) { @@ -1144,6 +1149,7 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor { constructor(options) { super(options); + this.isAlive = true; // used internally to prevent multiple death messages this.port.onmessage = (e) => { const { type, payload } = e.data || {}; if (type === 'initialize') { @@ -1153,8 +1159,9 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor { this.initialize(); } initialize(options) { - this.frameLen = 0; - this.numFrames = 0; + this.table = null; + this.frameLen = null; + this.numFrames = null; this.phase = []; if (options?.frames) { const key = options.key; @@ -1311,6 +1318,10 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor { process(_inputs, outputs, parameters) { if (currentTime >= parameters.end[0]) { + if (this.isAlive) { + this.port.postMessage({ type: 'died' }); + this.isAlive = false; + } return false; } if (currentTime <= parameters.begin[0]) { @@ -1318,7 +1329,7 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor { } const outL = outputs[0][0]; const outR = outputs[0][1] || outputs[0][0]; - if (!this.tables) { + if (!this.table) { outL.fill(0); if (outR !== outL) outR.set(outL); return true; From 4321814d36cac20ae5625bbd813b25ea3048411d Mon Sep 17 00:00:00 2001 From: Aria Date: Thu, 4 Dec 2025 13:04:33 -0600 Subject: [PATCH 031/124] Working version for compressor, filter, supersaw, wavetable --- packages/superdough/helpers.mjs | 16 ++++++++++++---- packages/superdough/synth.mjs | 2 +- packages/superdough/wavetable.mjs | 2 +- packages/superdough/worklets.mjs | 16 +++++++++------- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index 219c66d6f..c5c038312 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -1,7 +1,8 @@ import { getAudioContext } from './audioContext.mjs'; -import { clamp, nanFallback, midiToFreq, noteToMidi } from './util.mjs'; -import { getNoiseBuffer } from './noise.mjs'; import { logger } from './logger.mjs'; +import { getNoiseBuffer } from './noise.mjs'; +import { getNodeFromPool } from './nodePools.mjs'; +import { clamp, nanFallback, midiToFreq, noteToMidi } from './util.mjs'; export const noises = ['pink', 'white', 'brown', 'crackle']; @@ -128,6 +129,7 @@ export function getLfo(audioContext, begin, end, properties = {}) { } export function getCompressor(ac, threshold, ratio, knee, attack, release) { + const node = getNodeFromPool('compressor', () => new DynamicsCompressorNode(ac, {})); const options = { threshold: threshold ?? -3, ratio: ratio ?? 10, @@ -135,7 +137,12 @@ export function getCompressor(ac, threshold, ratio, knee, attack, release) { attack: attack ?? 0.005, release: release ?? 0.05, }; - return new DynamicsCompressorNode(ac, options); + node.threshold.value = options.threshold; + node.ratio.value = options.ratio; + node.knee.value = options.knee; + node.attack.value = options.attack; + node.release.value = options.release; + return node; } // changes the default values of the envelope based on what parameters the user has defined @@ -214,7 +221,8 @@ export function createFilter(context, start, end, params, cps, cycle) { filter = getWorklet(context, 'ladder-processor', { frequency, q, drive }); frequencyParam = filter.parameters.get('frequency'); } else { - filter = context.createBiquadFilter(); + const factory = () => context.createBiquadFilter(); + filter = getNodeFromPool('filter', factory); filter.type = type; filter.Q.value = q; filter.frequency.value = frequency; diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index b10136abf..6bf7a1379 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -168,7 +168,7 @@ export function registerSynthSounds() { const params = { frequency, begin, - end: end + 0.5, // add a grace period for pooling + end, freqspread: detune, voices, panspread, diff --git a/packages/superdough/wavetable.mjs b/packages/superdough/wavetable.mjs index a74b3747c..2cac9fac5 100644 --- a/packages/superdough/wavetable.mjs +++ b/packages/superdough/wavetable.mjs @@ -226,7 +226,7 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) { const envEnd = endWithRelease + 0.01; const params = { begin: t, - end: envEnd + 0.5, // add a grace period for pooling + end: envEnd, frequency, freqspread: value.detune, position: value.wt, diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 73fbe9456..e6887a3f6 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -518,16 +518,16 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor { ]; } process(_input, outputs, params) { - if (currentTime >= params.end[0]) { - // should terminate + if (currentTime >= params.end[0] + 0.5) { + // Outside of grace period - should terminate if (this.isAlive) { this.port.postMessage({ type: 'died' }); this.isAlive = false; } return false; } - if (currentTime <= params.begin[0]) { - // keep alive + if (currentTime >= params.end[0] || currentTime <= params.begin[0]) { + // Inside of grace period or not yet started return true; } const output = outputs[0]; @@ -1163,7 +1163,7 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor { this.frameLen = null; this.numFrames = null; this.phase = []; - if (options?.frames) { + if (options?.key) { const key = options.key; this.frameLen = options.frameLen; if (!tablesCache[key]) { @@ -1317,14 +1317,16 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor { } process(_inputs, outputs, parameters) { - if (currentTime >= parameters.end[0]) { + if (currentTime >= parameters.end[0] + 0.5) { + // Outside of grace period - should terminate if (this.isAlive) { this.port.postMessage({ type: 'died' }); this.isAlive = false; } return false; } - if (currentTime <= parameters.begin[0]) { + if (currentTime >= parameters.end[0] || currentTime <= parameters.begin[0]) { + // Inside of grace period or not yet started return true; } const outL = outputs[0][0]; From 0664f90178610c3cb781eb478f68ebacbb49d9a8 Mon Sep 17 00:00:00 2001 From: Aria Date: Thu, 4 Dec 2025 13:07:13 -0600 Subject: [PATCH 032/124] Remove old comment --- packages/superdough/nodePools.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/superdough/nodePools.mjs b/packages/superdough/nodePools.mjs index 08ec9ce51..205352247 100644 --- a/packages/superdough/nodePools.mjs +++ b/packages/superdough/nodePools.mjs @@ -42,7 +42,6 @@ export const releaseNodeToPool = (node) => { // Worklet already terminated, don't pool it return; } - // Fallback to a type-based key if the node was not created via getNodeFromPool const key = node[POOL_KEY]; if (key == null) return; const now = node.context?.currentTime ?? 0; From 4c99f4866b03ba5ad7498bfa11da6408e7609153 Mon Sep 17 00:00:00 2001 From: Aria Date: Thu, 4 Dec 2025 22:50:28 -0600 Subject: [PATCH 033/124] Use setvalueattime to pin values and prevent bleed --- packages/superdough/helpers.mjs | 15 ++++++++------- packages/superdough/synth.mjs | 7 ++++--- packages/superdough/wavetable.mjs | 7 ++++--- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index 1a9ed3797..1430b5642 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -137,11 +137,10 @@ export function getCompressor(ac, threshold, ratio, knee, attack, release) { attack: attack ?? 0.005, release: release ?? 0.05, }; - node.threshold.value = options.threshold; - node.ratio.value = options.ratio; - node.knee.value = options.knee; - node.attack.value = options.attack; - node.release.value = options.release; + const now = ac.currentTime; + Object.entries(options).forEach(([key, value]) => { + node[key].setValueAtTime(value, now); + }); return node; } @@ -224,8 +223,10 @@ export function createFilter(context, start, end, params, cps, cycle) { const factory = () => context.createBiquadFilter(); filter = getNodeFromPool('filter', factory); filter.type = type; - filter.Q.value = q; - filter.frequency.value = frequency; + const now = context.currentTime; + Object.entries({ Q: q, frequency }).forEach(([key, value]) => { + filter[key].setValueAtTime(value, now); + }); frequencyParam = filter.frequency; } const envelopeValues = [params.attack, params.decay, params.sustain, params.release]; diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 6bf7a1379..8f9efa40f 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -175,10 +175,11 @@ export function registerSynthSounds() { }; const factory = () => new AudioWorkletNode(ac, 'supersaw-oscillator', { outputChannelCount: [2] }); const o = getNodeFromPool('supersaw', factory); + const now = ac.currentTime; Object.entries(params).forEach(([key, value]) => { - if (value !== undefined) { - o.parameters.get(key).value = value; - } + const param = o.parameters.get(key); + const target = value !== undefined ? value : param.defaultValue; + param.setValueAtTime(target, now); }); o.port.postMessage({ type: 'initialize' }); o.port.onmessage = (e) => { diff --git a/packages/superdough/wavetable.mjs b/packages/superdough/wavetable.mjs index 2cac9fac5..ea075c608 100644 --- a/packages/superdough/wavetable.mjs +++ b/packages/superdough/wavetable.mjs @@ -238,10 +238,11 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) { }; const factory = () => new AudioWorkletNode(ac, 'wavetable-oscillator-processor', { outputChannelCount: [2] }); const source = getNodeFromPool('wavetable', factory); + const now = ac.currentTime; Object.entries(params).forEach(([key, value]) => { - if (value !== undefined) { - source.parameters.get(key).value = value; - } + const param = source.parameters.get(key); + const target = value !== undefined ? value : param.defaultValue; + param.setValueAtTime(target, now); }); source.port.postMessage({ type: 'initialize', payload }); source.port.onmessage = (e) => { From 177c3a4a2ef848e7f532a0fcf374fb63cff9c209 Mon Sep 17 00:00:00 2001 From: scrappy_fiddler Date: Sat, 6 Dec 2025 16:36:25 +0100 Subject: [PATCH 034/124] some items from review --- website/src/pages/learn/faq.mdx | 44 +++++++++++++++++---------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index c22391330..cef6fbf50 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -263,8 +263,6 @@ Try adding `.punchcard()` after the `release(.2)` for a visualization. ## I saw Switch Angel using functions which I cannot find in the reference (e.g. `trancegate`). How do I make it work? -Methods like `trancegate()`, `rlpf()` and `acidenv()` are currently not pattern methods which come natively with strudel. - They are part of a script/prebake for strudel which was written by Switch Angel and published [here](https://github.com/switchangel/strudel-scripts) You can find the instructions how to use that script in the readme.md there. @@ -277,17 +275,20 @@ The method `note` is used to reference a certain note (either as its name, such On the other hand, `n` is a way to reference the nth index of something. This something can be a scale (eg `n("0 2 4").scale("C:major")`) , but it can also be a particular note in a chord (see https://strudel.cc/recipes/recipes/#arpeggios for an example) . -The method `n` can also be used for something completely unrelated to notes like the nth sample from a sample map `s("hh*8").bank("RolandTR909").n("0 1 2 3")`. Note that `pick` does _not_ use `n`. +The method `n` can also be used for something completely unrelated to notes, in particular the nth sample from a sample map `s("hh*8").bank("RolandTR909").n("0 1 2 3")`. ".pickRestart([ - n("0 1 2 0"), - n("2 3 4 ~"), - n("[4 5] [4 3] 2 0"), - n("0 -3 0 ~") - ]).scale("C:major").s("piano")`} -/>{' '} + tune={` + n("<[0 1 2 3@3 -@2] [3 2 1 0@3 -@2] >").scale("A:minor:pentatonic").s("gm_acoustic_guitar_steel").n("<0 1 2 3>/2")`} +/> + + + + + +Note that `n` is not the only way that functions use indices, some take numbered patterns instead. + ## Is there a cheat sheet for all symbols? @@ -297,29 +298,30 @@ Yes! ' marks start and end of strings, is different from " " marks start and end of single line patterns in mini notation, is different from ' ` marks start and end of patterns with line breaks in mini notation, is different from ' -[] used for patterns, each item in it has the same length -<> used for patterns, alternates each item each cycle +[] used for patterns in mini notation, each item in it has the same length +<> used for patterns, alternates between items each cycle {} historically used for polyrhythmic patterns. {a b c}%4 is the same as
*4. -@3 elongates the item by a factor of 3 (other numbers work too) +@3 elongates the item by a factor of 3 (other numbers work too, even non-integer, but for numbers between 0 and 1 you need a leading zero like this: @0.5) @ after an item: elongates the item once (multiple @ work too c @ @ is the same as c@3) -_ after an item: also elongates an item once (multiple _ work too c _ _ is the same as c@3) +_ after an item: also elongates an item once (multiple _ work too c _ _ is the same as c@3), see below for a different usage. . this divides equal parts of a pattern and is called a foot. Can be used instead of [] like this: "1 6 7 8 . 2 . 3 . 4" is the same as "[1 6 7 8] 2 3 4" - silence ~ also silence -x not silence (for the use in struct, same as 1) -s increase a note of a scale by one semitone, i.e. sharp -b decrease a note of a scale by one semitone, i.e. flat -# used in mondo notation -# also used for names of chords, like F# +x not silence (for the use in struct, any non-silence symbol works there) +b decrease by one semitone, i.e. flat, works for steps of scales, note names (but not midi numbers) and chord names +s increase by one semitone, i.e. sharp, works for steps of scales, note names (but not midi numbers) but not chord names +# increase by one semitone, i.e. sharp works for steps of scales, note names (but not midi numbers) and chord names +# also used in mondo notation + *3 play the sample or pattern at thrice the speed, fast(3) !3 play the sample or pattern three times /2 play the sample or pattern at half speed, slow(2) ? play the pattern sometimes | once per cycle, choose randomly a pattern of those separated by i.e. chooseCycles() , play all items separated by it at the same time, i.e. stack() -: is used to separate multiple parameters, such as adsr(".1:.1:.5:.2") +: is used to separate multiple parameters, such as adsr(".1:.1:.5:.2"), this is is an operator which creates a list of these objects. $: at the start of a line, defines a member of the stack. is the only stack name that should occur multiple names -_ before a stack name: mutes the stack, i.e. hush(), for example _$: s("bd") +_ before a stack name: mutes the stack, i.e. hush(), for example _$: s("bd"), see above for a different usage. ``` ## Are there more FAQ items? From bd282f439ab3b30f613e6f60eee33c098d37600e Mon Sep 17 00:00:00 2001 From: scrappy_fiddler Date: Sat, 6 Dec 2025 16:37:39 +0100 Subject: [PATCH 035/124] everyone wants that trancegate --- website/src/pages/learn/faq.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index cef6fbf50..d4c38d414 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -263,6 +263,8 @@ Try adding `.punchcard()` after the `release(.2)` for a visualization. ## I saw Switch Angel using functions which I cannot find in the reference (e.g. `trancegate`). How do I make it work? +Methods like `trancegate()`, `rlpf()` and `acidenv()` are currently not pattern methods which come natively with strudel. + They are part of a script/prebake for strudel which was written by Switch Angel and published [here](https://github.com/switchangel/strudel-scripts) You can find the instructions how to use that script in the readme.md there. From ab03ece2b7ea61e9de4761e73f318e62812168c4 Mon Sep 17 00:00:00 2001 From: scrappy_fiddler Date: Sat, 6 Dec 2025 16:48:40 +0100 Subject: [PATCH 036/124] prettier --- packages/osc/server.js | 0 website/src/pages/learn/faq.mdx | 12 +++--------- 2 files changed, 3 insertions(+), 9 deletions(-) mode change 100755 => 100644 packages/osc/server.js diff --git a/packages/osc/server.js b/packages/osc/server.js old mode 100755 new mode 100644 diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index d4c38d414..36a8bbef1 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -267,7 +267,7 @@ Methods like `trancegate()`, `rlpf()` and `acidenv()` are currently not pattern They are part of a script/prebake for strudel which was written by Switch Angel and published [here](https://github.com/switchangel/strudel-scripts) -You can find the instructions how to use that script in the readme.md there. +You can find the instructions how to use that script in the readme.md there. ## Is there difference between `n` and `note`? @@ -277,21 +277,16 @@ The method `note` is used to reference a certain note (either as its name, such On the other hand, `n` is a way to reference the nth index of something. This something can be a scale (eg `n("0 2 4").scale("C:major")`) , but it can also be a particular note in a chord (see https://strudel.cc/recipes/recipes/#arpeggios for an example) . -The method `n` can also be used for something completely unrelated to notes, in particular the nth sample from a sample map `s("hh*8").bank("RolandTR909").n("0 1 2 3")`. +The method `n` can also be used for something completely unrelated to notes, in particular the nth sample from a sample map `s("hh*8").bank("RolandTR909").n("0 1 2 3")`. ").scale("A:minor:pentatonic").s("gm_acoustic_guitar_steel").n("<0 1 2 3>/2")`} /> - - - - Note that `n` is not the only way that functions use indices, some take numbered patterns instead. - ## Is there a cheat sheet for all symbols? Yes! @@ -314,14 +309,13 @@ b decrease by one semitone, i.e. flat, works for steps of scales, note names ( s increase by one semitone, i.e. sharp, works for steps of scales, note names (but not midi numbers) but not chord names # increase by one semitone, i.e. sharp works for steps of scales, note names (but not midi numbers) and chord names # also used in mondo notation - *3 play the sample or pattern at thrice the speed, fast(3) !3 play the sample or pattern three times /2 play the sample or pattern at half speed, slow(2) ? play the pattern sometimes | once per cycle, choose randomly a pattern of those separated by i.e. chooseCycles() , play all items separated by it at the same time, i.e. stack() -: is used to separate multiple parameters, such as adsr(".1:.1:.5:.2"), this is is an operator which creates a list of these objects. +: is used to separate multiple parameters, such as adsr(".1:.1:.5:.2"), this is is an operator which creates a list of these objects. $: at the start of a line, defines a member of the stack. is the only stack name that should occur multiple names _ before a stack name: mutes the stack, i.e. hush(), for example _$: s("bd"), see above for a different usage. ``` From 6402ad5d37f70583ba967e8364e1610620a95e01 Mon Sep 17 00:00:00 2001 From: scrappy_fiddler Date: Sat, 6 Dec 2025 16:55:57 +0100 Subject: [PATCH 037/124] better linebreak in example --- website/src/pages/learn/faq.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index 36a8bbef1..b91b48cca 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -282,7 +282,9 @@ The method `n` can also be used for something completely unrelated to notes, in ").scale("A:minor:pentatonic").s("gm_acoustic_guitar_steel").n("<0 1 2 3>/2")`} + n("<[0 1 2 3@3 -@2] [3 2 1 0@3 -@2] >") + .scale("A:minor:pentatonic") + .s("gm_acoustic_guitar_steel").n("<0 1 2 3>/2")`} /> Note that `n` is not the only way that functions use indices, some take numbered patterns instead. From a79491dd16fbe10460d5263c77273abe1b305113 Mon Sep 17 00:00:00 2001 From: Aria Date: Mon, 8 Dec 2025 16:32:54 -0600 Subject: [PATCH 038/124] Update loopbegin/end to not be offset --- packages/superdough/sampler.mjs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index 2deeb4b74..8e46426d3 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -65,21 +65,22 @@ export const getSampleBufferSource = async (hapValue, bank, resolveUrl) => { bufferSource.playbackRate.value = playbackRate; const { loopBegin = 0, loopEnd = 1, begin = 0, end = 1 } = hapValue; + const bufferDuration = bufferSource.buffer.duration; - // "The computation of the offset into the sound is performed using the sound buffer's natural sample rate, - // rather than the current playback rate, so even if the sound is playing at twice its normal speed, - // the midway point through a 10-second audio buffer is still 5." - const offset = begin * bufferSource.buffer.duration; + // The computation of the offset into the sound is performed using the sound buffer's natural duration, + // rather than the playback duration, so that even if the sound is playing at twice its normal speed, + // the midway point through a 10-second audio buffer is still 5. + const offset = begin * bufferDuration; const loop = hapValue.loop; if (loop) { bufferSource.loop = true; - bufferSource.loopStart = loopBegin * bufferSource.buffer.duration - offset; - bufferSource.loopEnd = loopEnd * bufferSource.buffer.duration - offset; + bufferSource.loopStart = loopBegin * bufferDuration; + bufferSource.loopEnd = loopEnd * bufferDuration; } - const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value; - const sliceDuration = (end - begin) * bufferDuration; - return { bufferSource, offset, bufferDuration, sliceDuration }; + const playbackDuration = bufferDuration / bufferSource.playbackRate.value; + const sliceDuration = (end - begin) * playbackDuration; + return { bufferSource, offset, bufferDuration, playbackDuration, sliceDuration }; }; export const loadBuffer = (url, ac, s, n = 0) => { From 597c94199118adcc597e3e437b0f7174192c8bb5 Mon Sep 17 00:00:00 2001 From: Aria Date: Wed, 10 Dec 2025 12:01:04 -0600 Subject: [PATCH 039/124] Add keyboard function --- packages/midi/midi.mjs | 72 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index cd38d3587..477da51cd 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -543,3 +543,75 @@ export async function midin(input) { device.addListener('midimessage', listeners[input]); return cc; } + +/** + * MIDI keyboard: Opens a MIDI input port to receive MIDI keyboard messages. + * + * The note length is fixed as Superdough is not currently set up for undetermined + * note durations + * + * @param {string | number} input MIDI device name or index defaulting to 0 + * @returns {function(): Pattern} A function that produces a pattern. + * When queried, the pattern will produces the most recently played midi notes and velocities, + * lasting for the specified duration + * @example + * const cc = await midin('IAC Driver Bus 1') + * note("c a f e").lpf(cc(0).range(0, 1000)).lpq(cc(1).range(0, 10)).sound("sawtooth") + * @example + * const allCC = await midin('IAC Driver Bus 1') + * const cc = (ccNum) => allCC(ccNum, 2) // just channel 2 + * note("c a f e").s("saw") + * .when(cc(0).gt(0), x => x.postgain(0)) + */ +const kHaps = {}; +const kListeners = {}; +export async function keyboard(input, noteLength = 0.5) { + const device = await _initialize(input); + if (!kHaps[input]) { + kHaps[input] = {}; + } + kListeners[input] && device.removeListener('midimessage', kListeners[input]); + kListeners[input] = (e) => { + const { dataBytes, message } = e; + const [cc, v] = dataBytes; + const noteoff = message.command === 8; + const key = `${input}_${cc}`; + const t = getTime() + 0.1; // slight delay so it's not too late for cyclist to catch + const span = new TimeSpan(t, t + noteLength); + let value = { midikey: key }; + if (noteoff) { + /* TODO: It's a big effort, but we could modify superdough to allow for situations where + we don't know the hap duration in advance. This would mean, for example, that if the hap + is flagged as such a special note-on event, we have all effects be persistent & all ADSR + envelopes stop at the S stage [and store references to them by note+pattern] + If this is implemented, then getting full keyboard functionality should be as simple + as sending the corresponding note-off event below and triggering `release` on each of those + referenced effects/envelopes + + value = { ...value, noteoff: true }; + + If this is achieved, we can remove the noteLength parameter + */ + return; + } else { + value = { ...value, note: Math.round(cc), velocity: v / 127 }; + } + kHaps[input][key] = new Hap(span, span, value, {}); + }; + device.addListener('midimessage', kListeners[input]); + const kb = () => { + const query = (state) => { + const haps = []; + for (const [key, hap] of Object.entries(kHaps[input])) { + haps.push(hap); + } + if (state.controls.cyclist) { + // Notes have been sent; clear them + kHaps[input] = {}; + } + return haps; + }; + return new Pattern(query); + }; + return kb; +} From 103b27c21ffa957684bbcd28b72e4ffe080ee6f6 Mon Sep 17 00:00:00 2001 From: Aria Date: Wed, 10 Dec 2025 13:27:11 -0600 Subject: [PATCH 040/124] Working version of keyboard with variable lengths; tests and cleanup --- packages/midi/midi.mjs | 115 +++++++++++++--------- test/__snapshots__/examples.test.mjs.snap | 4 + test/runtime.mjs | 5 + 3 files changed, 75 insertions(+), 49 deletions(-) diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 477da51cd..bfc42207a 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th */ import * as _WebMidi from 'webmidi'; -import { Pattern, isPattern, logger, ref } from '@strudel/core'; +import { Hap, Pattern, TimeSpan, getTime, isPattern, logger, ref, reify } from '@strudel/core'; import { noteToMidi, getControlName } from '@strudel/core'; import { Note } from 'webmidi'; import { scheduleAtTime } from '../superdough/helpers.mjs'; @@ -477,14 +477,41 @@ Pattern.prototype.midi = function (midiport, options = {}) { }); }; -let listeners = {}; -const refs = {}; -const refsByChan = {}; +/** + * Initialize a midi device + */ +async function _initialize(input) { + if (isPattern(input)) { + throw new Error( + `[midi] Midi input cannot be a pattern. Make sure to pass device name with single quotes. Example: midin('${ + WebMidi.outputs?.[0]?.name || 'IAC Driver Bus 1' + }')`, + ); + } + const initial = await enableWebMidi(); // only returns on first init + const device = getDevice(input, WebMidi.inputs); + if (!device) { + throw new Error( + `[midi] Midi device "${input}" not found.. connected devices: ${getMidiDeviceNamesString(WebMidi.inputs)}`, + ); + } + if (initial) { + const otherInputs = WebMidi.inputs.filter((o) => o.name !== device.name); + logger( + `[midi] Midi enabled! Using "${device.name}". ${ + otherInputs?.length ? `Also available: ${getMidiDeviceNamesString(otherInputs)}` : '' + }`, + ); + } + return device; +} /** * MIDI input: Opens a MIDI input port to receive MIDI control change messages. * * The output is a function that accepts a midi cc value to query as well as (optionally) a midi channel + * + * @name midin * @param {string | number} input MIDI device name or index defaulting to 0 * @returns {function(number, number=): Pattern} A function from (cc, channel?) to a pattern. * When queried, the pattern will produces the most recently received midi value (normalized to 0 to 1) @@ -498,29 +525,11 @@ const refsByChan = {}; * note("c a f e").s("saw") * .when(cc(0).gt(0), x => x.postgain(0)) */ +let listeners = {}; +const refs = {}; +const refsByChan = {}; export async function midin(input) { - if (isPattern(input)) { - throw new Error( - `midin: does not accept Pattern as input. Make sure to pass device name with single quotes. Example: midin('${ - WebMidi.outputs?.[0]?.name || 'IAC Driver Bus 1' - }')`, - ); - } - const initial = await enableWebMidi(); // only returns on first init - const device = getDevice(input, WebMidi.inputs); - if (!device) { - throw new Error( - `midiin: device "${input}" not found.. connected devices: ${getMidiDeviceNamesString(WebMidi.inputs)}`, - ); - } - if (initial) { - const otherInputs = WebMidi.inputs.filter((o) => o.name !== device.name); - logger( - `Midi enabled! Using "${device.name}". ${ - otherInputs?.length ? `Also available: ${getMidiDeviceNamesString(otherInputs)}` : '' - }`, - ); - } + const device = await _initialize(input); refs[input] ??= {}; refsByChan[input] ??= {}; const cc = (cc, chan) => { @@ -532,8 +541,7 @@ export async function midin(input) { listeners[input] && device.removeListener('midimessage', listeners[input]); listeners[input] = (e) => { - const ccNum = e.dataBytes[0]; - const v = e.dataBytes[1]; + const [ccNum, v] = e.dataBytes; const chan = e.message.channel; const scaled = v / 127; refsByChan[input][ccNum] ??= {}; @@ -550,40 +558,42 @@ export async function midin(input) { * The note length is fixed as Superdough is not currently set up for undetermined * note durations * + * @name keyboard * @param {string | number} input MIDI device name or index defaulting to 0 - * @returns {function(): Pattern} A function that produces a pattern. + * @returns {function((number | Pattern)=): Pattern} A function that produces a pattern. * When queried, the pattern will produces the most recently played midi notes and velocities, * lasting for the specified duration * @example - * const cc = await midin('IAC Driver Bus 1') - * note("c a f e").lpf(cc(0).range(0, 1000)).lpq(cc(1).range(0, 10)).sound("sawtooth") + * const kb = await keyboard('Arturia KeyStep 32') + * kb().s("tri").lpf(80).lpe(6).lpd(0.1).room(2).delay(0.35) * @example - * const allCC = await midin('IAC Driver Bus 1') - * const cc = (ccNum) => allCC(ccNum, 2) // just channel 2 - * note("c a f e").s("saw") - * .when(cc(0).gt(0), x => x.postgain(0)) + * const kb = await keyboard('Arturia KeyStep 32') + * kb("0.5 1") + * .s("saw") + * .add(note(rand.mul(0.3))) + * .lpf(1000).lpe(2).room(0.5) */ const kHaps = {}; const kListeners = {}; -export async function keyboard(input, noteLength = 0.5) { +export async function keyboard(input) { const device = await _initialize(input); if (!kHaps[input]) { - kHaps[input] = {}; + kHaps[input] = []; } kListeners[input] && device.removeListener('midimessage', kListeners[input]); kListeners[input] = (e) => { const { dataBytes, message } = e; - const [cc, v] = dataBytes; + const [note, velocity] = dataBytes; const noteoff = message.command === 8; - const key = `${input}_${cc}`; + const key = `${input}_${note}`; const t = getTime() + 0.1; // slight delay so it's not too late for cyclist to catch - const span = new TimeSpan(t, t + noteLength); + const span = new TimeSpan(t, t); let value = { midikey: key }; if (noteoff) { /* TODO: It's a big effort, but we could modify superdough to allow for situations where we don't know the hap duration in advance. This would mean, for example, that if the hap is flagged as such a special note-on event, we have all effects be persistent & all ADSR - envelopes stop at the S stage [and store references to them by note+pattern] + envelopes stop at the S stage [and store references to them by `midikey`] If this is implemented, then getting full keyboard functionality should be as simple as sending the corresponding note-off event below and triggering `release` on each of those referenced effects/envelopes @@ -594,20 +604,27 @@ export async function keyboard(input, noteLength = 0.5) { */ return; } else { - value = { ...value, note: Math.round(cc), velocity: v / 127 }; + value = { ...value, note: Math.round(note), velocity: velocity / 127 }; } - kHaps[input][key] = new Hap(span, span, value, {}); + kHaps[input].push(new Hap(span, span, value, {})); }; device.addListener('midimessage', kListeners[input]); - const kb = () => { + const kb = (noteLength = 0.5) => { + const nlPat = reify(noteLength); const query = (state) => { - const haps = []; - for (const [key, hap] of Object.entries(kHaps[input])) { - haps.push(hap); - } + const haps = kHaps[input].flatMap((hap) => { + const lenHaps = nlPat.query(state.setSpan(hap.wholeOrPart())); + return lenHaps.map((lenHap) => { + const nl = lenHap.value ?? 0.5; + const whole = new TimeSpan(hap.whole.begin, hap.whole.begin.add(nl)); + const part = new TimeSpan(hap.part.begin, hap.part.begin.add(nl)); + const context = hap.combineContext(lenHap); + return new Hap(whole, part, hap.value, context); + }); + }); if (state.controls.cyclist) { // Notes have been sent; clear them - kHaps[input] = {}; + kHaps[input] = []; } return haps; }; diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 09d285b5d..62e2de914 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -5943,6 +5943,10 @@ exports[`runs examples > example "juxBy" example index 0 1`] = ` exports[`runs examples > example "keyDown" example index 0 1`] = `[]`; +exports[`runs examples > example "keyboard" example index 0 1`] = `[]`; + +exports[`runs examples > example "keyboard" example index 1 1`] = `[]`; + exports[`runs examples > example "lastOf" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:c3 ]", diff --git a/test/runtime.mjs b/test/runtime.mjs index 2a29de3f5..51a713a60 100644 --- a/test/runtime.mjs +++ b/test/runtime.mjs @@ -131,6 +131,10 @@ const midin = () => { return (ccNum) => strudel.ref(() => 0); // returns ref with default value 0 }; +const keyboard = async () => { + return () => strudel.silence; +}; + const sysex = ([id, data]) => {}; // TODO: refactor to evalScope @@ -150,6 +154,7 @@ evalScope( */ { midin, + keyboard, sysex, // gist, // euclid, From c8b2e1ac9e529333f72cb4ac8e87fe80cdf82a6e Mon Sep 17 00:00:00 2001 From: 1d10t <1d10t@noreply.codeberg.org> Date: Fri, 12 Dec 2025 21:22:21 +0100 Subject: [PATCH 041/124] feat(superdough/audiocontext): make AudioContext injection optional - Modify `setDefaultAudioContext()` to accept an optional `existingAudioCtx` parameter. - If provided, it will use the existing context; otherwise, it creates a new one. - This allows for better integration with external audio systems that manage their own AudioContext. --- packages/superdough/audioContext.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/superdough/audioContext.mjs b/packages/superdough/audioContext.mjs index 71e01d57d..9bae75bd9 100644 --- a/packages/superdough/audioContext.mjs +++ b/packages/superdough/audioContext.mjs @@ -1,7 +1,7 @@ let audioContext; -export const setDefaultAudioContext = () => { - audioContext = new AudioContext(); +export const setDefaultAudioContext = (existingAudioCtx) => { + audioContext = existingAudioCtx ?? new AudioContext(); return audioContext; }; From 4e14718e93b7d9a5ebe7fb1892109745577b8bc7 Mon Sep 17 00:00:00 2001 From: 1d10t <1d10t@noreply.codeberg.org> Date: Fri, 12 Dec 2025 21:56:31 +0100 Subject: [PATCH 042/124] feat(webaudio): enable AudioContext sharing for webaudioRepl - Add support for optional `existingAudioCtx` parameter in `webaudioRepl()` function - When provided, sets it as the default AudioContext via `setDefaultAudioContext()` - Allows integration with external systems that manage their own Web Audio API context - Maintains backward compatibility with existing usage patterns --- packages/webaudio/webaudio.mjs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 383e87f87..8fc9b860f 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th */ import * as strudel from '@strudel/core'; -import { superdough, getAudioContext, setLogger, doughTrigger, registerWorklet } from 'superdough'; +import { superdough, setDefaultAudioContext, getAudioContext, setLogger, doughTrigger, registerWorklet } from 'superdough'; import './supradough.mjs'; import { workletUrl } from 'supradough'; @@ -27,6 +27,9 @@ export const webaudioOutput = (hap, _deadline, hapDuration, cps, t) => { }; export function webaudioRepl(options = {}) { + if (options.existingAudioCtx) { + setDefaultAudioContext(options.existingAudioCtx); + } options = { getTime: () => getAudioContext().currentTime, defaultOutput: webaudioOutput, From adf9596d070eb33ee8c52726cbd10fef7dea0141 Mon Sep 17 00:00:00 2001 From: Sergey S Yaglov Date: Sat, 13 Dec 2025 00:28:13 +0300 Subject: [PATCH 043/124] codestyle(webaudio) --- packages/webaudio/webaudio.mjs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 8fc9b860f..399e68ab1 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -5,7 +5,14 @@ This program is free software: you can redistribute it and/or modify it under th */ import * as strudel from '@strudel/core'; -import { superdough, setDefaultAudioContext, getAudioContext, setLogger, doughTrigger, registerWorklet } from 'superdough'; +import { + superdough, + setDefaultAudioContext, + getAudioContext, + setLogger, + doughTrigger, + registerWorklet, +} from 'superdough'; import './supradough.mjs'; import { workletUrl } from 'supradough'; From 181ebee374058b6bf7fcf686f39e7ab3ef23d733 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 19 Dec 2025 01:25:58 +0100 Subject: [PATCH 044/124] fix: repl package init audio properly --- examples/buildless/web-component-no-iframe.html | 3 ++- packages/repl/repl-component.mjs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/buildless/web-component-no-iframe.html b/examples/buildless/web-component-no-iframe.html index 8afa85cb5..ae4fb700d 100644 --- a/examples/buildless/web-component-no-iframe.html +++ b/examples/buildless/web-component-no-iframe.html @@ -1,4 +1,5 @@ - + + - + diff --git a/website/src/components/HeadCommonNext.astro b/website/src/components/HeadCommonNext.astro index 9f323a7a3..1f878bac0 100644 --- a/website/src/components/HeadCommonNext.astro +++ b/website/src/components/HeadCommonNext.astro @@ -24,7 +24,7 @@ const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL - + From 4743334a17388c58993b84fbb6bf57d604d7829e Mon Sep 17 00:00:00 2001 From: Aria Date: Fri, 9 Jan 2026 22:50:16 -0600 Subject: [PATCH 062/124] Reduce latency; change name to midikeys; include in i/o learn page --- packages/midi/midi.mjs | 10 +++++----- test/__snapshots__/examples.test.mjs.snap | 8 ++++---- test/runtime.mjs | 4 ++-- website/src/pages/learn/input-output.mdx | 6 +++++- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index bfc42207a..d4f6093a0 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -558,16 +558,16 @@ export async function midin(input) { * The note length is fixed as Superdough is not currently set up for undetermined * note durations * - * @name keyboard + * @name midikeys * @param {string | number} input MIDI device name or index defaulting to 0 * @returns {function((number | Pattern)=): Pattern} A function that produces a pattern. * When queried, the pattern will produces the most recently played midi notes and velocities, * lasting for the specified duration * @example - * const kb = await keyboard('Arturia KeyStep 32') + * const kb = await midikeys('Arturia KeyStep 32') * kb().s("tri").lpf(80).lpe(6).lpd(0.1).room(2).delay(0.35) * @example - * const kb = await keyboard('Arturia KeyStep 32') + * const kb = await midikeys('Arturia KeyStep 32') * kb("0.5 1") * .s("saw") * .add(note(rand.mul(0.3))) @@ -575,7 +575,7 @@ export async function midin(input) { */ const kHaps = {}; const kListeners = {}; -export async function keyboard(input) { +export async function midikeys(input) { const device = await _initialize(input); if (!kHaps[input]) { kHaps[input] = []; @@ -586,7 +586,7 @@ export async function keyboard(input) { const [note, velocity] = dataBytes; const noteoff = message.command === 8; const key = `${input}_${note}`; - const t = getTime() + 0.1; // slight delay so it's not too late for cyclist to catch + const t = getTime() + 0.03; // slight delay so it's not too late for cyclist to catch const span = new TimeSpan(t, t); let value = { midikey: key }; if (noteoff) { diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 62e2de914..2aa20a0eb 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -5943,10 +5943,6 @@ exports[`runs examples > example "juxBy" example index 0 1`] = ` exports[`runs examples > example "keyDown" example index 0 1`] = `[]`; -exports[`runs examples > example "keyboard" example index 0 1`] = `[]`; - -exports[`runs examples > example "keyboard" example index 1 1`] = `[]`; - exports[`runs examples > example "lastOf" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:c3 ]", @@ -7031,6 +7027,10 @@ exports[`runs examples > example "midicmd" example index 0 1`] = ` ] `; +exports[`runs examples > example "midikeys" example index 0 1`] = `[]`; + +exports[`runs examples > example "midikeys" example index 1 1`] = `[]`; + exports[`runs examples > example "midin" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:c cutoff:0 resonance:0 s:sawtooth ]", diff --git a/test/runtime.mjs b/test/runtime.mjs index 51a713a60..5a64913cf 100644 --- a/test/runtime.mjs +++ b/test/runtime.mjs @@ -131,7 +131,7 @@ const midin = () => { return (ccNum) => strudel.ref(() => 0); // returns ref with default value 0 }; -const keyboard = async () => { +const midikeys = async () => { return () => strudel.silence; }; @@ -154,7 +154,7 @@ evalScope( */ { midin, - keyboard, + midikeys, sysex, // gist, // euclid, diff --git a/website/src/pages/learn/input-output.mdx b/website/src/pages/learn/input-output.mdx index 0dd48b7de..1d4bf4c39 100644 --- a/website/src/pages/learn/input-output.mdx +++ b/website/src/pages/learn/input-output.mdx @@ -16,10 +16,14 @@ It is also possible to pattern other things with Strudel, such as software and h Strudel supports MIDI without any additional software (thanks to [webmidi](https://npmjs.com/package/webmidi)), just by adding methods to your pattern: -## midiin(inputName?) +## midin(inputName?) +## midikeys(inputName?) + + + ## midi(outputName?,options?) Either connect a midi device or use the IAC Driver (Mac) or Midi Through Port (Linux) for internal midi messages. From 3b2e9fa0b4bdf1837b7993b59fbb3516d14bb25a Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 10 Jan 2026 01:22:50 -0500 Subject: [PATCH 063/124] wip --- packages/superdough/superdough.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 6ef8435cd..c47939275 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -547,6 +547,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) } else if (getSound(s)) { const { onTrigger } = getSound(s); const onEnded = () => { + chain.releaseNodes(); audioNodes.forEach((n) => (isPoolable(n) ? releaseNodeToPool(n) : n?.disconnect())); activeSoundSources.delete(chainID); }; From e837c516c5eb3815e838df1707ff05a99d740261 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 10 Jan 2026 01:37:14 -0500 Subject: [PATCH 064/124] merge main --- packages/superdough/superdough.mjs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index c47939275..2fe80d441 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -10,17 +10,7 @@ import './vowel.mjs'; import { clamp, nanFallback, _mod, cycleToSeconds, pickAndRename } from './util.mjs'; import workletsUrl from './worklets.mjs?audioworklet'; import { getNodeFromPool, isPoolable, releaseNodeToPool } from './nodePools.mjs'; -import { - createFilter, - effectSend, - gainNode, - getCompressor, - getDistortion, - getLfo, - getWorklet, - releaseAudioNode, - webAudioTimeout, -} from './helpers.mjs'; +import { createFilter, effectSend, gainNode, getCompressor, getDistortion, getLfo, getWorklet } from './helpers.mjs'; import { map } from 'nanostores'; import { logger } from './logger.mjs'; import { connectLFO, connectEnvelope, connectBusModulator } from './modulators.mjs'; @@ -429,7 +419,7 @@ class Chain { return this; } releaseNodes() { - this.audioNodes.forEach((n) => releaseAudioNode(n)); + this.audioNodes.forEach((n) => (isPoolable(n) ? releaseNodeToPool(n) : n?.disconnect())); this.audioNodes = []; this.tails = []; } @@ -548,7 +538,6 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) const { onTrigger } = getSound(s); const onEnded = () => { chain.releaseNodes(); - audioNodes.forEach((n) => (isPoolable(n) ? releaseNodeToPool(n) : n?.disconnect())); activeSoundSources.delete(chainID); }; // We have to use onEnded because some sources (e.g. `sampler`) have From 0e9c50bf0a55e3225a26404ca593a9e34c484d45 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 10 Jan 2026 01:39:39 -0500 Subject: [PATCH 065/124] fix import --- packages/superdough/wavetable.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/superdough/wavetable.mjs b/packages/superdough/wavetable.mjs index f5da1f3e1..ee044ee49 100644 --- a/packages/superdough/wavetable.mjs +++ b/packages/superdough/wavetable.mjs @@ -9,6 +9,7 @@ import { getPitchEnvelope, getVibratoOscillator, webAudioTimeout, + releaseAudioNode } from './helpers.mjs'; import { getNodeFromPool, markWorkletAsDead, releaseNodeToPool } from './nodePools.mjs'; import { logger } from './logger.mjs'; From a205f7d87388c8fba8366bee02db55e7a030521e Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 10 Jan 2026 01:42:06 -0500 Subject: [PATCH 066/124] rm dead code --- packages/superdough/superdough.mjs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 2fe80d441..5d562ee2a 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -540,18 +540,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) chain.releaseNodes(); activeSoundSources.delete(chainID); }; - // We have to use onEnded because some sources (e.g. `sampler`) have - // an internal duration which is longer than `value.duration` - // const onEnded = () => - // webAudioTimeout( - // ac, - // () => { - // chain.releaseNodes(); - // activeSoundSources.delete(chainID); - // }, - // 0, - // endWithRelease, - // ); + const soundHandle = await onTrigger(t, value, onEnded, cps); if (soundHandle) { From 7a5020aa71f5692b627ac39e4d3ca7dcc9464896 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 10 Jan 2026 01:42:43 -0500 Subject: [PATCH 067/124] format --- packages/superdough/superdough.mjs | 2 +- packages/superdough/wavetable.mjs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 5d562ee2a..dea314ea2 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -540,7 +540,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) chain.releaseNodes(); activeSoundSources.delete(chainID); }; - + const soundHandle = await onTrigger(t, value, onEnded, cps); if (soundHandle) { diff --git a/packages/superdough/wavetable.mjs b/packages/superdough/wavetable.mjs index ee044ee49..adf8b0c44 100644 --- a/packages/superdough/wavetable.mjs +++ b/packages/superdough/wavetable.mjs @@ -9,7 +9,7 @@ import { getPitchEnvelope, getVibratoOscillator, webAudioTimeout, - releaseAudioNode + releaseAudioNode, } from './helpers.mjs'; import { getNodeFromPool, markWorkletAsDead, releaseNodeToPool } from './nodePools.mjs'; import { logger } from './logger.mjs'; From b4e00538f86c7d50a276bcfe6698ca4d38ab9fa0 Mon Sep 17 00:00:00 2001 From: floy Date: Sat, 10 Jan 2026 15:30:07 +0100 Subject: [PATCH 068/124] Don't show 'No sounds loaded' text if search is active --- website/src/repl/components/panel/SoundsTab.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/repl/components/panel/SoundsTab.jsx b/website/src/repl/components/panel/SoundsTab.jsx index 52fbf928c..0e46b1382 100644 --- a/website/src/repl/components/panel/SoundsTab.jsx +++ b/website/src/repl/components/panel/SoundsTab.jsx @@ -210,7 +210,7 @@ export function SoundsTab() { ) : ( '' )} - {!soundEntries.length && soundsFilter !== 'importSounds' ? 'No sounds loaded' : ''} + {!soundEntries.length && soundsFilter !== 'importSounds' && search == '' ? 'No sounds loaded' : ''}
); From fd0209dbc067b4c683f946738dedc9da3f752942 Mon Sep 17 00:00:00 2001 From: floy Date: Sat, 10 Jan 2026 15:39:02 +0100 Subject: [PATCH 069/124] Show explicit help text when search doesn't match --- website/src/repl/components/panel/SoundsTab.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/website/src/repl/components/panel/SoundsTab.jsx b/website/src/repl/components/panel/SoundsTab.jsx index 0e46b1382..2060c64a9 100644 --- a/website/src/repl/components/panel/SoundsTab.jsx +++ b/website/src/repl/components/panel/SoundsTab.jsx @@ -210,7 +210,10 @@ export function SoundsTab() { ) : ( '' )} - {!soundEntries.length && soundsFilter !== 'importSounds' && search == '' ? 'No sounds loaded' : ''} + {!soundEntries.length && soundsFilter !== 'importSounds' ? + search == '' ? 'No sounds loaded' : 'No sounds found' + : '' + }
); From 726e5ef14a6de2aab36202434ac52ae45c5e551f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sat, 10 Jan 2026 07:18:29 -0800 Subject: [PATCH 070/124] Fix formatting --- packages/midi/midi.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 9b32f6de4..63faeaf41 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -488,7 +488,7 @@ const refsByChan = {}; * MIDI input: Opens a MIDI input port to receive MIDI control change messages. * * The output is a function that accepts a midi cc value to query as well as (optionally) a midi channel - * @tags external_io + * @tags external_io * @param {string | number} input MIDI device name or index defaulting to 0 * @returns {function(number, number=): Pattern} A function from (cc, channel?) to a pattern. * When queried, the pattern will produces the most recently received midi value (normalized to 0 to 1) From d753eedb901c1ead239f8f288d887af292f3ce66 Mon Sep 17 00:00:00 2001 From: Aria Date: Sat, 10 Jan 2026 11:20:17 -0600 Subject: [PATCH 071/124] Make latency consistent across all cps --- packages/core/repl.mjs | 3 ++- packages/core/time.mjs | 9 +++++++++ packages/midi/midi.mjs | 6 ++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index 4d59f5990..77ceac8c5 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -2,7 +2,7 @@ import { NeoCyclist } from './neocyclist.mjs'; import { Cyclist } from './cyclist.mjs'; import { evaluate as _evaluate } from './evaluate.mjs'; import { errorLogger, logger } from './logger.mjs'; -import { setTime } from './time.mjs'; +import { setCpsFunc, setTime } from './time.mjs'; import { evalScope } from './evaluate.mjs'; import { register, Pattern, isPattern, silence, stack } from './pattern.mjs'; @@ -61,6 +61,7 @@ export function repl({ // NeoCyclist uses a shared worker to communicate between instances, which is not supported on mobile chrome const scheduler = sync && typeof SharedWorker != 'undefined' ? new NeoCyclist(schedulerOptions) : new Cyclist(schedulerOptions); + setCpsFunc(() => scheduler.cps); let pPatterns = {}; let anonymousIndex = 0; let allTransform; diff --git a/packages/core/time.mjs b/packages/core/time.mjs index 80daaf53c..3d94a694c 100644 --- a/packages/core/time.mjs +++ b/packages/core/time.mjs @@ -1,4 +1,5 @@ let time; +let cpsFunc; export function getTime() { if (!time) { throw new Error('no time set! use setTime to define a time source'); @@ -9,3 +10,11 @@ export function getTime() { export function setTime(func) { time = func; } + +export function setCpsFunc(func) { + cpsFunc = func; +} + +export function getCps() { + return cpsFunc?.(); +} diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index d4f6093a0..6233bf60d 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th */ import * as _WebMidi from 'webmidi'; -import { Hap, Pattern, TimeSpan, getTime, isPattern, logger, ref, reify } from '@strudel/core'; +import { Hap, Pattern, TimeSpan, getCps, getTime, isPattern, logger, ref, reify } from '@strudel/core'; import { noteToMidi, getControlName } from '@strudel/core'; import { Note } from 'webmidi'; import { scheduleAtTime } from '../superdough/helpers.mjs'; @@ -586,7 +586,9 @@ export async function midikeys(input) { const [note, velocity] = dataBytes; const noteoff = message.command === 8; const key = `${input}_${note}`; - const t = getTime() + 0.03; // slight delay so it's not too late for cyclist to catch + const cps = getCps() ?? 0.5; + const latencySeconds = 0.06; // slight delay so it's not too late for cyclist to catch + const t = getTime() + latencySeconds * cps; const span = new TimeSpan(t, t); let value = { midikey: key }; if (noteoff) { From dab16767612e88e2337c46e938d387f361fdceda Mon Sep 17 00:00:00 2001 From: Aria Date: Sat, 10 Jan 2026 11:26:30 -0600 Subject: [PATCH 072/124] Add license for time file --- packages/core/time.mjs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/core/time.mjs b/packages/core/time.mjs index 3d94a694c..2d4caecf5 100644 --- a/packages/core/time.mjs +++ b/packages/core/time.mjs @@ -1,3 +1,9 @@ +/* +time.mjs - Core time module. Used to expose parameters from the Scheduler like `time` and `cps` +Copyright (C) 2026 Strudel contributors - see +This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ + let time; let cpsFunc; export function getTime() { From 0cf11adc2c8416ed1df1563b3f6c16cc7dc637fd Mon Sep 17 00:00:00 2001 From: Aria Date: Sat, 10 Jan 2026 13:07:54 -0600 Subject: [PATCH 073/124] Allow naked distortions for the purpose of FX --- packages/core/pattern.mjs | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 02cf91881..58c83a6f8 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -3586,6 +3586,20 @@ export const morph = (frompat, topat, bypat) => { return frompat.innerBind((from) => topat.innerBind((to) => bypat.innerBind((by) => _morph(from, to, by)))); }; +const _distortWithAlg = function (name) { + const func = function (args, pat) { + const argsPat = reify(args).fmap((v) => (Array.isArray(v) ? [...v, name] : [v, 1, name])); + if (!pat) { + return pure({}).distort(argsPat); + } + return pat.distort(argsPat); + }; + Pattern.prototype[name] = function (args) { + return func(args, this); + }; + return func; +}; + /** * Soft-clipping distortion * @@ -3594,6 +3608,8 @@ export const morph = (frompat, topat, bypat) => { * @param {number | Pattern} volume linear postgain of the distortion * */ +export const soft = _distortWithAlg('soft'); + /** * Hard-clipping distortion * @@ -3602,6 +3618,8 @@ export const morph = (frompat, topat, bypat) => { * @param {number | Pattern} volume linear postgain of the distortion * */ +export const hard = _distortWithAlg('hard'); + /** * Cubic polynomial distortion * @@ -3610,6 +3628,8 @@ export const morph = (frompat, topat, bypat) => { * @param {number | Pattern} volume linear postgain of the distortion * */ +export const cubic = _distortWithAlg('cubic'); + /** * Diode-emulating distortion * @@ -3618,6 +3638,8 @@ export const morph = (frompat, topat, bypat) => { * @param {number | Pattern} volume linear postgain of the distortion * */ +export const diode = _distortWithAlg('diode'); + /** * Asymmetrical diode distortion * @@ -3626,6 +3648,8 @@ export const morph = (frompat, topat, bypat) => { * @param {number | Pattern} volume linear postgain of the distortion * */ +export const asym = _distortWithAlg('asym'); + /** * Wavefolding distortion * @@ -3634,6 +3658,8 @@ export const morph = (frompat, topat, bypat) => { * @param {number | Pattern} volume linear postgain of the distortion * */ +export const fold = _distortWithAlg('fold'); + /** * Wavefolding distortion composed with sinusoid * @@ -3642,6 +3668,8 @@ export const morph = (frompat, topat, bypat) => { * @param {number | Pattern} volume linear postgain of the distortion * */ +export const sinefold = _distortWithAlg('sinefold'); + /** * Distortion via Chebyshev polynomials * @@ -3650,14 +3678,7 @@ export const morph = (frompat, topat, bypat) => { * @param {number | Pattern} volume linear postgain of the distortion * */ -const distAlgoNames = ['scurve', 'soft', 'hard', 'cubic', 'diode', 'asym', 'fold', 'sinefold', 'chebyshev']; -for (const name of distAlgoNames) { - // Add aliases for distortion algorithms - Pattern.prototype[name] = function (args) { - const argsPat = reify(args).fmap((v) => (Array.isArray(v) ? [...v, name] : [v, 1, name])); - return this.distort(argsPat); - }; -} +export const chebyshev = _distortWithAlg('chebyshev'); /** * Turns a list of patterns into a single pattern which outputs list-values From 447692106923167d3c0ddc308041bb79d4a944ca Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 10 Jan 2026 16:46:11 -0500 Subject: [PATCH 074/124] use releasenode --- packages/superdough/superdough.mjs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index dea314ea2..41b5ce976 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -419,7 +419,7 @@ class Chain { return this; } releaseNodes() { - this.audioNodes.forEach((n) => (isPoolable(n) ? releaseNodeToPool(n) : n?.disconnect())); + this.audioNodes.forEach((n) => (isPoolable(n) ? releaseNodeToPool(n) : releaseAudioNode(n))); this.audioNodes = []; this.tails = []; } @@ -536,10 +536,17 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) nodes.main['source'] = [sourceNode]; } else if (getSound(s)) { const { onTrigger } = getSound(s); - const onEnded = () => { - chain.releaseNodes(); - activeSoundSources.delete(chainID); - }; + + const onEnded = () => + webAudioTimeout( + ac, + () => { + chain.releaseNodes(); + activeSoundSources.delete(chainID); + }, + 0, + endWithRelease, + ); const soundHandle = await onTrigger(t, value, onEnded, cps); From 741ff3f3f0117298b5c665869979d4144a706984 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 10 Jan 2026 16:55:53 -0500 Subject: [PATCH 075/124] fix import --- packages/superdough/superdough.mjs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 41b5ce976..d2baa9398 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -10,7 +10,17 @@ import './vowel.mjs'; import { clamp, nanFallback, _mod, cycleToSeconds, pickAndRename } from './util.mjs'; import workletsUrl from './worklets.mjs?audioworklet'; import { getNodeFromPool, isPoolable, releaseNodeToPool } from './nodePools.mjs'; -import { createFilter, effectSend, gainNode, getCompressor, getDistortion, getLfo, getWorklet } from './helpers.mjs'; +import { + createFilter, + effectSend, + gainNode, + getCompressor, + getDistortion, + getLfo, + getWorklet, + releaseAudioNode, + webAudioTimeout, +} from './helpers.mjs'; import { map } from 'nanostores'; import { logger } from './logger.mjs'; import { connectLFO, connectEnvelope, connectBusModulator } from './modulators.mjs'; From 0571a8dc7d706f2fbd2647668b8acba18625ece0 Mon Sep 17 00:00:00 2001 From: gueejla Date: Sat, 10 Jan 2026 22:11:13 -0600 Subject: [PATCH 076/124] fixes Serial onTrigger() params #1633 --- packages/serial/serial.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/serial/serial.mjs b/packages/serial/serial.mjs index 692109522..e007b24b9 100644 --- a/packages/serial/serial.mjs +++ b/packages/serial/serial.mjs @@ -68,7 +68,7 @@ Pattern.prototype.serial = function (br = 115200, sendcrc = false, singlecharids if (!(name in writeMessagers)) { getWriter(name, br); } - const onTrigger = (t_deprecate, hap, currentTime, cps, targetTime) => { + const onTrigger = (hap, currentTime, _cps, targetTime) => { var message = ''; var chk = 0; if (typeof hap.value === 'object') { From 604a1b770cab5f55d6cada4915bccff4de460435 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 11 Jan 2026 00:27:07 -0500 Subject: [PATCH 077/124] dialog --- .../panel/ImportPrebakeScriptButton.jsx | 15 +++++++- .../src/repl/components/panel/SettingsTab.jsx | 36 ++++++------------- website/src/repl/util.mjs | 16 +++++++-- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/website/src/repl/components/panel/ImportPrebakeScriptButton.jsx b/website/src/repl/components/panel/ImportPrebakeScriptButton.jsx index c997f6b6e..64ce198be 100644 --- a/website/src/repl/components/panel/ImportPrebakeScriptButton.jsx +++ b/website/src/repl/components/panel/ImportPrebakeScriptButton.jsx @@ -1,6 +1,7 @@ import { errorLogger } from '@strudel/core'; import { useSettings, storePrebakeScript } from '../../../settings.mjs'; import { SpecialActionInput } from '../button/action-button'; +import { confirmDialog, SETTING_CHANGE_RELOAD_MSG } from '@src/repl/util.mjs'; async function importScript(script) { const reader = new FileReader(); @@ -23,7 +24,19 @@ export function ImportPrebakeScriptButton() { type="file" label="import prebake script" accept=".strudel" - onChange={(e) => importScript(e.target.files[0])} + onChange={async (e) => { + const file = e.target.files[0]; + const confirmed = await confirmDialog(SETTING_CHANGE_RELOAD_MSG); + if (!confirmed) { + return; + } + try { + await importScript(file); + window.location.reload(); + } catch (e) { + errorLogger(e); + } + }} /> ); } diff --git a/website/src/repl/components/panel/SettingsTab.jsx b/website/src/repl/components/panel/SettingsTab.jsx index f46fa661c..7f488c544 100644 --- a/website/src/repl/components/panel/SettingsTab.jsx +++ b/website/src/repl/components/panel/SettingsTab.jsx @@ -1,13 +1,13 @@ import { defaultSettings, settingsMap, useSettings } from '../../../settings.mjs'; import { themes } from '@strudel/codemirror'; import { Textbox } from '../textbox/Textbox.jsx'; -import { isUdels } from '../../util.mjs'; +import { confirmAndReloadPage, isUdels } from '../../util.mjs'; import { ButtonGroup } from './Forms.jsx'; import { AudioDeviceSelector } from './AudioDeviceSelector.jsx'; import { AudioEngineTargetSelector } from './AudioEngineTargetSelector.jsx'; import { confirmDialog } from '../../util.mjs'; import { DEFAULT_MAX_POLYPHONY, setMaxPolyphony, setMultiChannelOrbits } from '@strudel/webaudio'; -import { ActionButton, SpecialActionButton } from '../button/action-button.jsx'; +import { SpecialActionButton } from '../button/action-button.jsx'; import { ImportPrebakeScriptButton } from './ImportPrebakeScriptButton.jsx'; function Checkbox({ label, value, onChange, disabled = false }) { @@ -86,8 +86,6 @@ const fontFamilyOptions = { galactico: 'galactico', }; -const RELOAD_MSG = 'Changing this setting requires the window to reload itself. OK?'; - export function SettingsTab({ started }) { const { theme, @@ -127,11 +125,8 @@ export function SettingsTab({ started }) { isDisabled={started} audioDeviceName={audioDeviceName} onChange={(audioDeviceName) => { - confirmDialog(RELOAD_MSG).then((r) => { - if (r == true) { - settingsMap.setKey('audioDeviceName', audioDeviceName); - return window.location.reload(); - } + confirmAndReloadPage(() => { + settingsMap.setKey('audioDeviceName', audioDeviceName); }); }} /> @@ -141,11 +136,8 @@ export function SettingsTab({ started }) { { - confirmDialog(RELOAD_MSG).then((r) => { - if (r == true) { - settingsMap.setKey('audioEngineTarget', target); - return window.location.reload(); - } + confirmAndReloadPage(() => { + settingsMap.setKey('audioEngineTarget', target); }); }} /> @@ -175,12 +167,9 @@ export function SettingsTab({ started }) { label="Multi Channel Orbits" onChange={(cbEvent) => { const val = cbEvent.target.checked; - confirmDialog(RELOAD_MSG).then((r) => { - if (r == true) { - settingsMap.setKey('multiChannelOrbits', val); - setMultiChannelOrbits(val); - return window.location.reload(); - } + confirmAndReloadPage(() => { + settingsMap.setKey('multiChannelOrbits', val); + setMultiChannelOrbits(val); }); }} value={multiChannelOrbits} @@ -297,11 +286,8 @@ export function SettingsTab({ started }) { label="Sync across Browser Tabs / Windows" onChange={(cbEvent) => { const newVal = cbEvent.target.checked; - confirmDialog(RELOAD_MSG).then((r) => { - if (r) { - settingsMap.setKey('isSyncEnabled', newVal); - window.location.reload(); - } + confirmAndReloadPage(() => { + settingsMap.setKey('isSyncEnabled', newVal); }); }} disabled={shouldAlwaysSync} diff --git a/website/src/repl/util.mjs b/website/src/repl/util.mjs index 4a7cb26a8..06dad2467 100644 --- a/website/src/repl/util.mjs +++ b/website/src/repl/util.mjs @@ -1,4 +1,4 @@ -import { code2hash, evalScope, hash2code, logger } from '@strudel/core'; +import { code2hash, errorLogger, evalScope, hash2code, logger } from '@strudel/core'; import { settingPatterns } from '../settings.mjs'; import { setVersionDefaults } from '@strudel/webaudio'; import { getMetadata } from '../metadata_parser'; @@ -107,7 +107,19 @@ export function confirmDialog(msg) { resolve(confirmed); }); } - +export const SETTING_CHANGE_RELOAD_MSG = 'Changing this setting requires the window to reload itself. OK?'; +export function confirmAndReloadPage(onSuccess) { + confirmDialog(SETTING_CHANGE_RELOAD_MSG).then((r) => { + if (r == true) { + try { + onSuccess(); + return window.location.reload(); + } catch (e) { + errorLogger(e); + } + } + }); +} //RIP due to SPAM // let lastShared; // export async function shareCode(codeToShare) { From 0174e086399ae0411e7f6da31cc3fd2bf8df0840 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 11 Jan 2026 10:50:44 +0100 Subject: [PATCH 078/124] fix: add trem to top level --- packages/core/controls.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 8fef91294..f15b3d4cd 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -877,7 +877,7 @@ export const { coarse } = registerControl('coarse'); * note("d d d# d".fast(4)).s("supersaw").tremolo("<3 2 100> ").tremoloskew("<.5>") * */ -export const { tremolo } = registerControl(['tremolo', 'tremolodepth', 'tremoloskew', 'tremolophase'], 'trem'); +export const { tremolo, trem } = registerControl(['tremolo', 'tremolodepth', 'tremoloskew', 'tremolophase'], 'trem'); /** * Modulate the amplitude of a sound with a continuous waveform From c74707e804f926ffc3910a3e80aa8bda8e86d5a4 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 11 Jan 2026 10:58:13 +0100 Subject: [PATCH 079/124] fix: export start cycle min 0 --- website/src/repl/components/panel/ExportTab.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/repl/components/panel/ExportTab.jsx b/website/src/repl/components/panel/ExportTab.jsx index 9671ff1a3..cbb132e44 100644 --- a/website/src/repl/components/panel/ExportTab.jsx +++ b/website/src/repl/components/panel/ExportTab.jsx @@ -66,7 +66,7 @@ export default function ExportTab(Props) {
{ let v = parseInt(e.target.value); From 8f3dc8daea3759330f4a7bd930a42d5653d500a1 Mon Sep 17 00:00:00 2001 From: yaxu Date: Sun, 11 Jan 2026 11:34:55 +0100 Subject: [PATCH 080/124] Update website/src/pages/learn/faq.mdx --- website/src/pages/learn/faq.mdx | 35 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index b91b48cca..7fbc0d572 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -8,27 +8,26 @@ import { JsDoc } from '../../docs/JsDoc'; # Frequently Asked Questions -This page contains frequently asked questions. Usually, the topic is explained in more detail in a section which is linked in the answer. This page's aim is to give an overview over topics which are important to the users of strudel. +This page contains frequently asked questions, with answers. Usually, the topic is explained in more detail in a section which is linked in the answer. ## Is Strudel/Tidal free? -Yes - there is no charge, this is a collective open source project, and the music you make with it is your own. +Yes - there is no charge, this is a collective open source project, and the music you make with it is your own. However if you can, please make a one-off or regular donation to our [opencollective fund](https://opencollective.com/tidalcycles), that supports the software and cultural development of Strudel and other Uzu languages. -However there are some caveats - the source code must stay free, i.e. you cannot distribute strudel or tidal as part of projects with incompatible licenses - see the [license](https://www.gnu.org/licenses/agpl-3.0.en.html) for details. The contributed examples and tracks are also separately licensed, and must not e.g. be used to train AI models without permission. +While there is no charge there are some caveats, e.g.: + +* the source code must stay free, i.e. you cannot distribute strudel or tidal as part of projects with incompatible licenses - see the [license](https://www.gnu.org/licenses/agpl-3.0.en.html) for details. +* the contributed examples and tracks are also separately licensed, and must not e.g. be used to train AI models without permission. ## How to record or export audio? -There are multiple ways to record the audio -- and video -- output of Strudel: +Strudel is not a digital audio workstation and does not operate following the same principles shared by most traditional audio softwares. However, there are multiple ways to record the audio -- and video -- output of Strudel: - - capture the raw stereo signal coming out of your web browser. - - - use the alternative SuperDirt audio engine. Read [this page](/learn/input-output/#oscsuperdirtstrudeldirt) to know more about it. - - - capture the audio/video stream using a capture tool such as [OBS](). - - - don't record anything and code it again in front of your friends. - -You will need an external audio editor/DAW such as Reaper/Audacity/Ardour, etc. Strudel itself does not have functionality for exporting stems / individual tracks to an audio or midi file. Strudel is not a digital audio workstation and does not operate following the same principles shared by most traditional audio softwares. +* Use the 'export' tab to render and download as an audio file. +* capture the raw stereo signal coming out of your web browser. You will need an external audio editor/DAW such as Reaper/Audacity/Ardour, etc. +* use the alternative SuperDirt audio engine. Read [this page](/learn/input-output/#oscsuperdirtstrudeldirt) to know more about it. +* capture the audio/video stream using a capture tool such as [OBS](), which is designed for live streaming, but also works very well for recording. +* don't record anything and code it again in front of your friends. ## Can I use strudel with my IDE? @@ -37,13 +36,13 @@ Yes you can. There are experimental modes, made by community members, for severa - VS Code: [Strudel VS](https://marketplace.visualstudio.com/items?itemName=cmillsdev.strudelvs): an experimental mode for Microsoft VSCode. A revived version of [TidalStrudel](https://marketplace.visualstudio.com/items?itemName=roipoussiere.tidal-strudel), which is defunct. - nvim: [strudel.nvim](https://github.com/gruvw/strudel.nvim) -## How can I record samples? +## How can I use my own samples? -You can use your own samples with Strudel. There are multiple ways to load your sample collection. Some methods are good for quick experimentation, some others are good to share your audio collection with other musicians: +There are multiple ways to load your sample collection. Some methods are good for quick experimentation, some others are good to share your audio collection with other musicians: - - Import folders [from the interface](/learn/samples/#from-disk-via-import-sounds-folder) - - - - Host your sound library e.g. on github and [load them from an URL](/learn/samples/#loading-custom-samples) +- Import folders [from the interface](/learn/samples/#from-disk-via-import-sounds-folder). These are stored locally in your web browser, and not uploaded. +- Serve a folder of samples locally using the [strudel 'sampler' commandline tool](https://strudel.cc/learn/samples/#from-disk-via-strudelsampler). This can be most reliable method, but requires [nodejs](https://nodejs.org) to be installed. +- Host your sound library online on the web and [load them from an URL](/learn/samples/#loading-custom-samples) ## Can I use Strudel with AI/LLM tools? From 25dbfe4c7d38330892a2e8000eb75f8569b7dcfa Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 11 Jan 2026 11:43:04 +0100 Subject: [PATCH 081/124] fix: remove faulty default readme --- website/README.md | 184 +--------------------------------------------- 1 file changed, 1 insertion(+), 183 deletions(-) diff --git a/website/README.md b/website/README.md index 115d6d556..84bf732e1 100644 --- a/website/README.md +++ b/website/README.md @@ -3,186 +3,4 @@ This is the website for Strudel, deployed at [strudel.cc](https://strudel.cc). It includes the REPL live coding editor and the documentation site. -## Run locally - -```bash -# from project root -npm run setup -npm run repl -``` - -## Build - -```bash -cd website -npm run build # <- builds repl + tutorial to ../docs -npm run preview # <- test static build -``` - -## Generate PWA icons - -```sh -cd website/public -npx pwa-asset-generator icon.png icons -``` - -# Standard Readme of Astro Starter Kit: Docs Site - -```bash -npm create astro@latest -- --template docs -``` - -[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/docs) -[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/s/github/withastro/astro/tree/latest/examples/docs) - -![docs](https://user-images.githubusercontent.com/4677417/186189283-0831b9ab-d6b9-485d-8955-3057e532ab31.png) - - -## Features - -- ✅ **Full Markdown support** -- ✅ **Responsive mobile-friendly design** -- ✅ **Sidebar navigation** -- ✅ **Search (powered by Algolia)** -- ✅ **Multi-language i18n** -- ✅ **Automatic table of contents** -- ✅ **Automatic list of contributors** -- ✅ (and, best of all) **dark mode** - -## Commands Cheatsheet - -All commands are run from the root of the project, from a terminal: - -| Command | Action | -| :--------------------- | :----------------------------------------------- | -| `npm install` | Installs dependencies | -| `npm run dev` | Starts local dev server at `localhost:4321` | -| `npm run build` | Build your production site to `./dist/` | -| `npm run preview` | Preview your build locally, before deploying | -| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | -| `npm run astro --help` | Get help using the Astro CLI | - -To deploy your site to production, check out our [Deploy an Astro Website](https://docs.astro.build/guides/deploy) guide. - -## New to Astro? - -Welcome! Check out [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). - -## Customize This Theme - -edit: removed, as css styles have been replaced with tailwind styles - -### Site metadata - -`src/config.ts` contains several data objects that describe metadata about your site like title, description, default language, and Open Graph details. You can customize these to match your project. - -## Page metadata - -Astro uses frontmatter in Markdown pages to choose layouts and pass properties to those layouts. If you are using the default layout, you can customize the page in many different ways to optimize SEO and other things. For example, you can use the `title` and `description` properties to set the document title, meta title, meta description, and Open Graph description. - -```markdown ---- -title: Example title -description: Really cool docs example that uses Astro -layout: ../../layouts/MainLayout.astro ---- - -# Page content... -``` - -For more SEO related properties, look at `src/components/HeadSEO.astro` - -### Sidebar navigation - -The sidebar navigation is controlled by the `SIDEBAR` variable in your `src/config.ts` file. You can customize the sidebar by modifying this object. A default, starter navigation has already been created for you. - -```ts -export const SIDEBAR = { - en: [ - { text: "Section Header", header: true }, - { text: "Introduction", link: "en/introduction" }, - { text: "Page 2", link: "en/page-2" }, - { text: "Page 3", link: "en/page-3" }, - - { text: "Another Section", header: true }, - { text: "Page 4", link: "en/page-4" }, - ], -}; -``` - -Note the top-level `en` key: This is needed for multi-language support. You can change it to whatever language you'd like, or add new languages as you go. More details on this below. - -### Multiple Languages support - -The Astro docs template supports multiple languages out of the box. The default theme only shows `en` documentation, but you can enable multi-language support features by adding a second language to your project. - -To add a new language to your project, you'll want to extend the current `src/pages/[lang]/...` layout: - -```diff - 📂 src/pages - ┣ 📂 en - ┃ ┣ 📜 page-1.md - ┃ ┣ 📜 page-2.md - ┃ ┣ 📜 page-3.astro -+ ┣ 📂 es -+ ┃ ┣ 📜 page-1.md -+ ┃ ┣ 📜 page-2.md -+ ┃ ┣ 📜 page-3.astro -``` - -You'll also need to add the new language name to the `KNOWN_LANGUAGES` map in your `src/config.ts` file. This will enable your new language switcher in the site header. - -```diff -// src/config.ts -export const KNOWN_LANGUAGES = { - English: 'en', -+ Spanish: 'es', -}; -``` - -Last step: you'll need to add a new entry to your sidebar, to create the table of contents for that language. While duplicating every page might not sound ideal to everyone, this extra control allows you to create entirely custom content for every language. - -> Make sure the sidebar `link` value points to the correct language! - -```diff -// src/config.ts -export const SIDEBAR = { - en: [ - { text: 'Section Header', header: true, }, - { text: 'Introduction', link: 'en/introduction' }, - // ... - ], -+ es: [ -+ { text: 'Encabezado de sección', header: true, }, -+ { text: 'Introducción', link: 'es/introduction' }, -+ // ... -+ ], -}; - -// ... -``` - -If you plan to use Spanish as the default language, you just need to modify the redirect path in `src/pages/index.astro`: - -```diff - -``` - -You can also remove the above script and write a landing page in Spanish instead. - -### What if I don't plan to support multiple languages? - -That's totally fine! Not all projects need (or can support) multiple languages. You can continue to use this theme without ever adding a second language. - -If that single language is not English, you can just replace `en` in directory layouts and configurations with the preferred language. - -### Search (Powered by Algolia) - -[Algolia](https://www.algolia.com/) offers a free service to qualified open source projects called [DocSearch](https://docsearch.algolia.com/). If you are accepted to the DocSearch program, provide your API Key & index name in `src/config.ts` and a search box will automatically appear in your site header. - -Note that Aglolia and Astro are not affiliated. We have no say over acceptance to the DocSearch program. - -If you'd prefer to remove Algolia's search and replace it with your own, check out the `src/components/Header.astro` component to see where the component is added. +more setup info, see [project setup](../CONTRIBUTING.md#project-setup) \ No newline at end of file From f3e6f868577a0f3e3ed50c97c9022a5972ac17e3 Mon Sep 17 00:00:00 2001 From: yaxu Date: Sun, 11 Jan 2026 11:55:40 +0100 Subject: [PATCH 082/124] Update website/src/pages/learn/faq.mdx Trimmed all the DAW talk a little! --- website/src/pages/learn/faq.mdx | 61 +++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index 7fbc0d572..f8ebcb0fb 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -46,21 +46,34 @@ There are multiple ways to load your sample collection. Some methods are good fo ## Can I use Strudel with AI/LLM tools? -You are free to do what you like with Strudel, within the terms of the free/open source AGPLv3 license. However as a community we are interested in exploring human creativity. AI is _way_ over-hyped right now, including by people with very shady motives. Many in the community are very wary of people training models on their tunes that they've poured their love into. So please keep discussion and questions around AI and LLMs to channels dedicated to the topic and be fully respectful of other people's work. +You are free to do what you like with Strudel, within the terms of the free/open source AGPLv3 license. +However as a community we are interested in exploring human creativity. AI is _way_ over-hyped right now, +including by people with very shady motives. Many in the community are very wary of people training models +on their tunes that they've poured their love into. So please keep discussion and questions around AI and +LLMs to channels dedicated to the topic and be fully respectful of other people's work. -Furthermore, tools like ChatGPT generally give wrong answers. Please don't ask the community to fix those answers for you, as generally they will be timewasting nonsense. +Furthermore, tools like ChatGPT generally give wrong answers. Please don't ask the community to fix those +answers for you, as generally they will be timewasting nonsense. -Human questions only! +Human questions are always welcome! + +## Where can I download loads of patterns to train my LLM? + +You cannot, as there is no such place. For details regarding our stance towards AI/LLM, see [above](/learn/faq/#can-i-use-strudel-with-aillm-tools) ## How to run offline? -Strudel works offline just fine! There are multiple techniques to run it yourself, see [this explanation](learn/pwa/#using-strudel-offline). +Strudel works offline just fine! There are multiple techniques for this, see [this explanation](learn/pwa/#using-strudel-offline). ## How to change tempo? How do I translate BPM to cpm? -If you have your tempo in beats per minute and use 4 beats per cycle (e.g. if your track is in 4/4ths) then you can do `setcpm(BPM/4)` where BPM is your beats per minute. +Strudel works in cycles, rather than beats, but if you assume a certain number of beats per cycle, you can convert between them. -If you have a different number of beats per bar or are using more or less beats per cycle (e.g. If you want to put only half a bar or two bars into one cycle), adjust accordingly. +For example, if you have your tempo in beats per minute and use 4 beats per cycle (e.g. if your track is in 4/4ths) then you can do `setcpm(BPM/4)` +where BPM is your beats per minute. + +If you have a different number of beats per bar or are using more or less beats per cycle (e.g. If you want to put only half a bar or +two bars into one cycle), adjust accordingly. ## Where can I see all the functions? @@ -72,11 +85,17 @@ If you pop open the sidetab of strudel.cc (small white < on the right hand side) ## How do I use this exactly like a DAW? -Short answer: you don't. +Strudel has different design aims for a DAW, and so treating it like one will likely be frustrating. DAWs are geared towards +sequencing notes over time in predictable ways, whereas Strudel and similar Uzu languages are geared towards combining and +transforming patterns in ways that can be hard to predict. -Long answer: you can use Strudel to work along your creative work in a DAW. There are many ways to do so. +If you want to emulate the functionality of a DAW in Strudel, you'll have to identify the operations +executed by the DAW (sequencing, repeating, applying filters and envelopes) and write code that is equivalent to these +operations. For example in Strudel, the 'arrange' and 'pick' methods are useful for sequencing patterns over time (see question on these later in this document). -If you want to emulate the functionality of a DAW in a live coding language, you'll have to identify the operations executed by the DAW (sequencing, repeating, applying filters and envelopes) and write code that is equivalent to these operations. You might then find that the typical DAW workflow is not really adapted to live coding (because, despite both being ways of making music on the computer, they are two very different tools) and adapt your way of proceeding to the medium of code. This might mean leaving more place to serendipity and writing code that you don't predict the output of. +You might still find that the typical DAW workflow is not really adapted to live coding because, despite +both being ways of making music on the computer, they are two very different tools. You could then adapt your way of proceeding +to the medium of code, which might mean leaving more place to serendipity and writing code that you don't predict the output of. ## Why doesn't everyone just use a DAW? @@ -84,39 +103,29 @@ There is no easy answer to this question. Here are some thoughts: - Live coding tools such as Strudel are excellent for improvising music and visuals using a computer. DAWs are valuable and robust companions for other activities such as producing, mastering and mixing audio, among other usages. Using a tool does not exclude from using any another tool, just build a toolbox. -- Live coding has been practiced for quite some time as a performative activity. Artists like to show their screens while playing in front of an audience. It is an essential part of what they do, of the way they share their activity with everybody. +- Live coding has developed over decades as a distinct creative practice. For example, live coding artists like to show their screens while playing in front of an audience. It is an essential part of what they do, of the way they share their activity with everybody. -- Code is a human language, it is made for other humans to read it. You can read the code and enjoy the music too. It has meaning, value, and there might even be something poetic/important about it! - Strudel is free and open source, you can inspect the code, reshape it, contribute to it if you can/want. It is not opaque and this matters for many people. There is no black box, no obscure abstractions, no business model or hidden features. We need open tools in the arts! - Live coders don't shy away from using DAWs. They use them all of the time, especially when it makes their life easier for... live coding! +- Code is a human language, it is made for other humans to read it. You can read the code and enjoy the music too. It has meaning, value, and there might even be something poetic/important about it! - Strudel is free and open source, you can inspect the code, reshape it, contribute to it if you can/want. It is not opaque and this matters for many people. There is no black box, no obscure abstractions, no business model, no user tracking or hidden features. We need open tools in the arts! - Live coders don't all shy away from using DAWs. Many use them all of the time, especially when it makes their life easier for... live coding! - Code is an artistic material like any other. There is something valuable in the process of making music through code. More generally speaking, it is nice to tackle creative problems through the use of a programming language: creative thinking, building up your own solutions, DIY approach to music-making, unexpected outcome of algorithms, funny human errors, etc. - There are pianos and trumpets in your DAW: why do people continue playing the piano or the trumpet? Think of live coding tools as instruments that you activate through the act of programming. -## Is it more efficient to use Strudel than a DAW? - -Strudel was not build to be a DAW, yet it can still be used to make covers, arrange tracks, or prepare patterns for jamming. When playing concerts or jamming, some livecoders prepare their code, some perform from scratch. - -It might be interesting for you to check out for yourself how strudel can be used to express yourself creatively. Also you are free to combine a language like Strudel with a DAW. - ## How can I interface Strudel with my favorite music software? What can I do with it? Strudel can send [MIDI and OSC](/learn/input-output/), which are protocols for communicating musical information. -Another music software (or hardware!) can then listen to these messages and process them according to its capabilities. +Other music software (or hardware!) can then listen to these messages and process them according to its capabilities. -A simple example would be to send livecoded audio to Ableton on different tracks and then use it to mix them. +A simple example would be to send livecoded audio to a DAW like Ardour on different tracks and then use it to mix them. You could also send the MIDI of a sequenced pattern to Musescore and then have it transcribe your livecoded work as a musical score. -You could also send MIDI to your hardware synths because you think they sound better than the software synths built-in Strudel. +You could also send MIDI to your hardware synths, if you like their sound. -## How do I use this in my closed source webgame? +## How do I use this in my closed source webgame or other software? -You don't. You would need to re-license your game to AGPLv3 to fulfill the license Strudel is distributed under. - -## Where can I download loads of patterns to train my LLM? - -You cannot, as there is no such place. For details regarding our stance towards AI/LLM, see [above](/learn/faq/#can-i-use-strudel-with-aillm-tools) +You don't. You need to license your game to a free/open source license fulfill the [AGPLv3 license](https://codeberg.org/uzu/strudel/src/branch/main/LICENSE) Strudel is distributed under. ## How to play different patterns simultaneously? From 85fd567fb2e2ac40e1aac886af103dc83744201c Mon Sep 17 00:00:00 2001 From: yaxu Date: Sun, 11 Jan 2026 12:21:05 +0100 Subject: [PATCH 083/124] Format and remove link to open document --- website/src/pages/learn/faq.mdx | 46 ++++++++++++++------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index f8ebcb0fb..e43839dc3 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -16,18 +16,18 @@ Yes - there is no charge, this is a collective open source project, and the musi While there is no charge there are some caveats, e.g.: -* the source code must stay free, i.e. you cannot distribute strudel or tidal as part of projects with incompatible licenses - see the [license](https://www.gnu.org/licenses/agpl-3.0.en.html) for details. -* the contributed examples and tracks are also separately licensed, and must not e.g. be used to train AI models without permission. +- the source code must stay free, i.e. you cannot distribute strudel or tidal as part of projects with incompatible licenses - see the [license](https://www.gnu.org/licenses/agpl-3.0.en.html) for details. +- the contributed examples and tracks are also separately licensed, and must not e.g. be used to train AI models without permission. ## How to record or export audio? Strudel is not a digital audio workstation and does not operate following the same principles shared by most traditional audio softwares. However, there are multiple ways to record the audio -- and video -- output of Strudel: -* Use the 'export' tab to render and download as an audio file. -* capture the raw stereo signal coming out of your web browser. You will need an external audio editor/DAW such as Reaper/Audacity/Ardour, etc. -* use the alternative SuperDirt audio engine. Read [this page](/learn/input-output/#oscsuperdirtstrudeldirt) to know more about it. -* capture the audio/video stream using a capture tool such as [OBS](), which is designed for live streaming, but also works very well for recording. -* don't record anything and code it again in front of your friends. +- Use the 'export' tab to render and download as an audio file. +- capture the raw stereo signal coming out of your web browser. You will need an external audio editor/DAW such as Reaper/Audacity/Ardour, etc. +- use the alternative SuperDirt audio engine. Read [this page](/learn/input-output/#oscsuperdirtstrudeldirt) to know more about it. +- capture the audio/video stream using a capture tool such as [OBS](https://obsproject.com/fr), which is designed for live streaming, but also works very well for recording. +- don't record anything and code it again in front of your friends. ## Can I use strudel with my IDE? @@ -40,19 +40,19 @@ Yes you can. There are experimental modes, made by community members, for severa There are multiple ways to load your sample collection. Some methods are good for quick experimentation, some others are good to share your audio collection with other musicians: -- Import folders [from the interface](/learn/samples/#from-disk-via-import-sounds-folder). These are stored locally in your web browser, and not uploaded. +- Import folders [from the interface](/learn/samples/#from-disk-via-import-sounds-folder). These are stored locally in your web browser, and not uploaded. - Serve a folder of samples locally using the [strudel 'sampler' commandline tool](https://strudel.cc/learn/samples/#from-disk-via-strudelsampler). This can be most reliable method, but requires [nodejs](https://nodejs.org) to be installed. - Host your sound library online on the web and [load them from an URL](/learn/samples/#loading-custom-samples) ## Can I use Strudel with AI/LLM tools? -You are free to do what you like with Strudel, within the terms of the free/open source AGPLv3 license. -However as a community we are interested in exploring human creativity. AI is _way_ over-hyped right now, -including by people with very shady motives. Many in the community are very wary of people training models -on their tunes that they've poured their love into. So please keep discussion and questions around AI and +You are free to do what you like with Strudel, within the terms of the free/open source AGPLv3 license. +However as a community we are interested in exploring human creativity. AI is _way_ over-hyped right now, +including by people with very shady motives. Many in the community are very wary of people training models +on their tunes that they've poured their love into. So please keep discussion and questions around AI and LLMs to channels dedicated to the topic and be fully respectful of other people's work. -Furthermore, tools like ChatGPT generally give wrong answers. Please don't ask the community to fix those +Furthermore, tools like ChatGPT generally give wrong answers. Please don't ask the community to fix those answers for you, as generally they will be timewasting nonsense. Human questions are always welcome! @@ -69,7 +69,7 @@ Strudel works offline just fine! There are multiple techniques for this, see [th Strudel works in cycles, rather than beats, but if you assume a certain number of beats per cycle, you can convert between them. -For example, if you have your tempo in beats per minute and use 4 beats per cycle (e.g. if your track is in 4/4ths) then you can do `setcpm(BPM/4)` +For example, if you have your tempo in beats per minute and use 4 beats per cycle (e.g. if your track is in 4/4ths) then you can do `setcpm(BPM/4)` where BPM is your beats per minute. If you have a different number of beats per bar or are using more or less beats per cycle (e.g. If you want to put only half a bar or @@ -85,16 +85,16 @@ If you pop open the sidetab of strudel.cc (small white < on the right hand side) ## How do I use this exactly like a DAW? -Strudel has different design aims for a DAW, and so treating it like one will likely be frustrating. DAWs are geared towards -sequencing notes over time in predictable ways, whereas Strudel and similar Uzu languages are geared towards combining and +Strudel has different design aims for a DAW, and so treating it like one will likely be frustrating. DAWs are geared towards +sequencing notes over time in predictable ways, whereas Strudel and similar Uzu languages are geared towards combining and transforming patterns in ways that can be hard to predict. -If you want to emulate the functionality of a DAW in Strudel, you'll have to identify the operations -executed by the DAW (sequencing, repeating, applying filters and envelopes) and write code that is equivalent to these +If you want to emulate the functionality of a DAW in Strudel, you'll have to identify the operations +executed by the DAW (sequencing, repeating, applying filters and envelopes) and write code that is equivalent to these operations. For example in Strudel, the 'arrange' and 'pick' methods are useful for sequencing patterns over time (see question on these later in this document). -You might still find that the typical DAW workflow is not really adapted to live coding because, despite -both being ways of making music on the computer, they are two very different tools. You could then adapt your way of proceeding +You might still find that the typical DAW workflow is not really adapted to live coding because, despite +both being ways of making music on the computer, they are two very different tools. You could then adapt your way of proceeding to the medium of code, which might mean leaving more place to serendipity and writing code that you don't predict the output of. ## Why doesn't everyone just use a DAW? @@ -329,9 +329,3 @@ s increase by one semitone, i.e. sharp, works for steps of scales, note names $: at the start of a line, defines a member of the stack. is the only stack name that should occur multiple names _ before a stack name: mutes the stack, i.e. hush(), for example _$: s("bd"), see above for a different usage. ``` - -## Are there more FAQ items? - -These pages have been taken from [this pad](https://doc.patternclub.org/_CgofWouTciXXHexUP9AVg?both). Some of the items there have not been brushed up and brought here. - -These include the following items: 9, 11, 12 and 19 From 550d7ff6cb84fe3273256c2153cb869bc27d1263 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 11 Jan 2026 12:32:54 +0100 Subject: [PATCH 084/124] fix: react error --- website/src/repl/components/panel/Reference.jsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index 3053f03dc..34701036d 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react'; +import { useMemo, useState, Fragment } from 'react'; import jsdocJson from '../../../../../doc.json'; import { Textbox } from '../textbox/Textbox'; @@ -109,9 +109,8 @@ export function Reference() {
From 9ffd0e496fcb71fcbc46add25eef7e40f56d4fb6 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 11 Jan 2026 12:33:03 +0100 Subject: [PATCH 085/124] fix: all doc --- packages/core/repl.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index 047989f7f..9fe8eef81 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -120,7 +120,9 @@ export function repl({ // TODO - not documented as jsdoc examples as the test framework doesn't simulate enough context for `each` and `all`.. - /** Applies a function to all the running patterns. Note that the patterns are groups together into a single `stack` before the function is applied. This is probably what you want, but see `each` for + let allTransforms = []; + /** + * Applies a function to all the running patterns. Note that the patterns are groups together into a single `stack` before the function is applied. This is probably what you want, but see `each` for * a version that applies the function to each pattern separately. * ``` * $: sound("bd - cp sd") @@ -135,7 +137,6 @@ export function repl({ * * @tags combiners */ - let allTransforms = []; const all = function (transform) { allTransforms.push(transform); return silence; From a659c89385b84e08c001c47669fb98253ffb55f9 Mon Sep 17 00:00:00 2001 From: floy Date: Sun, 11 Jan 2026 13:25:44 +0100 Subject: [PATCH 086/124] fix code formatting --- website/src/repl/components/panel/SoundsTab.jsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/website/src/repl/components/panel/SoundsTab.jsx b/website/src/repl/components/panel/SoundsTab.jsx index 2060c64a9..5a7df5ba9 100644 --- a/website/src/repl/components/panel/SoundsTab.jsx +++ b/website/src/repl/components/panel/SoundsTab.jsx @@ -210,10 +210,11 @@ export function SoundsTab() { ) : ( '' )} - {!soundEntries.length && soundsFilter !== 'importSounds' ? - search == '' ? 'No sounds loaded' : 'No sounds found' - : '' - } + {!soundEntries.length && soundsFilter !== 'importSounds' + ? search == '' + ? 'No sounds loaded' + : 'No sounds found' + : ''}
); From 8d2bca59c4373a74d6f05ecb9432b5384135109c Mon Sep 17 00:00:00 2001 From: JesCoding Date: Wed, 24 Dec 2025 11:39:28 +0100 Subject: [PATCH 087/124] Fix sounds example to work in the REPL `freq` only takes one argument, so switch this to mini-notation so this can be used in the REPL as indicated in the paragraph below. Make it so this can be played by the user. Fix syntax-type typo. --- website/src/pages/technical-manual/sounds.mdx | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/website/src/pages/technical-manual/sounds.mdx b/website/src/pages/technical-manual/sounds.mdx index 11078713f..73ae05796 100644 --- a/website/src/pages/technical-manual/sounds.mdx +++ b/website/src/pages/technical-manual/sounds.mdx @@ -13,7 +13,7 @@ Let's take a closer look about how sounds are implemented in the webaudio output All sounds are registered in the sound map, using the the `registerSound` function: -```ts +```js function registerSound( name: string, // The name of the sound that should be given to `s`, e.g. `mysaw` // The function called by the scheduler to trigger the sound: @@ -35,34 +35,36 @@ When `registerSound` is called, it registers `{ onTrigger, data }` under the giv This might be a bit abstract, so here is a minimal example: -```js -registerSound( - 'mysaw', - (time, value, onended) => { - let { freq } = value; // destructure control params - const ctx = getAudioContext(); - // create oscillator - const o = new OscillatorNode(ctx, { type: 'sawtooth', frequency: Number(freq) }); - o.start(time); - // add gain node to level down osc - const g = new GainNode(ctx, { gain: 0.3 }); - // connect osc to gain - const node = o.connect(g); - // this function can be called from outside to stop the sound - const stop = (time) => o.stop(time); - // ended will be fired when stop has been fired - o.addEventListener('ended', () => { - o.disconnect(); - g.disconnect(); - onended(); - }); - return { node, stop }; - }, - { type: 'synth' }, -); -// use the sound -freq(220, 440, 330).s('mysaw'); -``` + { + let { freq } = value; // destructure control params + const ctx = getAudioContext(); + // create oscillator + const o = new OscillatorNode(ctx, { type: 'sawtooth', frequency: Number(freq) }); + o.start(time); + // add gain node to level down osc + const g = new GainNode(ctx, { gain: 0.3 }); + // connect osc to gain + const node = o.connect(g); + // this function can be called from outside to stop the sound + const stop = (time) => o.stop(time); + // ended will be fired when stop has been fired + o.addEventListener('ended', () => { + o.disconnect(); + g.disconnect(); + onended(); + }); + return { node, stop }; + }, + { type: 'synth' }, + ); + // use the sound + freq("220 440 330").s('mysaw');`} +/> You can actually use this code in the [REPL](https://strudel.cc/) and it'll work. After evaluating the code, you should see `mysaw` in listed in the sounds tab. From 31cacc3c29a18e59761cda66e912c8ee8e00d8a3 Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 11 Jan 2026 16:23:17 +0000 Subject: [PATCH 088/124] add warm to faq --- website/src/pages/learn/faq.mdx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/website/src/pages/learn/faq.mdx b/website/src/pages/learn/faq.mdx index e43839dc3..fc3bcdb2d 100644 --- a/website/src/pages/learn/faq.mdx +++ b/website/src/pages/learn/faq.mdx @@ -19,6 +19,14 @@ While there is no charge there are some caveats, e.g.: - the source code must stay free, i.e. you cannot distribute strudel or tidal as part of projects with incompatible licenses - see the [license](https://www.gnu.org/licenses/agpl-3.0.en.html) for details. - the contributed examples and tracks are also separately licensed, and must not e.g. be used to train AI models without permission. +## How do I try out the latest features? + +The main, stable strudel website is [strudel.cc](https://strudel.cc/). There is also [warm.strudel.cc](https://warm.strudel.cc), known as "warm strudel", which has the latest development features. You might find warm strudel has bug fixes and features that the main website doesn't, but it will often be less stable and probably not suitable for important performances. + +Alternatively, you can run strudel locally to try out the latest features. You can find development-oriented [instructions for that here](https://codeberg.org/uzu/strudel/src/branch/main/CONTRIBUTING.md#project-setup). + +You can see the [latest changes here](https://codeberg.org/uzu/strudel/pulls?q=&type=all&sort=recentupdate&state=closed&labels=&milestone=0&project=0&assignee=0&poster=0), as 'pull requests'. + ## How to record or export audio? Strudel is not a digital audio workstation and does not operate following the same principles shared by most traditional audio softwares. However, there are multiple ways to record the audio -- and video -- output of Strudel: From 753dc48b69ef800d21efe4541a6126b30e9f292e Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 11 Jan 2026 19:12:45 +0100 Subject: [PATCH 089/124] update changelog --- CHANGELOG.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a13e379d9..19a502f33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,62 @@ NOTE: you can generate this with `node warm.js`. it might still not be perfectly sorted... +## january 2026 + +- 2026-01-11T19:00:25+01:00 Fix sounds example to work in the REPL by JesCoding in: [#1851](https://codeberg.org/uzu/strudel/pulls/1851) +- 2026-01-11T13:52:02+01:00 Improve hint text when sound search has no results by floy in: [#1883](https://codeberg.org/uzu/strudel/pulls/1883) +- 2026-01-11T12:45:07+01:00 fix: remove faulty default readme by froos in: [#1889](https://codeberg.org/uzu/strudel/pulls/1889) +- 2026-01-11T12:15:34+01:00 fix/self-hosted-config by alienmind in: [#1880](https://codeberg.org/uzu/strudel/pulls/1880) +- 2026-01-11T12:07:48+01:00 Feat: Support External AudioContext Injection by 1d10t in: [#1833](https://codeberg.org/uzu/strudel/pulls/1833) +- 2026-01-11T11:37:37+01:00 fix: add trem to top level by froos in: [#1887](https://codeberg.org/uzu/strudel/pulls/1887) +- 2026-01-11T11:37:18+01:00 fix: export start cycle min 0 by froos in: [#1888](https://codeberg.org/uzu/strudel/pulls/1888) +- 2026-01-11T11:05:44+01:00 fix: repl package init audio properly by froos in: [#1836](https://codeberg.org/uzu/strudel/pulls/1836) +- 2026-01-11T06:51:24+01:00 Fix: show reload dialog when uploading prebake script by daslyfe in: [#1886](https://codeberg.org/uzu/strudel/pulls/1886) +- 2026-01-11T06:01:42+01:00 fixes Serial onTrigger() params #1633 by gueejla in: [#1885](https://codeberg.org/uzu/strudel/pulls/1885) +- 2026-01-10T20:52:40+01:00 Allow top level distortions for the purpose of FX by glossing in: [#1884](https://codeberg.org/uzu/strudel/pulls/1884) +- 2026-01-10T23:12:48+01:00 Perf: Targeted node pools by glossing in: [#1810](https://codeberg.org/uzu/strudel/pulls/1810) +- 2026-01-09T03:43:37+01:00 Bake in scaling by `freq` for FM with a gain node by glossing in: [#1878](https://codeberg.org/uzu/strudel/pulls/1878) +- 2026-01-09T02:53:38+01:00 Bug fix: Properly handle subcontrols by glossing in: [#1877](https://codeberg.org/uzu/strudel/pulls/1877) +- 2026-01-07T20:06:18+01:00 Feat: Add ability to turn mini parsing off with mini-off decorator by glossing in: [#1786](https://codeberg.org/uzu/strudel/pulls/1786) +- 2026-01-07T19:22:24+01:00 Bug Fix: Update loopStart/End to not be offset by glossing in: [#1826](https://codeberg.org/uzu/strudel/pulls/1826) +- 2026-01-06T06:35:49+01:00 Update modulator docstrings and allow ids to be patterns by glossing in: [#1874](https://codeberg.org/uzu/strudel/pulls/1874) +- 2026-01-04T17:14:11+01:00 Fix doc link in @strudel/osc README.md by forrcaho in: [#1872](https://codeberg.org/uzu/strudel/pulls/1872) +- 2026-01-04T02:01:07+01:00 Make stretch modulatable by glossing in: [#1870](https://codeberg.org/uzu/strudel/pulls/1870) +- 2026-01-02T18:44:53+01:00 Make pan modulatable by glossing in: [#1865](https://codeberg.org/uzu/strudel/pulls/1865) +- 2026-01-01T21:42:23+01:00 Fix transpilation example to have same mini-notation by JesCoding in: [#1850](https://codeberg.org/uzu/strudel/pulls/1850) +- 2026-01-01T21:39:05+01:00 fix: missing punctuation by eddyflux in: [#1860](https://codeberg.org/uzu/strudel/pulls/1860) +- 2026-01-01T20:20:34+01:00 Fix formatting of docstring by glossing in: [#1864](https://codeberg.org/uzu/strudel/pulls/1864) +- 2026-01-01T19:52:15+01:00 Feat: FX Chains by glossing in: [#1861](https://codeberg.org/uzu/strudel/pulls/1861) + +## december 2025 + +- 2025-12-29T21:59:11+01:00 Bugfix: Fix modulator clamping when min/max not specified by glossing in: [#1859](https://codeberg.org/uzu/strudel/pulls/1859) +- 2025-12-29T16:07:18+01:00 Fix AudioContext change detection. Use AudioNode.context by jeromew in: [#1858](https://codeberg.org/uzu/strudel/pulls/1858) +- 2025-12-28T14:20:03+01:00 dough repl fixes by froos in: [#1855](https://codeberg.org/uzu/strudel/pulls/1855) +- 2025-12-19T08:11:50+01:00 fix: visual block selection mode for vim bindings by Dsm0 in: [#1839](https://codeberg.org/uzu/strudel/pulls/1839) +- 2025-12-28T22:58:38+01:00 Say that @license should use SPDX identifier by Wuzzy in: [#1817](https://codeberg.org/uzu/strudel/pulls/1817) +- 2025-12-28T22:56:07+01:00 Expose Vim object in order to create custom keybindings by JohnBjrk in: [#1816](https://codeberg.org/uzu/strudel/pulls/1816) +- 2025-12-20T22:27:52+01:00 Document "-" in mini-notation by Wuzzy in: [#1818](https://codeberg.org/uzu/strudel/pulls/1818) +- 2025-12-20T22:20:27+01:00 simplify envValAtTime and remove asymmetric behavior (fix #1653) by pulu in: [#1815](https://codeberg.org/uzu/strudel/pulls/1815) +- 2025-12-14T15:09:12+01:00 delta -> per / perx / cyclesPer refinements by yaxu in: [#1832](https://codeberg.org/uzu/strudel/pulls/1832) +- 2025-12-14T19:33:37+01:00 Fix: wrong warning in build environments by jeromew in: [#1835](https://codeberg.org/uzu/strudel/pulls/1835) +- 2025-12-12T10:28:27+01:00 Add delta signal for representing the duration of events in patterns that are combined with it by yaxu in: [#1831](https://codeberg.org/uzu/strudel/pulls/1831) +- 2025-12-11T16:37:58+01:00 Updates relating to LLM, github, etc by yaxu in: [#1830](https://codeberg.org/uzu/strudel/pulls/1830) +- 2025-12-10T15:32:44+01:00 fix .as so it doesn't set undefined values by yaxu in: [#1827](https://codeberg.org/uzu/strudel/pulls/1827) +- 2025-12-08T19:27:31+01:00 [perf] propagate `onceEnded` and `releaseAudioNode` by jeromew in: [#1809](https://codeberg.org/uzu/strudel/pulls/1809) +- 2025-12-07T19:15:43+01:00 Add vel as a synonym for velocity & update a few docstrings by glossing in: [#1781](https://codeberg.org/uzu/strudel/pulls/1781) +- 2025-12-07T18:53:04+01:00 [perf] fix phaser leak of unused biquads by jeromew in: [#1800](https://codeberg.org/uzu/strudel/pulls/1800) +- 2025-12-07T18:42:50+01:00 Bug fix: Remove failing tests due to shabda removal by glossing in: [#1820](https://codeberg.org/uzu/strudel/pulls/1820) +- 2025-12-03T17:35:05+01:00 [perf] release unused AudioBufferSourceNode + releaseAudioNode by jeromew in: [#1805](https://codeberg.org/uzu/strudel/pulls/1805) +- **2025-12-01 strudel.cc deployed** + ## november 2025 +- 2025-11-29T01:00:42+01:00 added export to getSuperdoughAudioController() so that its possible to route superdough audio through other webaudio applications. by ndr0n in: [#1796](https://codeberg.org/uzu/strudel/pulls/1796) +- 2025-11-28T23:26:20+01:00 add revv() for reversing whole patterns by yaxu in: [#1791](https://codeberg.org/uzu/strudel/pulls/1791) +- 2025-11-28T20:19:16+01:00 [perf] Disconnect lfos for phaser and filters by glossing in: [#1787](https://codeberg.org/uzu/strudel/pulls/1787) +- 2025-11-27T23:08:52+01:00 fix: return silence when no pattern is returned by froos in: [#1795](https://codeberg.org/uzu/strudel/pulls/1795) +- 2025-11-27T22:38:05+01:00 add CHANGELOG.md + basic script to generate by froos in: [#1794](https://codeberg.org/uzu/strudel/pulls/1794) - 2025-11-27T22:03:53+01:00 [perf] in `noise`, let noiseMix do the disconnect when it exists by jeromew in: [#1783](https://codeberg.org/uzu/strudel/pulls/1783) - 2025-11-27T22:01:01+01:00 Bug Fix: Retries for sounds tab by glossing in: [#1754](https://codeberg.org/uzu/strudel/pulls/1754) - 2025-11-27T20:36:46+01:00 [hydra] return the hydra object when await initHydra(..) is called by jeromew in: [#1784](https://codeberg.org/uzu/strudel/pulls/1784) @@ -52,6 +106,7 @@ NOTE: you can generate this with `node warm.js`. it might still not be perfectly - 2025-10-28T22:51:16+01:00 Repurpose vim shortcuts for usability by dtricks in: [#1624](https://codeberg.org/uzu/strudel/pulls/1624) - 2025-10-28T22:35:21+01:00 Add support for euclidian in mondo with `bd&3:8` by TristanCacqueray in: [#1630](https://codeberg.org/uzu/strudel/pulls/1630) - 2025-10-27T10:42:07+01:00 Use bunny cdn for all samples and json files by yaxu in: [#1701](https://codeberg.org/uzu/strudel/pulls/1701) +- **2025-10-27 @strudel/core@1.2.5** - 2025-10-26T22:52:54+01:00 degithub - switch some samples to bunnycdn by yaxu in: [#1697](https://codeberg.org/uzu/strudel/pulls/1697) - 2025-10-26T17:09:22+01:00 Fix ZZFX example by moumar in: [#1685](https://codeberg.org/uzu/strudel/pulls/1685) - 2025-10-23T15:56:04+02:00 Make osc port and host configurable. Changes dependencies. by yaxu in: [#1682](https://codeberg.org/uzu/strudel/pulls/1682) From 12cc1e9b5ea22017bacfce974e2c5aad0ca7eaa6 Mon Sep 17 00:00:00 2001 From: Aria Date: Sun, 11 Jan 2026 14:51:47 -0600 Subject: [PATCH 090/124] Add immediate triggering; move time to schedulerState --- packages/core/index.mjs | 2 +- packages/core/repl.mjs | 4 +- .../core/{time.mjs => schedulerState.mjs} | 22 ++++++- packages/midi/midi.mjs | 59 +++++++++++++++++-- 4 files changed, 79 insertions(+), 8 deletions(-) rename packages/core/{time.mjs => schedulerState.mjs} (70%) diff --git a/packages/core/index.mjs b/packages/core/index.mjs index 9260e3671..542a54243 100644 --- a/packages/core/index.mjs +++ b/packages/core/index.mjs @@ -22,7 +22,7 @@ export * from './repl.mjs'; export * from './signal.mjs'; export * from './speak.mjs'; export * from './state.mjs'; -export * from './time.mjs'; +export * from './schedulerState.mjs'; export * from './timespan.mjs'; export * from './ui.mjs'; export * from './util.mjs'; diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index 71f68b809..e39b57de3 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -2,7 +2,7 @@ import { NeoCyclist } from './neocyclist.mjs'; import { Cyclist } from './cyclist.mjs'; import { evaluate as _evaluate } from './evaluate.mjs'; import { errorLogger, logger } from './logger.mjs'; -import { setCpsFunc, setTime } from './time.mjs'; +import { setCpsFunc, setPattern as exposeSchedulerPattern, setTime, setTriggerFunc } from './schedulerState.mjs'; import { evalScope } from './evaluate.mjs'; import { register, Pattern, isPattern, silence, stack } from './pattern.mjs'; import { reset_state } from './impure.mjs'; @@ -65,6 +65,7 @@ export function repl({ // NeoCyclist uses a shared worker to communicate between instances, which is not supported on mobile chrome const scheduler = sync && typeof SharedWorker != 'undefined' ? new NeoCyclist(schedulerOptions) : new Cyclist(schedulerOptions); + setTriggerFunc(schedulerOptions.onTrigger); setCpsFunc(() => scheduler.cps); let pPatterns = {}; let anonymousIndex = 0; @@ -90,6 +91,7 @@ export function repl({ const setPattern = async (pattern, autostart = true) => { pattern = editPattern?.(pattern) || pattern; await scheduler.setPattern(pattern, autostart); + exposeSchedulerPattern(pattern); return pattern; }; setTime(() => scheduler.now()); // TODO: refactor? diff --git a/packages/core/time.mjs b/packages/core/schedulerState.mjs similarity index 70% rename from packages/core/time.mjs rename to packages/core/schedulerState.mjs index 2d4caecf5..97a4f56dc 100644 --- a/packages/core/time.mjs +++ b/packages/core/schedulerState.mjs @@ -1,11 +1,13 @@ /* -time.mjs - Core time module. Used to expose parameters from the Scheduler like `time` and `cps` -Copyright (C) 2026 Strudel contributors - see +schedulerState.mjs - Module to pipe out various parameters from the scheduler for global consumption +Copyright (C) 2026 Strudel contributors - see This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ let time; let cpsFunc; +let pattern; +let triggerFunc; export function getTime() { if (!time) { throw new Error('no time set! use setTime to define a time source'); @@ -24,3 +26,19 @@ export function setCpsFunc(func) { export function getCps() { return cpsFunc?.(); } + +export function setPattern(pat) { + pattern = pat; +} + +export function getPattern() { + return pattern; +} + +export function setTriggerFunc(func) { + triggerFunc = func; +} + +export function getTriggerFunc() { + return triggerFunc; +} diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 6233bf60d..ad6b799dc 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -5,9 +5,22 @@ This program is free software: you can redistribute it and/or modify it under th */ import * as _WebMidi from 'webmidi'; -import { Hap, Pattern, TimeSpan, getCps, getTime, isPattern, logger, ref, reify } from '@strudel/core'; +import { + Hap, + Pattern, + TimeSpan, + getCps, + getPattern, + getTime, + getTriggerFunc, + isPattern, + logger, + ref, + reify, +} from '@strudel/core'; import { noteToMidi, getControlName } from '@strudel/core'; import { Note } from 'webmidi'; +import { getAudioContext } from '@strudel/webaudio'; import { scheduleAtTime } from '../superdough/helpers.mjs'; // if you use WebMidi from outside of this package, make sure to import that instance: @@ -575,6 +588,33 @@ export async function midin(input) { */ const kHaps = {}; const kListeners = {}; + +function triggerKbNow(input, cps, now, latencyCycles) { + const pattern = getPattern(); + const trigger = getTriggerFunc(); + if (!pattern || !trigger) { + return false; + } + const t = now + latencyCycles; + const eps = 1e-6; + const haps = pattern.queryArc(t - eps, t + eps, { _cps: cps }); + // Only keep haps coming from `midikeys` + const kbHaps = haps.filter((hap) => hap.value?.midikey?.startsWith(`${input}_`)); + const ctxNow = getAudioContext().currentTime; + if (!kbHaps.length) { + return false; + } + kbHaps.forEach((hap) => { + if (!hap.hasOnset()) { + return; + } + const t = ctxNow + (hap.whole.begin - now) / cps; + const duration = hap.duration / cps; + trigger(hap, t - ctxNow, duration, cps, t); + }); + + return true; +} export async function midikeys(input) { const device = await _initialize(input); if (!kHaps[input]) { @@ -584,11 +624,14 @@ export async function midikeys(input) { kListeners[input] = (e) => { const { dataBytes, message } = e; const [note, velocity] = dataBytes; - const noteoff = message.command === 8; + const noteon = message.command === 9; + const noteoff = message.command === 8 || (noteon && velocity === 0); const key = `${input}_${note}`; const cps = getCps() ?? 0.5; - const latencySeconds = 0.06; // slight delay so it's not too late for cyclist to catch - const t = getTime() + latencySeconds * cps; + const triggerAvailable = !!(getPattern() && getTriggerFunc()); + const latencySeconds = triggerAvailable ? 0.01 : 0.06; // avoid missing notes due to cyclist / trigger latency + const now = getTime(); + const t = now + latencySeconds * cps; const span = new TimeSpan(t, t); let value = { midikey: key }; if (noteoff) { @@ -609,6 +652,14 @@ export async function midikeys(input) { value = { ...value, note: Math.round(note), velocity: velocity / 127 }; } kHaps[input].push(new Hap(span, span, value, {})); + if (!noteoff && triggerAvailable) { + // If we have access to a trigger function, we call it to immediately + // dispatch to the audio engine, rather than waiting for cyclist to catch these haps + const triggered = triggerKbNow(input, cps, now, latencySeconds * cps); + if (triggered) { + kHaps[input] = []; + } + } }; device.addListener('midimessage', kListeners[input]); const kb = (noteLength = 0.5) => { From 5355a0c563bea7b0e98dbfbfbff65c7a60f00b01 Mon Sep 17 00:00:00 2001 From: Aria Date: Sun, 11 Jan 2026 23:34:52 -0600 Subject: [PATCH 091/124] Ignore non-note messages --- packages/midi/midi.mjs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index ad6b799dc..50a58b059 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -589,7 +589,7 @@ export async function midin(input) { const kHaps = {}; const kListeners = {}; -function triggerKbNow(input, cps, now, latencyCycles) { +function _triggerKeyboard(input, cps, now, latencyCycles) { const pattern = getPattern(); const trigger = getTriggerFunc(); if (!pattern || !trigger) { @@ -623,9 +623,14 @@ export async function midikeys(input) { kListeners[input] && device.removeListener('midimessage', kListeners[input]); kListeners[input] = (e) => { const { dataBytes, message } = e; - const [note, velocity] = dataBytes; const noteon = message.command === 9; - const noteoff = message.command === 8 || (noteon && velocity === 0); + let noteoff = message.command === 8; + if (!noteon && !noteoff) { + // Ignore non-note messages (e.g. CC, pitchbend, modwheel, etc.) + return; + } + const [note, velocity] = dataBytes; + noteoff ||= noteon && velocity === 0; // handle devices which may use velocity = 0 to signal noteoff const key = `${input}_${note}`; const cps = getCps() ?? 0.5; const triggerAvailable = !!(getPattern() && getTriggerFunc()); @@ -655,7 +660,7 @@ export async function midikeys(input) { if (!noteoff && triggerAvailable) { // If we have access to a trigger function, we call it to immediately // dispatch to the audio engine, rather than waiting for cyclist to catch these haps - const triggered = triggerKbNow(input, cps, now, latencySeconds * cps); + const triggered = _triggerKeyboard(input, cps, now, latencySeconds * cps); if (triggered) { kHaps[input] = []; } From c2720a573821562b816a11d75311613cf57541f9 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 12 Jan 2026 11:26:18 -0800 Subject: [PATCH 092/124] working --- packages/core/pattern.mjs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 58c83a6f8..4cf30c787 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -26,6 +26,7 @@ import { } from './util.mjs'; import drawLine from './drawLine.mjs'; import { errorLogger, logger } from './logger.mjs'; +import { strudelScope } from './evaluate.mjs'; let stringParser; @@ -1683,7 +1684,9 @@ export function register(name, func, patternify = true, preserveSteps = false, j // toplevel functions get curried as well as patternified // because pfunc uses spread args, we need to state the arity explicitly! - return curry(pfunc, null, arity); + const curried = curry(pfunc, null, arity); + strudelScope[name] = pfunc; + return curried; } // Like register, but defaults to stepJoin From 35bfd8b3ff53111425fd35eb0486979ba5265a3b Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 12 Jan 2026 11:28:54 -0800 Subject: [PATCH 093/124] curried --- packages/core/pattern.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 4cf30c787..25a2bb0b9 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1685,7 +1685,7 @@ export function register(name, func, patternify = true, preserveSteps = false, j // toplevel functions get curried as well as patternified // because pfunc uses spread args, we need to state the arity explicitly! const curried = curry(pfunc, null, arity); - strudelScope[name] = pfunc; + strudelScope[name] = curried; return curried; } From 7c5f53f9ae8b85507300164be00852eccf6d0bf6 Mon Sep 17 00:00:00 2001 From: Aria Date: Mon, 12 Jan 2026 13:51:21 -0600 Subject: [PATCH 094/124] Add ack message for worklet death; remove max pool size --- packages/superdough/nodePools.mjs | 7 ++----- packages/superdough/synth.mjs | 5 ++++- packages/superdough/wavetable.mjs | 5 ++++- packages/superdough/worklets.mjs | 26 ++++++++++++++++++++++++-- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/packages/superdough/nodePools.mjs b/packages/superdough/nodePools.mjs index 205352247..000099dc6 100644 --- a/packages/superdough/nodePools.mjs +++ b/packages/superdough/nodePools.mjs @@ -8,7 +8,6 @@ This program is free software: you can redistribute it and/or modify it under th const nodePools = new Map(); const POOL_KEY = Symbol('nodePoolKey'); const IS_WORKLET_DEAD = Symbol('nodePoolIsWorkletDead'); -const MAX_POOL_SIZE = 64; export const isPoolable = (node) => !!node[POOL_KEY]; @@ -47,10 +46,8 @@ export const releaseNodeToPool = (node) => { const now = node.context?.currentTime ?? 0; getParams(node).forEach((param) => param.cancelScheduledValues(now)); const pool = nodePools.get(key) ?? []; - if (pool.length < MAX_POOL_SIZE) { - pool.push(new WeakRef(node)); - nodePools.set(key, pool); - } + pool.push(new WeakRef(node)); + nodePools.set(key, pool); }; export const markWorkletAsDead = (worklet) => (worklet[IS_WORKLET_DEAD] = true); diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index f4bdd749f..ec1724da0 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -186,7 +186,10 @@ export function registerSynthSounds() { }); o.port.postMessage({ type: 'initialize' }); o.port.onmessage = (e) => { - if (e.data.type === 'died') markWorkletAsDead(o); + if (e.data.type === 'died') { + markWorkletAsDead(o); + o.port.postMessage({ type: 'diedACK' }); + } o.port.onmessage = null; }; const gainAdjustment = 1 / Math.sqrt(voices); diff --git a/packages/superdough/wavetable.mjs b/packages/superdough/wavetable.mjs index adf8b0c44..a228bc1d7 100644 --- a/packages/superdough/wavetable.mjs +++ b/packages/superdough/wavetable.mjs @@ -252,7 +252,10 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) { }); source.port.postMessage({ type: 'initialize', payload }); source.port.onmessage = (e) => { - if (e.data.type === 'died') markWorkletAsDead(source); + if (e.data.type === 'died') { + markWorkletAsDead(source); + source.port.postMessage({ type: 'diedACK' }); + } source.port.onmessage = null; }; if (ac.currentTime > t) { diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 52c13e836..61c620878 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -464,11 +464,19 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor { constructor() { super(); this.isAlive = true; // used internally to prevent multiple death messages + // diedACK is used for the main thread to acknowledge that the worklet has died + // This is so that we don't risk simultaneously killing the worklet (`return false`) + // and pooling the worklet (main thread) before the async `died` message hits + // the main thread + this.diedACK = false; this.port.onmessage = (e) => { const { type, payload } = e.data || {}; if (type === 'initialize') { this.initialize(payload); } + if (type === 'diedACK') { + this.diedACK = true; + } }; this.initialize(); } @@ -529,7 +537,10 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor { this.port.postMessage({ type: 'died' }); this.isAlive = false; } - return false; + if (this.diedACK || currentTime >= params.end[1] + 1) { + return false; + } + return true; } if (currentTime >= params.end[0] || currentTime <= params.begin[0]) { // Inside of grace period or not yet started @@ -1165,11 +1176,19 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor { constructor(options) { super(options); this.isAlive = true; // used internally to prevent multiple death messages + // diedACK is used for the main thread to acknowledge that the worklet has died + // This is so that we don't risk simultaneously killing the worklet (`return false`) + // and pooling the worklet (main thread) before the async `died` message hits + // the main thread + this.diedACK = false; this.port.onmessage = (e) => { const { type, payload } = e.data || {}; if (type === 'initialize') { this.initialize(payload); } + if (type === 'diedACK') { + this.diedACK = true; + } }; this.initialize(); } @@ -1338,7 +1357,10 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor { this.port.postMessage({ type: 'died' }); this.isAlive = false; } - return false; + if (this.diedACK || currentTime >= parameters.end[1] + 1) { + return false; + } + return true; } if (currentTime >= parameters.end[0] || currentTime <= parameters.begin[0]) { // Inside of grace period or not yet started From 1f1f3288a6fe0d9f1803f1ceb00cd12bb746ba05 Mon Sep 17 00:00:00 2001 From: Aria Date: Mon, 12 Jan 2026 17:58:20 -0600 Subject: [PATCH 095/124] Switch to tracking grace period in node pool; add negative begin and ends --- packages/superdough/nodePools.mjs | 32 +++++++++---- packages/superdough/synth.mjs | 9 +--- packages/superdough/wavetable.mjs | 9 +--- packages/superdough/worklets.mjs | 78 +++++++++++-------------------- 4 files changed, 53 insertions(+), 75 deletions(-) diff --git a/packages/superdough/nodePools.mjs b/packages/superdough/nodePools.mjs index 000099dc6..90b4c0ae7 100644 --- a/packages/superdough/nodePools.mjs +++ b/packages/superdough/nodePools.mjs @@ -7,10 +7,13 @@ This program is free software: you can redistribute it and/or modify it under th const nodePools = new Map(); const POOL_KEY = Symbol('nodePoolKey'); -const IS_WORKLET_DEAD = Symbol('nodePoolIsWorkletDead'); export const isPoolable = (node) => !!node[POOL_KEY]; +const getNodeTime = (node) => { + return node.context?.currentTime ?? 0; +}; + const getParams = (node) => { const params = new Set(); node.parameters?.forEach((param) => params.add(param)); @@ -37,32 +40,43 @@ export const releaseNodeToPool = (node) => { // not reusable return; } - if (node[IS_WORKLET_DEAD]) { - // Worklet already terminated, don't pool it - return; - } const key = node[POOL_KEY]; if (key == null) return; - const now = node.context?.currentTime ?? 0; + const now = getNodeTime(node); getParams(node).forEach((param) => param.cancelScheduledValues(now)); const pool = nodePools.get(key) ?? []; pool.push(new WeakRef(node)); nodePools.set(key, pool); }; -export const markWorkletAsDead = (worklet) => (worklet[IS_WORKLET_DEAD] = true); +// Audio worklets are given a grace period to survive (`return true`) after +// being released. This concludes at time `end + 0.5`. We test here whether we are +// within some safe distance of that (`end + 0.45`) and if so, permit the node to be +// released. This helps to prevent race conditions between node termination and node +// re-use +const isNodeAlive = (node) => { + // Skip check if node is not a worklet + if (!(node instanceof AudioWorkletNode)) return true; + const now = getNodeTime(node); + const end = node?.parameters?.get('end').value ?? 0; + return now < end + 0.45; +}; // Attempt to get node from the pool. If this fails, fall back // to building it with the factory export const getNodeFromPool = (key, factory) => { const pool = nodePools.get(key) ?? []; let node; + let found = false; while (pool.length) { const ref = pool.pop(); node = ref?.deref(); - if (node != null && !node[IS_WORKLET_DEAD]) break; + if (node != null && isNodeAlive(node)) { + found = true; + break; + } } - if (node == null || node[IS_WORKLET_DEAD]) { + if (!found) { node = factory(); } node[POOL_KEY] = key; diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index ec1724da0..9561ab363 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -18,7 +18,7 @@ import { } from './helpers.mjs'; import { logger } from './logger.mjs'; import { getNoiseMix, getNoiseOscillator } from './noise.mjs'; -import { getNodeFromPool, markWorkletAsDead, releaseNodeToPool } from './nodePools.mjs'; +import { getNodeFromPool, releaseNodeToPool } from './nodePools.mjs'; const waveforms = ['triangle', 'square', 'sawtooth', 'sine', 'user', 'one']; const waveformAliases = [ @@ -185,13 +185,6 @@ export function registerSynthSounds() { param.setValueAtTime(target, now); }); o.port.postMessage({ type: 'initialize' }); - o.port.onmessage = (e) => { - if (e.data.type === 'died') { - markWorkletAsDead(o); - o.port.postMessage({ type: 'diedACK' }); - } - o.port.onmessage = null; - }; const gainAdjustment = 1 / Math.sqrt(voices); getPitchEnvelope(o.parameters.get('detune'), value, begin, holdend); const vibratoHandle = getVibratoOscillator(o.parameters.get('detune'), value, begin); diff --git a/packages/superdough/wavetable.mjs b/packages/superdough/wavetable.mjs index a228bc1d7..1de42f643 100644 --- a/packages/superdough/wavetable.mjs +++ b/packages/superdough/wavetable.mjs @@ -11,7 +11,7 @@ import { webAudioTimeout, releaseAudioNode, } from './helpers.mjs'; -import { getNodeFromPool, markWorkletAsDead, releaseNodeToPool } from './nodePools.mjs'; +import { getNodeFromPool, releaseNodeToPool } from './nodePools.mjs'; import { logger } from './logger.mjs'; export const Warpmode = Object.freeze({ @@ -251,13 +251,6 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) { param.setValueAtTime(target, now); }); source.port.postMessage({ type: 'initialize', payload }); - source.port.onmessage = (e) => { - if (e.data.type === 'died') { - markWorkletAsDead(source); - source.port.postMessage({ type: 'diedACK' }); - } - source.port.onmessage = null; - }; if (ac.currentTime > t) { logger(`[wavetable] still loading sound "${s}:${n}"`, 'highlight'); return; diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 61c620878..6689278e3 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -463,20 +463,11 @@ registerProcessor('distort-processor', DistortProcessor); class SuperSawOscillatorProcessor extends AudioWorkletProcessor { constructor() { super(); - this.isAlive = true; // used internally to prevent multiple death messages - // diedACK is used for the main thread to acknowledge that the worklet has died - // This is so that we don't risk simultaneously killing the worklet (`return false`) - // and pooling the worklet (main thread) before the async `died` message hits - // the main thread - this.diedACK = false; this.port.onmessage = (e) => { const { type, payload } = e.data || {}; if (type === 'initialize') { this.initialize(payload); } - if (type === 'diedACK') { - this.diedACK = true; - } }; this.initialize(); } @@ -487,16 +478,16 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor { return [ { name: 'begin', - defaultValue: 0, + defaultValue: -1, max: Number.POSITIVE_INFINITY, - min: 0, + min: -1, }, { name: 'end', - defaultValue: 0, + defaultValue: -1, max: Number.POSITIVE_INFINITY, - min: 0, + min: -1, }, { @@ -531,19 +522,17 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor { ]; } process(_input, outputs, params) { - if (currentTime >= params.end[0] + 0.5) { - // Outside of grace period - should terminate - if (this.isAlive) { - this.port.postMessage({ type: 'died' }); - this.isAlive = false; - } - if (this.diedACK || currentTime >= params.end[1] + 1) { - return false; - } - return true; - } - if (currentTime >= params.end[0] || currentTime <= params.begin[0]) { - // Inside of grace period or not yet started + const begin = params.begin[0]; + const end = params.end[0]; + const beginDefined = begin >= 0; + const endDefined = end >= 0; + // We give a 0.5s grace period (for node pooling) before termination + const shouldTerminate = endDefined && currentTime >= end + 0.5; + const ended = endDefined && currentTime >= end; + const notStarted = currentTime <= begin; + if (shouldTerminate) { + return false; + } else if (ended || notStarted || !beginDefined) { return true; } const output = outputs[0]; @@ -1159,8 +1148,8 @@ const tablesCache = {}; class WavetableOscillatorProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [ - { name: 'begin', defaultValue: 0, min: 0, max: Number.POSITIVE_INFINITY }, - { name: 'end', defaultValue: 0, min: 0, max: Number.POSITIVE_INFINITY }, + { name: 'begin', defaultValue: -1, min: -1, max: Number.POSITIVE_INFINITY }, + { name: 'end', defaultValue: -1, min: -1, max: Number.POSITIVE_INFINITY }, { name: 'frequency', defaultValue: 440, min: Number.EPSILON }, { name: 'detune', defaultValue: 0 }, { name: 'freqspread', defaultValue: 0.18, min: 0 }, @@ -1175,20 +1164,11 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor { constructor(options) { super(options); - this.isAlive = true; // used internally to prevent multiple death messages - // diedACK is used for the main thread to acknowledge that the worklet has died - // This is so that we don't risk simultaneously killing the worklet (`return false`) - // and pooling the worklet (main thread) before the async `died` message hits - // the main thread - this.diedACK = false; this.port.onmessage = (e) => { const { type, payload } = e.data || {}; if (type === 'initialize') { this.initialize(payload); } - if (type === 'diedACK') { - this.diedACK = true; - } }; this.initialize(); } @@ -1351,19 +1331,17 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor { } process(_inputs, outputs, parameters) { - if (currentTime >= parameters.end[0] + 0.5) { - // Outside of grace period - should terminate - if (this.isAlive) { - this.port.postMessage({ type: 'died' }); - this.isAlive = false; - } - if (this.diedACK || currentTime >= parameters.end[1] + 1) { - return false; - } - return true; - } - if (currentTime >= parameters.end[0] || currentTime <= parameters.begin[0]) { - // Inside of grace period or not yet started + const begin = parameters.begin[0]; + const end = parameters.end[0]; + const beginDefined = begin >= 0; + const endDefined = end >= 0; + // We give a 0.5s grace period (for node pooling) before termination + const shouldTerminate = endDefined && currentTime >= end + 0.5; + const ended = endDefined && currentTime >= end; + const notStarted = currentTime <= begin; + if (shouldTerminate) { + return false; + } else if (ended || notStarted || !beginDefined) { return true; } const outL = outputs[0][0]; From 8b1fb12388aa523224f8e7a93e5bca0e3368fbd0 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 13 Jan 2026 03:32:19 +0100 Subject: [PATCH 096/124] fix: class -> className --- website/src/repl/components/panel/PatternsTab.jsx | 2 +- website/src/repl/components/panel/Reference.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/src/repl/components/panel/PatternsTab.jsx b/website/src/repl/components/panel/PatternsTab.jsx index 56f0b4cb8..04f6452e5 100644 --- a/website/src/repl/components/panel/PatternsTab.jsx +++ b/website/src/repl/components/panel/PatternsTab.jsx @@ -126,7 +126,7 @@ export function PatternsTab({ context }) { return (
-
+
diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index 505cf50d2..c03ac0cf9 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -55,7 +55,7 @@ export function Reference() { return (
-
+
From e82bb6c4100e58b4a9f99e617745a74d1fb74b1c Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 13 Jan 2026 03:38:09 +0100 Subject: [PATCH 097/124] fix: use pattern.id as title fallback --- website/src/repl/components/panel/PatternsTab.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/repl/components/panel/PatternsTab.jsx b/website/src/repl/components/panel/PatternsTab.jsx index 04f6452e5..75c019438 100644 --- a/website/src/repl/components/panel/PatternsTab.jsx +++ b/website/src/repl/components/panel/PatternsTab.jsx @@ -29,7 +29,7 @@ export function PatternLabel({ pattern } /* : { pattern: Tables<'code'> } */) { if (!isNaN(date)) { title = date.toLocaleDateString(); } else { - title = 'unnamed'; + title = pattern.id || 'unnamed'; } } From e5ab6b3c93b35973e46607c45bb071b323177bbf Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 13 Jan 2026 03:47:33 +0100 Subject: [PATCH 098/124] fix: short circuit --- website/src/repl/components/panel/PatternsTab.jsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/website/src/repl/components/panel/PatternsTab.jsx b/website/src/repl/components/panel/PatternsTab.jsx index 75c019438..c72ebaa30 100644 --- a/website/src/repl/components/panel/PatternsTab.jsx +++ b/website/src/repl/components/panel/PatternsTab.jsx @@ -89,12 +89,11 @@ export function PatternsTab({ context }) { const viewingPatternID = viewingPatternData?.id; const visiblePatterns = useMemo(() => { + if (!search) { + return userPatterns; + } return Object.fromEntries( Object.entries(userPatterns).filter(([_key, pattern]) => { - if (!search) { - return true; - } - const meta = getMetadata(pattern.code); // Search for specific meta keys From 80875cca956024e017281108fcd2fee673e6f1f3 Mon Sep 17 00:00:00 2001 From: Aria Date: Wed, 14 Jan 2026 11:44:48 -0600 Subject: [PATCH 099/124] Expose scheduler state and don't enqueue/trigger midi keys when repl stopped --- packages/core/repl.mjs | 9 ++++++++- packages/core/schedulerState.mjs | 9 +++++++++ packages/midi/midi.mjs | 8 ++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index e39b57de3..a056cac53 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -2,7 +2,13 @@ import { NeoCyclist } from './neocyclist.mjs'; import { Cyclist } from './cyclist.mjs'; import { evaluate as _evaluate } from './evaluate.mjs'; import { errorLogger, logger } from './logger.mjs'; -import { setCpsFunc, setPattern as exposeSchedulerPattern, setTime, setTriggerFunc } from './schedulerState.mjs'; +import { + setCpsFunc, + setIsStarted, + setPattern as exposeSchedulerPattern, + setTime, + setTriggerFunc, +} from './schedulerState.mjs'; import { evalScope } from './evaluate.mjs'; import { register, Pattern, isPattern, silence, stack } from './pattern.mjs'; import { reset_state } from './impure.mjs'; @@ -52,6 +58,7 @@ export function repl({ getTime, onToggle: (started) => { updateState({ started }); + setIsStarted(started); onToggle?.(started); if (!started) { reset_state(); diff --git a/packages/core/schedulerState.mjs b/packages/core/schedulerState.mjs index 97a4f56dc..14b4c9054 100644 --- a/packages/core/schedulerState.mjs +++ b/packages/core/schedulerState.mjs @@ -8,6 +8,7 @@ let time; let cpsFunc; let pattern; let triggerFunc; +let isStarted; export function getTime() { if (!time) { throw new Error('no time set! use setTime to define a time source'); @@ -42,3 +43,11 @@ export function setTriggerFunc(func) { export function getTriggerFunc() { return triggerFunc; } + +export function setIsStarted(val) { + isStarted = !!val; +} + +export function getIsStarted() { + return isStarted; +} diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 50a58b059..d0a1ce18d 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -10,6 +10,7 @@ import { Pattern, TimeSpan, getCps, + getIsStarted, getPattern, getTime, getTriggerFunc, @@ -625,8 +626,11 @@ export async function midikeys(input) { const { dataBytes, message } = e; const noteon = message.command === 9; let noteoff = message.command === 8; - if (!noteon && !noteoff) { - // Ignore non-note messages (e.g. CC, pitchbend, modwheel, etc.) + // Don't enqueue or trigger midi notes if scheduler is not started + const notStarted = !getIsStarted(); + // Ignore non-note messages (e.g. CC, pitchbend, modwheel, etc.) + const notANote = !noteon && !noteoff; + if (notStarted || notANote) { return; } const [note, velocity] = dataBytes; From ffcab7bf6c774b452b99709f035f9f2b7116599c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Bj=C3=B6rk?= Date: Wed, 14 Jan 2026 22:25:27 +0100 Subject: [PATCH 100/124] Added docs for pattern search --- website/src/pages/learn/metadata.mdx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/website/src/pages/learn/metadata.mdx b/website/src/pages/learn/metadata.mdx index c94257925..35a1ad05c 100644 --- a/website/src/pages/learn/metadata.mdx +++ b/website/src/pages/learn/metadata.mdx @@ -53,6 +53,7 @@ Available tags are: - `@url`: web page(s) related to the music (git repository, Soundcloud link, etc.) - `@genre`: music genre(s) (pop, jazz, etc.) - `@album`: music album name +- `@tag`: custom tag Note to tool authors: _Never_ trust that a song has filled those fields with syntactically correct values; make sure your software is robust enough it doesn't break if it encounters bad values @@ -92,3 +93,18 @@ If a tag doesn't accept a list, it can take multi-line values: the sofa in the living room. */ ``` + +# Searching meta-data in the online repl + +Meta-data can be used in the search field of the patterns tab in the online repl. + +For example to search for all patterns by a specific author use the search term +``` +by: Ada L +``` +or search for patterns with a specific genre like +``` +genre: unicorns +``` + +Hint: If no meta-data property is provided in the search all patterns with a `@title`, `@by` or `@tag` matching the search term will be shown. From 71dfbeea8828590a7cf061f5d9aa8648c35b96c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Bj=C3=B6rk?= Date: Wed, 14 Jan 2026 22:35:10 +0100 Subject: [PATCH 101/124] Fix markdown format --- website/src/pages/learn/metadata.mdx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/website/src/pages/learn/metadata.mdx b/website/src/pages/learn/metadata.mdx index 35a1ad05c..07a1357c1 100644 --- a/website/src/pages/learn/metadata.mdx +++ b/website/src/pages/learn/metadata.mdx @@ -99,10 +99,13 @@ If a tag doesn't accept a list, it can take multi-line values: Meta-data can be used in the search field of the patterns tab in the online repl. For example to search for all patterns by a specific author use the search term + ``` by: Ada L -``` +``` + or search for patterns with a specific genre like + ``` genre: unicorns ``` From 85e79d993263f84b1aa63f4efa9eb71b9cb93d42 Mon Sep 17 00:00:00 2001 From: Aria Date: Wed, 14 Jan 2026 16:53:11 -0600 Subject: [PATCH 102/124] Set pooled values immediately instead of scheduling --- packages/superdough/helpers.mjs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index 0ca085e3e..bb402ea78 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -154,9 +154,8 @@ export function getCompressor(ac, threshold, ratio, knee, attack, release) { attack: attack ?? 0.005, release: release ?? 0.05, }; - const now = ac.currentTime; Object.entries(options).forEach(([key, value]) => { - node[key].setValueAtTime(value, now); + node[key].value = value;; }); return node; } @@ -242,9 +241,8 @@ export function createFilter(context, start, end, params, cps, cycle) { const factory = () => context.createBiquadFilter(); filter = getNodeFromPool('filter', factory); filter.type = type; - const now = context.currentTime; Object.entries({ Q: q, frequency }).forEach(([key, value]) => { - filter[key].setValueAtTime(value, now); + filter[key].value = value; }); frequencyParam = filter.frequency; } From 21e6d81d349f475527d26509749385d70c5d0374 Mon Sep 17 00:00:00 2001 From: Aria Date: Wed, 14 Jan 2026 16:58:45 -0600 Subject: [PATCH 103/124] Also set values immediately for supersaw and wavetable --- packages/superdough/helpers.mjs | 2 +- packages/superdough/synth.mjs | 3 +-- packages/superdough/wavetable.mjs | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index bb402ea78..584b19673 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -155,7 +155,7 @@ export function getCompressor(ac, threshold, ratio, knee, attack, release) { release: release ?? 0.05, }; Object.entries(options).forEach(([key, value]) => { - node[key].value = value;; + node[key].value = value; }); return node; } diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 9561ab363..38ad643de 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -178,11 +178,10 @@ export function registerSynthSounds() { }; const factory = () => new AudioWorkletNode(ac, 'supersaw-oscillator', { outputChannelCount: [2] }); const o = getNodeFromPool('supersaw', factory); - const now = ac.currentTime; Object.entries(params).forEach(([key, value]) => { const param = o.parameters.get(key); const target = value !== undefined ? value : param.defaultValue; - param.setValueAtTime(target, now); + param.value = target; }); o.port.postMessage({ type: 'initialize' }); const gainAdjustment = 1 / Math.sqrt(voices); diff --git a/packages/superdough/wavetable.mjs b/packages/superdough/wavetable.mjs index 1de42f643..6ef1f0ebb 100644 --- a/packages/superdough/wavetable.mjs +++ b/packages/superdough/wavetable.mjs @@ -244,11 +244,10 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) { }; const factory = () => new AudioWorkletNode(ac, 'wavetable-oscillator-processor', { outputChannelCount: [2] }); const source = getNodeFromPool('wavetable', factory); - const now = ac.currentTime; Object.entries(params).forEach(([key, value]) => { const param = source.parameters.get(key); const target = value !== undefined ? value : param.defaultValue; - param.setValueAtTime(target, now); + param.value = target; }); source.port.postMessage({ type: 'initialize', payload }); if (ac.currentTime > t) { From 60e5682829848dcc65a3073c9e0fb0eb46a08183 Mon Sep 17 00:00:00 2001 From: jeromew Date: Thu, 15 Jan 2026 15:42:34 +0000 Subject: [PATCH 104/124] Update kabelsalat dependency --- packages/core/package.json | 2 +- packages/superdough/package.json | 2 +- pnpm-lock.yaml | 36 ++++++++++++++++---------------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 64253b081..abb54e9ce 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -31,7 +31,7 @@ }, "homepage": "https://strudel.cc", "dependencies": { - "@kabelsalat/web": "^0.4.0", + "@kabelsalat/web": "^0.4.1", "fraction.js": "^5.2.1" }, "gitHead": "0e26d4e741500f5bae35b023608f062a794905c2", diff --git a/packages/superdough/package.json b/packages/superdough/package.json index 2125b8d98..78db8338e 100644 --- a/packages/superdough/package.json +++ b/packages/superdough/package.json @@ -36,7 +36,7 @@ "vite-plugin-bundle-audioworklet": "workspace:*" }, "dependencies": { - "@kabelsalat/lib": "^0.4.0", + "@kabelsalat/lib": "^0.4.1", "nanostores": "^0.11.3" }, "engines": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2684ce3bb..53437f8f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,7 +41,7 @@ importers: version: 2.2.7 '@vitest/coverage-v8': specifier: 3.0.4 - version: 3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.10.10)(@vitest/ui@3.0.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0)) + version: 3.0.4(vitest@3.0.4) '@vitest/ui': specifier: ^3.0.4 version: 3.0.4(vitest@3.0.4) @@ -235,8 +235,8 @@ importers: packages/core: dependencies: '@kabelsalat/web': - specifier: ^0.4.0 - version: 0.4.0 + specifier: ^0.4.1 + version: 0.4.1 fraction.js: specifier: ^5.2.1 version: 5.2.1 @@ -522,8 +522,8 @@ importers: packages/superdough: dependencies: '@kabelsalat/lib': - specifier: ^0.4.0 - version: 0.4.0 + specifier: ^0.4.1 + version: 0.4.1 nanostores: specifier: ^0.11.3 version: 0.11.3 @@ -1975,14 +1975,14 @@ packages: resolution: {integrity: sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw==} engines: {node: '>=v12.0.0'} - '@kabelsalat/core@0.3.1': - resolution: {integrity: sha512-y2wHZyKnwbhJdGuwXvkaIb5vqbNTW6rMDnBslHGxQHTFX5xYyxJLOcuF2/EOB698t09kOVhmmpLXKbyokf3pOA==} + '@kabelsalat/core@0.4.0': + resolution: {integrity: sha512-5zV8nh8HffW8aexObXs5pE0xgL0jb1cHc3o8UN6AvK0mBt87fjZvW65E4rMslHyq+OuCscoBJW7B3MbYfWrwMQ==} - '@kabelsalat/lib@0.4.0': - resolution: {integrity: sha512-UoOUhYOFShDjYCTne1edevCHHxSC07FsxG9tQ5GGOOzNoKMUpGVY8Ecq9X1GVyseQFvQBgz3aqAC6K8FPZp4ZQ==} + '@kabelsalat/lib@0.4.1': + resolution: {integrity: sha512-gBCjrZKD9huTKNJuBC6BXM4PMQSg8otL8A/vp8j98P9v6yWTX1TuyaqdLg/1PrIAMV4hlWoav9ZD3YMFlrvouw==} - '@kabelsalat/web@0.4.0': - resolution: {integrity: sha512-uvcOJjpKf8PG6KrgY0QEpoV4IAbIPDXh73bPtFSdDcXApbkSyEEyeNE8IoqIlqfcjIX926IOCAx9nM6FsFp6DA==} + '@kabelsalat/web@0.4.1': + resolution: {integrity: sha512-ASkFhePJLx3GjadYOueI7sHKXBbRPk/a1pfslFeQNI9gU7yZq/KrnkmOmLrgrtixAsy7gyi1vWqkmRRvH3Ki6w==} '@lerna/create@8.1.9': resolution: {integrity: sha512-DPnl5lPX4v49eVxEbJnAizrpMdMTBz1qykZrAbBul9rfgk531v8oAt+Pm6O/rpAleRombNM7FJb5rYGzBJatOQ==} @@ -9276,16 +9276,16 @@ snapshots: dependencies: lodash: 4.17.21 - '@kabelsalat/core@0.3.1': {} + '@kabelsalat/core@0.4.0': {} - '@kabelsalat/lib@0.4.0': + '@kabelsalat/lib@0.4.1': dependencies: - '@kabelsalat/core': 0.3.1 + '@kabelsalat/core': 0.4.0 - '@kabelsalat/web@0.4.0': + '@kabelsalat/web@0.4.1': dependencies: - '@kabelsalat/core': 0.3.1 - '@kabelsalat/lib': 0.4.0 + '@kabelsalat/core': 0.4.0 + '@kabelsalat/lib': 0.4.1 '@lerna/create@8.1.9(encoding@0.1.13)(typescript@5.7.3)': dependencies: @@ -10580,7 +10580,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.10.10)(@vitest/ui@3.0.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0))': + '@vitest/coverage-v8@3.0.4(vitest@3.0.4)': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 From ae9638c3534635e48d9671ef018a297f8ffcd11c Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 16 Jan 2026 09:58:14 +0100 Subject: [PATCH 105/124] update changelog + fix script to not miss entries --- CHANGELOG.md | 961 ++++++++++++++++++++++++++++++++++++++++++++++++++- warm.js | 40 ++- 2 files changed, 977 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19a502f33..3daf4e6cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,20 @@ NOTE: you can generate this with `node warm.js`. it might still not be perfectly ## january 2026 + +- 2026-01-15T16:59:14+01:00 mondo fix: add registered functions to scope automatically by daslyfe in: [#1896](https://codeberg.org/uzu/strudel/pulls/1896) +- 2026-01-15T16:58:14+01:00 Added docs for pattern search by JohnBjrk in: [#1905](https://codeberg.org/uzu/strudel/pulls/1905) +- 2026-01-15T16:57:19+01:00 Update kabelsalat dependency by jeromew in: [#1911](https://codeberg.org/uzu/strudel/pulls/1911) +- 2026-01-15T00:24:39+01:00 Bug Fix: Set pooled values immediately instead of scheduling by glossing in: [#1907](https://codeberg.org/uzu/strudel/pulls/1907) +- 2026-01-14T20:29:16+01:00 Feat: Kabelsalat integration by glossing in: [#1876](https://codeberg.org/uzu/strudel/pulls/1876) +- 2026-01-14T19:07:49+01:00 Feat: MIDI Keyboard 🎹🐈 by glossing in: [#1828](https://codeberg.org/uzu/strudel/pulls/1828) +- 2026-01-14T18:14:52+01:00 Bug Fix: Fix race condition between worklet termination and port messages by glossing in: [#1897](https://codeberg.org/uzu/strudel/pulls/1897) +- 2026-01-13T04:06:52+01:00 Add search/filter in patterns tab by JohnBjrk in: [#1842](https://codeberg.org/uzu/strudel/pulls/1842) +- 2026-01-13T00:18:10+01:00 Add shortcut for navigating through labels! by daslyfe in: [#1807](https://codeberg.org/uzu/strudel/pulls/1807) +- 2026-01-12T00:17:37+01:00 add warm.strudel.cc to faq by yaxu in: [#1891](https://codeberg.org/uzu/strudel/pulls/1891) - 2026-01-11T19:00:25+01:00 Fix sounds example to work in the REPL by JesCoding in: [#1851](https://codeberg.org/uzu/strudel/pulls/1851) - 2026-01-11T13:52:02+01:00 Improve hint text when sound search has no results by floy in: [#1883](https://codeberg.org/uzu/strudel/pulls/1883) +- 2026-01-11T13:19:46+01:00 New page FAQ in "More" by scrappy_fiddler in: [#1753](https://codeberg.org/uzu/strudel/pulls/1753) - 2026-01-11T12:45:07+01:00 fix: remove faulty default readme by froos in: [#1889](https://codeberg.org/uzu/strudel/pulls/1889) - 2026-01-11T12:15:34+01:00 fix/self-hosted-config by alienmind in: [#1880](https://codeberg.org/uzu/strudel/pulls/1880) - 2026-01-11T12:07:48+01:00 Feat: Support External AudioContext Injection by 1d10t in: [#1833](https://codeberg.org/uzu/strudel/pulls/1833) @@ -14,8 +26,8 @@ NOTE: you can generate this with `node warm.js`. it might still not be perfectly - 2026-01-11T11:05:44+01:00 fix: repl package init audio properly by froos in: [#1836](https://codeberg.org/uzu/strudel/pulls/1836) - 2026-01-11T06:51:24+01:00 Fix: show reload dialog when uploading prebake script by daslyfe in: [#1886](https://codeberg.org/uzu/strudel/pulls/1886) - 2026-01-11T06:01:42+01:00 fixes Serial onTrigger() params #1633 by gueejla in: [#1885](https://codeberg.org/uzu/strudel/pulls/1885) -- 2026-01-10T20:52:40+01:00 Allow top level distortions for the purpose of FX by glossing in: [#1884](https://codeberg.org/uzu/strudel/pulls/1884) - 2026-01-10T23:12:48+01:00 Perf: Targeted node pools by glossing in: [#1810](https://codeberg.org/uzu/strudel/pulls/1810) +- 2026-01-10T20:52:40+01:00 Allow top level distortions for the purpose of FX by glossing in: [#1884](https://codeberg.org/uzu/strudel/pulls/1884) - 2026-01-09T03:43:37+01:00 Bake in scaling by `freq` for FM with a gain node by glossing in: [#1878](https://codeberg.org/uzu/strudel/pulls/1878) - 2026-01-09T02:53:38+01:00 Bug fix: Properly handle subcontrols by glossing in: [#1877](https://codeberg.org/uzu/strudel/pulls/1877) - 2026-01-07T20:06:18+01:00 Feat: Add ability to turn mini parsing off with mini-off decorator by glossing in: [#1786](https://codeberg.org/uzu/strudel/pulls/1786) @@ -32,23 +44,32 @@ NOTE: you can generate this with `node warm.js`. it might still not be perfectly ## december 2025 - 2025-12-29T21:59:11+01:00 Bugfix: Fix modulator clamping when min/max not specified by glossing in: [#1859](https://codeberg.org/uzu/strudel/pulls/1859) +- 2025-12-29T20:54:11+01:00 Feature: LFOs and Envelopes by glossing in: [#1507](https://codeberg.org/uzu/strudel/pulls/1507) - 2025-12-29T16:07:18+01:00 Fix AudioContext change detection. Use AudioNode.context by jeromew in: [#1858](https://codeberg.org/uzu/strudel/pulls/1858) -- 2025-12-28T14:20:03+01:00 dough repl fixes by froos in: [#1855](https://codeberg.org/uzu/strudel/pulls/1855) -- 2025-12-19T08:11:50+01:00 fix: visual block selection mode for vim bindings by Dsm0 in: [#1839](https://codeberg.org/uzu/strudel/pulls/1839) - 2025-12-28T22:58:38+01:00 Say that @license should use SPDX identifier by Wuzzy in: [#1817](https://codeberg.org/uzu/strudel/pulls/1817) - 2025-12-28T22:56:07+01:00 Expose Vim object in order to create custom keybindings by JohnBjrk in: [#1816](https://codeberg.org/uzu/strudel/pulls/1816) +- 2025-12-28T14:20:03+01:00 dough repl fixes by froos in: [#1855](https://codeberg.org/uzu/strudel/pulls/1855) +- 2025-12-28T13:40:29+01:00 add basic dough repl by froos in: [#1749](https://codeberg.org/uzu/strudel/pulls/1749) - 2025-12-20T22:27:52+01:00 Document "-" in mini-notation by Wuzzy in: [#1818](https://codeberg.org/uzu/strudel/pulls/1818) - 2025-12-20T22:20:27+01:00 simplify envValAtTime and remove asymmetric behavior (fix #1653) by pulu in: [#1815](https://codeberg.org/uzu/strudel/pulls/1815) +- 2025-12-19T08:11:50+01:00 fix: visual block selection mode for vim bindings by Dsm0 in: [#1839](https://codeberg.org/uzu/strudel/pulls/1839) +- 2025-12-19T01:03:53+01:00 [perf] Add audiograph `await debugAudiograph()` feature by jeromew in: [#1763](https://codeberg.org/uzu/strudel/pulls/1763) +- 2025-12-19T00:45:41+01:00 Feature: non-realtime exporting by Ghost in: [#1674](https://codeberg.org/uzu/strudel/pulls/1674) +- 2025-12-14T19:33:37+01:00 Fix: wrong warning in build environments by jeromew in: [#1835](https://codeberg.org/uzu/strudel/pulls/1835) - 2025-12-14T15:09:12+01:00 delta -> per / perx / cyclesPer refinements by yaxu in: [#1832](https://codeberg.org/uzu/strudel/pulls/1832) -- 2025-12-14T19:33:37+01:00 Fix: wrong warning in build environments by jeromew in: [#1835](https://codeberg.org/uzu/strudel/pulls/1835) +- 2025-12-14T01:07:36+01:00 Improved randomness by glossing in: [#1505](https://codeberg.org/uzu/strudel/pulls/1505) - 2025-12-12T10:28:27+01:00 Add delta signal for representing the duration of events in patterns that are combined with it by yaxu in: [#1831](https://codeberg.org/uzu/strudel/pulls/1831) +- 2025-12-11T18:00:49+01:00 Feature: stateful timeline function for jumping between timelines by yaxu in: [#1669](https://codeberg.org/uzu/strudel/pulls/1669) - 2025-12-11T16:37:58+01:00 Updates relating to LLM, github, etc by yaxu in: [#1830](https://codeberg.org/uzu/strudel/pulls/1830) - 2025-12-10T15:32:44+01:00 fix .as so it doesn't set undefined values by yaxu in: [#1827](https://codeberg.org/uzu/strudel/pulls/1827) - 2025-12-08T19:27:31+01:00 [perf] propagate `onceEnded` and `releaseAudioNode` by jeromew in: [#1809](https://codeberg.org/uzu/strudel/pulls/1809) +- 2025-12-07T21:51:40+01:00 Feat: Add channel support to midi in by glossing in: [#1775](https://codeberg.org/uzu/strudel/pulls/1775) +- 2025-12-07T21:23:43+01:00 Feat: Transient shaper by glossing in: [#1777](https://codeberg.org/uzu/strudel/pulls/1777) - 2025-12-07T19:15:43+01:00 Add vel as a synonym for velocity & update a few docstrings by glossing in: [#1781](https://codeberg.org/uzu/strudel/pulls/1781) - 2025-12-07T18:53:04+01:00 [perf] fix phaser leak of unused biquads by jeromew in: [#1800](https://codeberg.org/uzu/strudel/pulls/1800) - 2025-12-07T18:42:50+01:00 Bug fix: Remove failing tests due to shabda removal by glossing in: [#1820](https://codeberg.org/uzu/strudel/pulls/1820) -- 2025-12-03T17:35:05+01:00 [perf] release unused AudioBufferSourceNode + releaseAudioNode by jeromew in: [#1805](https://codeberg.org/uzu/strudel/pulls/1805) +- 2025-12-03T18:27:28+01:00 Feature: Eight FMs by glossing in: [#1628](https://codeberg.org/uzu/strudel/pulls/1628) +- 2025-12-03T17:35:05+01:00 [perf] release unused AudioBufferSourceNode + releaseAudioNode by jeromew in: [#1805](https://codeberg.org/uzu/strudel/pulls/1805) - **2025-12-01 strudel.cc deployed** ## november 2025 @@ -57,6 +78,7 @@ NOTE: you can generate this with `node warm.js`. it might still not be perfectly - 2025-11-28T23:26:20+01:00 add revv() for reversing whole patterns by yaxu in: [#1791](https://codeberg.org/uzu/strudel/pulls/1791) - 2025-11-28T20:19:16+01:00 [perf] Disconnect lfos for phaser and filters by glossing in: [#1787](https://codeberg.org/uzu/strudel/pulls/1787) - 2025-11-27T23:08:52+01:00 fix: return silence when no pattern is returned by froos in: [#1795](https://codeberg.org/uzu/strudel/pulls/1795) +- 2025-11-27T22:47:03+01:00 fix(tool/dbpatch): add missing package name by peterpf in: [#1765](https://codeberg.org/uzu/strudel/pulls/1765) - 2025-11-27T22:38:05+01:00 add CHANGELOG.md + basic script to generate by froos in: [#1794](https://codeberg.org/uzu/strudel/pulls/1794) - 2025-11-27T22:03:53+01:00 [perf] in `noise`, let noiseMix do the disconnect when it exists by jeromew in: [#1783](https://codeberg.org/uzu/strudel/pulls/1783) - 2025-11-27T22:01:01+01:00 Bug Fix: Retries for sounds tab by glossing in: [#1754](https://codeberg.org/uzu/strudel/pulls/1754) @@ -73,6 +95,7 @@ NOTE: you can generate this with `node warm.js`. it might still not be perfectly - 2025-11-23T09:47:24+01:00 Fix interoperability issue between `all` and `await initHydra()` by jeromew in: [#1663](https://codeberg.org/uzu/strudel/pulls/1663) - 2025-11-23T04:23:06+01:00 Bug fix: Swap l/r gains with temp variable by glossing in: [#1768](https://codeberg.org/uzu/strudel/pulls/1768) - 2025-11-23T00:52:28+01:00 FIX: prevent LFO filter modulation from popping from negative values by daslyfe in: [#1767](https://codeberg.org/uzu/strudel/pulls/1767) +- 2025-11-23T00:03:32+01:00 prefix "S" for solo by froos in: [#1481](https://codeberg.org/uzu/strudel/pulls/1481) - 2025-11-21T01:56:11+01:00 Bug Fix: Use frac due to negative frequencies from FM by glossing in: [#1759](https://codeberg.org/uzu/strudel/pulls/1759) - 2025-11-20T19:48:16+01:00 [perf] fix `connect-leak` added by #1742 when noise() is not used by jeromew in: [#1757](https://codeberg.org/uzu/strudel/pulls/1757) - 2025-11-20T14:51:00+01:00 [perf] fix `connect-leak` in `delay` effect by jeromew in: [#1755](https://codeberg.org/uzu/strudel/pulls/1755) @@ -80,6 +103,7 @@ NOTE: you can generate this with `node warm.js`. it might still not be perfectly - 2025-11-18T23:52:45+01:00 wchooseCycles has now notes in an example by scrappy_fiddler in: [#1748](https://codeberg.org/uzu/strudel/pulls/1748) - 2025-11-17T05:31:54+01:00 Feature: Partials by glossing in: [#1659](https://codeberg.org/uzu/strudel/pulls/1659) - 2025-11-16T22:06:14+01:00 README: update superdough documentation by TristanMlct in: [#1741](https://codeberg.org/uzu/strudel/pulls/1741) +- 2025-11-16T22:00:43+01:00 Feature: Envelope worklet by glossing in: [#1524](https://codeberg.org/uzu/strudel/pulls/1524) - 2025-11-16T21:08:54+01:00 Worklet optimizations by glossing in: [#1730](https://codeberg.org/uzu/strudel/pulls/1730) - 2025-11-15T14:59:30+01:00 [perf] disconnect Offline AudioNode connections in generateReverb by jeromew in: [#1740](https://codeberg.org/uzu/strudel/pulls/1740) - 2025-11-15T14:57:47+01:00 [perf] disconnect `send` effect AudioNode when `room` is used by jeromew in: [#1736](https://codeberg.org/uzu/strudel/pulls/1736) @@ -88,15 +112,15 @@ NOTE: you can generate this with `node warm.js`. it might still not be perfectly - 2025-11-12T21:06:13+01:00 Fix web README sample code by Kissaki in: [#1725](https://codeberg.org/uzu/strudel/pulls/1725) - 2025-11-12T20:59:39+01:00 Fix typo: 'studel' -> 'strudel' by drhayes in: [#1726](https://codeberg.org/uzu/strudel/pulls/1726) - 2025-11-12T15:23:41+01:00 fix for node 24 support in tests - #1718 by ausav in: [#1719](https://codeberg.org/uzu/strudel/pulls/1719) -- 2025-11-11T09:00:49+01:00 soundAlias example fix by PepsiiMan in: [#1720](https://codeberg.org/uzu/strudel/pulls/1720) +- 2025-11-11T09:00:49+01:00 soundAlias example fix by PepsiiMan in: [#1720](https://codeberg.org/uzu/strudel/pulls/1720) - 2025-11-10T08:18:36+01:00 fix: Make Supradough package build by munshkr in: [#1711](https://codeberg.org/uzu/strudel/pulls/1711) - 2025-11-10T08:18:00+01:00 added docs for spaces in scale names by ondras in: [#1715](https://codeberg.org/uzu/strudel/pulls/1715) -- 2025-11-10T02:44:01+01:00 Feature: LFOs for Filters by glossing in: [#1636](https://codeberg.org/uzu/strudel/pulls/1636) +- 2025-11-10T02:44:01+01:00 Feature: LFOs for Filters by glossing in: [#1636](https://codeberg.org/uzu/strudel/pulls/1636) - 2025-11-05T17:07:00+01:00 improvement: sync midi messages to audio context clock by daslyfe in: [#1708](https://codeberg.org/uzu/strudel/pulls/1708) - 2025-11-04T19:36:14+01:00 Add stick button mappings to gamepad implementation and improve docs by kaosuryoko in: [#1696](https://codeberg.org/uzu/strudel/pulls/1696) - 2025-11-04T19:29:35+01:00 Refactor sound stopping and triggering logic in SoundsTab component by IJOL in: [#1688](https://codeberg.org/uzu/strudel/pulls/1688) - 2025-11-04T19:13:21+01:00 Add setting to toggle pattern auto-start on pattern change by moumar in: [#1690](https://codeberg.org/uzu/strudel/pulls/1690) -- 2025-11-04T18:58:50+01:00 Voicings JSDoc by sharkeys_lunchbox in: [#1686](https://codeberg.org/uzu/strudel/pulls/1686) +- 2025-11-04T18:58:50+01:00 Voicings JSDoc by sharkeys_lunchbox in: [#1686](https://codeberg.org/uzu/strudel/pulls/1686) - 2025-11-01T07:50:40+01:00 FIX: Loading local samples uses too much memory by daslyfe in: [#1706](https://codeberg.org/uzu/strudel/pulls/1706) @@ -104,7 +128,7 @@ NOTE: you can generate this with `node warm.js`. it might still not be perfectly - 2025-10-28T22:58:24+01:00 Bug Fix: Handle scale-for-notes when n also supplied by glossing in: [#1625](https://codeberg.org/uzu/strudel/pulls/1625) - 2025-10-28T22:51:16+01:00 Repurpose vim shortcuts for usability by dtricks in: [#1624](https://codeberg.org/uzu/strudel/pulls/1624) -- 2025-10-28T22:35:21+01:00 Add support for euclidian in mondo with `bd&3:8` by TristanCacqueray in: [#1630](https://codeberg.org/uzu/strudel/pulls/1630) +- 2025-10-28T22:35:21+01:00 Add support for euclidian in mondo with `bd&3:8` by TristanCacqueray in: [#1630](https://codeberg.org/uzu/strudel/pulls/1630) - 2025-10-27T10:42:07+01:00 Use bunny cdn for all samples and json files by yaxu in: [#1701](https://codeberg.org/uzu/strudel/pulls/1701) - **2025-10-27 @strudel/core@1.2.5** - 2025-10-26T22:52:54+01:00 degithub - switch some samples to bunnycdn by yaxu in: [#1697](https://codeberg.org/uzu/strudel/pulls/1697) @@ -114,30 +138,943 @@ NOTE: you can generate this with `node warm.js`. it might still not be perfectly - 2025-10-22T22:15:19+02:00 Fix sampler.mjs githubPath by jeromew in: [#1684](https://codeberg.org/uzu/strudel/pulls/1684) - 2025-10-22T16:20:50+02:00 Fix onPaint for widgets by yaxu in: [#1658](https://codeberg.org/uzu/strudel/pulls/1658) - 2025-10-22T15:19:12+02:00 Fix a bug introduced by #4e17cfbdd6 by milliganf in: [#1679](https://codeberg.org/uzu/strudel/pulls/1679) +- 2025-10-20T22:37:15+02:00 feature/autocomplete-sound-names by drdozer in: [#1564](https://codeberg.org/uzu/strudel/pulls/1564) +- 2025-10-20T21:58:12+02:00 add replicate + use it for ! in mondo by JoStro in: [#1436](https://codeberg.org/uzu/strudel/pulls/1436) - 2025-10-18T04:29:20+02:00 Bug Fix: Wavetable: phase wrapping at 1 and detune by glossing in: [#1620](https://codeberg.org/uzu/strudel/pulls/1620) - 2025-10-16T20:26:28+02:00 github samples: default to "samples" if repository is not specified by prezmop in: [#1644](https://codeberg.org/uzu/strudel/pulls/1644) - 2025-10-16T17:33:39+02:00 Docs: add example of custom chained function by dariusk in: [#1642](https://codeberg.org/uzu/strudel/pulls/1642) - 2025-10-14T12:39:48+02:00 textbox by yaxu in: [#1650](https://codeberg.org/uzu/strudel/pulls/1650) +- 2025-10-13T07:26:51+02:00 Feature: Distortion Modes by glossing in: [#1561](https://codeberg.org/uzu/strudel/pulls/1561) - 2025-10-13T07:05:57+02:00 Feature: Wavetable FM by glossing in: [#1623](https://codeberg.org/uzu/strudel/pulls/1623) -- 2025-10-12T20:43:42+02:00 fix: repair REPL sample sources and URL concat (#1640) by erikfox in: [#1646](https://codeberg.org/uzu/strudel/pulls/1646) +- 2025-10-12T20:43:42+02:00 fix: repair REPL sample sources and URL concat (#1640) by erikfox in: [#1646](https://codeberg.org/uzu/strudel/pulls/1646) - 2025-10-10T11:25:02+02:00 Add control-metadata by yaxu in: [#1634](https://codeberg.org/uzu/strudel/pulls/1634) - 2025-10-07T22:59:39+02:00 Fix MQTT: change the trigger handler to match new hap by jurrchen in: [#1629](https://codeberg.org/uzu/strudel/pulls/1629) - 2025-10-05T21:42:19+02:00 Fix case sensitivity for synonym search by vvolhejn in: [#1618](https://codeberg.org/uzu/strudel/pulls/1618) - 2025-10-01T08:56:35+02:00 Optimize wavetable synth by glossing in: [#1613](https://codeberg.org/uzu/strudel/pulls/1613) - 2025-10-01T02:02:33+02:00 fix: pattern switching by daslyfe in: [#1616](https://codeberg.org/uzu/strudel/pulls/1616) -## September 2025 +## september 2025 - 2025-09-29T02:10:54+02:00 fix wavetable JSON by daslyfe in: [#1611](https://codeberg.org/uzu/strudel/pulls/1611) - 2025-09-28T21:28:16+02:00 rename wavetable controls to match existing naming conventions by daslyfe in: [#1610](https://codeberg.org/uzu/strudel/pulls/1610) - 2025-09-27T23:28:13+02:00 Feature: Wavetable synth improvements by glossing in: [#1607](https://codeberg.org/uzu/strudel/pulls/1607) - 2025-09-27T21:43:10+02:00 wavetable improvements by daslyfe in: [#1608](https://codeberg.org/uzu/strudel/pulls/1608) - 2025-09-26T08:48:46+02:00 create orbit based DJ filter by daslyfe in: [#1603](https://codeberg.org/uzu/strudel/pulls/1603) +- 2025-09-26T08:15:49+02:00 Feature: Wavetable synth by glossing in: [#1525](https://codeberg.org/uzu/strudel/pulls/1525) - 2025-09-23T13:28:20+02:00 fix: time signal by daslyfe in: [#1583](https://codeberg.org/uzu/strudel/pulls/1583) - 2025-09-21T16:16:06+02:00 mondo: fix all + setcpm + setcps by froos in: [#1600](https://codeberg.org/uzu/strudel/pulls/1600) - 2025-09-19T21:59:43+02:00 fix: osc error message by froos in: [#1597](https://codeberg.org/uzu/strudel/pulls/1597) - 2025-09-18T21:09:56+02:00 osc: --debug flag to see incoming messages by froos in: [#1595](https://codeberg.org/uzu/strudel/pulls/1595) - 2025-09-17T14:12:10+02:00 simplify osc usage by froos in: [#1588](https://codeberg.org/uzu/strudel/pulls/1588) - 2025-09-16T16:59:58+02:00 dev: logger errors to console in dev mode by daslyfe in: [#1585](https://codeberg.org/uzu/strudel/pulls/1585) +- 2025-09-16T03:19:37+02:00 Bug Fix / Feature: Updates to `duck` to avoid clicks and allow configuration over release/attack by glossing in: [#1514](https://codeberg.org/uzu/strudel/pulls/1514) +- 2025-09-15T23:36:31+02:00 Feat: Enhance `scale` function to quantize notes to a named scale by glossing in: [#1492](https://codeberg.org/uzu/strudel/pulls/1492) - 2025-09-15T22:52:14+02:00 add tic80 font by froos in: [#1579](https://codeberg.org/uzu/strudel/pulls/1579) -- 2025-09-15T00:00:19+02:00 supradough: fix delay + add some jsdoc types by froos in: [#1578](https://codeberg.org/uzu/strudel/pulls/1578) \ No newline at end of file +- 2025-09-15T00:00:19+02:00 supradough: fix delay + add some jsdoc types by froos in: [#1578](https://codeberg.org/uzu/strudel/pulls/1578) +- 2025-09-14T12:42:58+02:00 added plyWith/plyWithClassic functions by Dsm0 in: [#1571](https://codeberg.org/uzu/strudel/pulls/1571) +- 2025-09-14T10:39:12+02:00 feat: sound alias by Options in: [#1494](https://codeberg.org/uzu/strudel/pulls/1494) +- 2025-09-14T01:20:15+02:00 Bug Fix: Allow penv values to be falsy by glossing in: [#1559](https://codeberg.org/uzu/strudel/pulls/1559) +- 2025-09-14T01:10:48+02:00 Update website/src/pages/workshop/first-sounds.mdx by fesmith in: [#1566](https://codeberg.org/uzu/strudel/pulls/1566) +- 2025-09-14T01:07:47+02:00 fix: autocomplete-styles + html rendering by froos in: [#1570](https://codeberg.org/uzu/strudel/pulls/1570) +- 2025-09-14T00:49:17+02:00 Feature: Synonyms in autocomplete and reference by glossing in: [#1535](https://codeberg.org/uzu/strudel/pulls/1535) +- 2025-09-13T20:01:48+02:00 Docs: add xen package examples by dudymas in: [#1446](https://codeberg.org/uzu/strudel/pulls/1446) +- 2025-09-13T19:51:10+02:00 Update website/src/pages/learn/code.mdx by anecondev in: [#1391](https://codeberg.org/uzu/strudel/pulls/1391) +- 2025-09-11T22:52:30+02:00 supradough poc by froos in: [#1362](https://codeberg.org/uzu/strudel/pulls/1362) +- 2025-09-11T17:29:35+02:00 fix: exclude mondough dependencies by froos in: [#1563](https://codeberg.org/uzu/strudel/pulls/1563) +- 2025-09-11T00:27:21+02:00 add basicSetup for keybindings by Dsm0 in: [#1462](https://codeberg.org/uzu/strudel/pulls/1462) +- 2025-09-10T17:25:26+02:00 feat: add delete user samples button for convenience by daslyfe in: [#1556](https://codeberg.org/uzu/strudel/pulls/1556) +- 2025-09-08T08:54:01+02:00 Fix formatting of REPL footnote by ddbeck in: [#1547](https://codeberg.org/uzu/strudel/pulls/1547) +- 2025-09-08T05:38:08+02:00 fix: OSC by daslyfe in: [#1557](https://codeberg.org/uzu/strudel/pulls/1557) +- 2025-09-07T22:47:13+02:00 Add examples for ? and | operators to documentation by james-collapse in: [#1532](https://codeberg.org/uzu/strudel/pulls/1532) +- 2025-09-07T21:48:19+02:00 Signal flow documentation by glossing in: [#1504](https://codeberg.org/uzu/strudel/pulls/1504) +- 2025-09-07T21:27:29+02:00 Allow flattening of dirs with sample server by glossing in: [#1529](https://codeberg.org/uzu/strudel/pulls/1529) +- 2025-09-07T21:05:24+02:00 fix: prevent orbit clicking by defining max number of channels in an orbit by daslyfe in: [#1552](https://codeberg.org/uzu/strudel/pulls/1552) +- 2025-09-02T08:42:42+02:00 Padding for tooltips, preserving linebreaks in descriptions, not closing autocompletes on click by glossing in: [#1521](https://codeberg.org/uzu/strudel/pulls/1521) +- 2025-09-01T17:50:11+02:00 Update "restore defaults" to not delete patterns by glossing in: [#1519](https://codeberg.org/uzu/strudel/pulls/1519) + +## august 2025 + +- 2025-08-31T19:10:04+02:00 feat: add the ability to control the speed and start time of the reverb IR by daslyfe in: [#1501](https://codeberg.org/uzu/strudel/pulls/1501) +- 2025-08-25T02:31:22+02:00 Bug Fix: Duck on Mobile / Safari by glossing in: [#1503](https://codeberg.org/uzu/strudel/pulls/1503) +- 2025-08-23T18:39:53+02:00 fix benchmarks by yaxu in: [#1517](https://codeberg.org/uzu/strudel/pulls/1517) +- 2025-08-21T20:57:31+02:00 add --json flag to @strudel/sampler to generate a strudel.json by froos in: [#1513](https://codeberg.org/uzu/strudel/pulls/1513) +- 2025-08-21T10:04:28+02:00 remove hs2js postinstall by froos in: [#1510](https://codeberg.org/uzu/strudel/pulls/1510) +- 2025-08-20T15:15:34+02:00 doc: scrub, duckorbit, duckattack, duckdepth + fix: arp, arpWidth, hush by fyynn in: [#1502](https://codeberg.org/uzu/strudel/pulls/1502) +- 2025-08-20T12:31:37+02:00 fix: repl autocomplete not rendering correctly by robase in: [#1480](https://codeberg.org/uzu/strudel/pulls/1480) +- 2025-08-20T12:10:02+02:00 BUG FIX: make 'midin' initialization work with multiple controllers by cbabraham in: [#1469](https://codeberg.org/uzu/strudel/pulls/1469) +- 2025-08-17T23:18:46+02:00 euclidish by yaxu in: [#1482](https://codeberg.org/uzu/strudel/pulls/1482) +- 2025-08-17T06:37:23+02:00 feat: Create a duck (sidechain) orbit effect by daslyfe in: [#1470](https://codeberg.org/uzu/strudel/pulls/1470) +- 2025-08-17T04:40:15+02:00 Bug Fix: Restore `log` functionality after onTrigger arg removal by glossing in: [#1491](https://codeberg.org/uzu/strudel/pulls/1491) +- 2025-08-17T04:33:39+02:00 Bug Fix: Handle zero appearing first in FM's ADSR by glossing in: [#1496](https://codeberg.org/uzu/strudel/pulls/1496) +- 2025-08-17T04:27:19+02:00 Bug Fix: FM (and vibrato) for Supersaws by glossing in: [#1495](https://codeberg.org/uzu/strudel/pulls/1495) +- 2025-08-04T07:51:31+02:00 Fix incorrect stack Mini Notation by samyk in: [#1484](https://codeberg.org/uzu/strudel/pulls/1484) +- 2025-08-03T18:57:45+02:00 fix: regression caused by incorrectly exported alias for transpose and scaleTranspose by daslyfe in: [#1489](https://codeberg.org/uzu/strudel/pulls/1489) +- 2025-08-03T18:34:13+02:00 Add `strans` and `scaleTrans` as synonyms of `scaleTranspose` by TodePond in: [#1488](https://codeberg.org/uzu/strudel/pulls/1488) +- 2025-08-02T18:18:28+02:00 Add `trans` alias for `transpose` by TodePond in: [#1487](https://codeberg.org/uzu/strudel/pulls/1487) +- 2025-08-02T03:27:26+02:00 feat: Create Accurate 909 bass drum synthesizer by daslyfe in: [#1478](https://codeberg.org/uzu/strudel/pulls/1478) +- 2025-08-02T03:23:49+02:00 feat: Add fmwave control and ability to fm with noise by daslyfe in: [#1472](https://codeberg.org/uzu/strudel/pulls/1472) + +## july 2025 + +- 2025-07-30T00:22:41+02:00 hotfix: uzu kit JSON should be in the repo to avoid offline loading errors by daslyfe in: [#1485](https://codeberg.org/uzu/strudel/pulls/1485) +- 2025-07-24T05:28:23+02:00 feat: make uzu-drumkit the default drumkit by daslyfe in: [#1422](https://codeberg.org/uzu/strudel/pulls/1422) +- 2025-07-21T07:16:19+02:00 FIX: PWM modulation by daslyfe in: [#1467](https://codeberg.org/uzu/strudel/pulls/1467) +- 2025-07-19T17:16:22+02:00 Tremolo modulation by Ghost in: [#1095](https://codeberg.org/uzu/strudel/pulls/1095) +- 2025-07-15T09:39:33+02:00 fix: can now use def in mondough by froos in: [#1461](https://codeberg.org/uzu/strudel/pulls/1461) +- 2025-07-11T11:10:24+02:00 fixed keybinding presedence issue by Dsm0 in: [#1456](https://codeberg.org/uzu/strudel/pulls/1456) +- 2025-07-11T11:09:05+02:00 added multicursor support on ctrl/cmd + click by Dsm0 in: [#1457](https://codeberg.org/uzu/strudel/pulls/1457) +- 2025-07-09T09:39:46+02:00 added tab-indent setting by Dsm0 in: [#1454](https://codeberg.org/uzu/strudel/pulls/1454) +- 2025-07-08T15:48:41+02:00 ontrigger-refactoring by froos in: [#1442](https://codeberg.org/uzu/strudel/pulls/1442) +- 2025-07-07T23:14:24+02:00 fix: remove first gm_synth_bass_1, as it doesn't work in safari by froos in: [#1452](https://codeberg.org/uzu/strudel/pulls/1452) +- 2025-07-07T00:06:16+02:00 fix: minor doc changes by Aurel300 in: [#1435](https://codeberg.org/uzu/strudel/pulls/1435) +- 2025-07-05T12:53:45+02:00 Fix randrun and deps including shuffle. Fixes #1441 by yaxu in: [#1447](https://codeberg.org/uzu/strudel/pulls/1447) +- 2025-07-05T12:15:52+02:00 add setDefault + resetDefaults to superdough by froos in: [#1433](https://codeberg.org/uzu/strudel/pulls/1433) +- 2025-07-04T22:36:19+02:00 disable fm for supersaw by froos in: [#1443](https://codeberg.org/uzu/strudel/pulls/1443) + +## june 2025 + +- 2025-06-30T13:28:10+02:00 corrected minor spelling mistake (#1425) by tj-mueller in: [#1434](https://codeberg.org/uzu/strudel/pulls/1434) +- 2025-06-30T05:26:11+02:00 Add browser cache explanation in samples.mdx by Ghost in: [#1369](https://codeberg.org/uzu/strudel/pulls/1369) +- 2025-06-30T05:18:42+02:00 add-release-notes by froos in: [#1432](https://codeberg.org/uzu/strudel/pulls/1432) +- 2025-06-28T20:30:49+02:00 feat: delaytime in cycles by Ghost in: [#1341](https://codeberg.org/uzu/strudel/pulls/1341) +- 2025-06-28T13:44:49+02:00 Case insensitive search in the reference tab by kdiab in: [#1420](https://codeberg.org/uzu/strudel/pulls/1420) +- 2025-06-28T00:04:13+02:00 add unjoin, into and chunkinto by yaxu in: [#1418](https://codeberg.org/uzu/strudel/pulls/1418) +- 2025-06-26T15:29:46+02:00 mondo notation by froos in: [#1311](https://codeberg.org/uzu/strudel/pulls/1311) +- 2025-06-24T06:14:15+02:00 allow calling `all` multiple times by froos in: [#1417](https://codeberg.org/uzu/strudel/pulls/1417) +- 2025-06-24T05:53:22+02:00 tonal: allow scales without tonic (default to C) by Ghost in: [#1092](https://codeberg.org/uzu/strudel/pulls/1092) +- 2025-06-20T20:00:12+02:00 website intro: fix whitespace and code hosting name by trofi in: [#1400](https://codeberg.org/uzu/strudel/pulls/1400) +- 2025-06-19T10:15:07+02:00 fix stepcat #1396 by yaxu in: [#1398](https://codeberg.org/uzu/strudel/pulls/1398) +- 2025-06-19T09:52:38+02:00 deprecate cpm + refactor docs to use setcpm by froos in: [#1397](https://codeberg.org/uzu/strudel/pulls/1397) +- 2025-06-19T02:49:09+02:00 feat: 3 new codemirror themes by daslyfe in: [#1394](https://codeberg.org/uzu/strudel/pulls/1394) +- 2025-06-14T16:23:00+02:00 link back to tech manual in wiki by yaxu in: [#1382](https://codeberg.org/uzu/strudel/pulls/1382) +- 2025-06-14T14:55:37+02:00 degithub-links by froos in: [#1380](https://codeberg.org/uzu/strudel/pulls/1380) +- 2025-06-13T15:24:42+02:00 fix euclidLegatoRot by bwagner in: [#1378](https://codeberg.org/uzu/strudel/pulls/1378) +- 2025-06-13T10:07:51+02:00 remove-ms by yaxu in: [#1377](https://codeberg.org/uzu/strudel/pulls/1377) +- 2025-06-12T10:25:48+02:00 Share fat URL by Ghost in: [#1375](https://codeberg.org/uzu/strudel/pulls/1375) +- 2025-06-05T20:39:16+02:00 feat: onTriggerTime for interfacing with random APIs and doing illegal things on event time by Ghost in: [#1364](https://codeberg.org/uzu/strudel/pulls/1364) +- 2025-06-05T01:08:18+02:00 feat: berlin noise by Ghost in: [#1363](https://codeberg.org/uzu/strudel/pulls/1363) + +## may 2025 + +- 2025-05-29T14:36:43+02:00 Fix bytebeat sample offset by Ghost in: [#1359](https://codeberg.org/uzu/strudel/pulls/1359) +- 2025-05-29T12:14:18+02:00 preserve stepcount in chunks by yaxu in: [#1358](https://codeberg.org/uzu/strudel/pulls/1358) +- 2025-05-29T02:34:30+02:00 Byte Beat improvements -> 2 by Ghost in: [#1357](https://codeberg.org/uzu/strudel/pulls/1357) +- 2025-05-28T16:40:32+02:00 Bytebeat improvements by Ghost in: [#1356](https://codeberg.org/uzu/strudel/pulls/1356) +- 2025-05-27T17:42:39+02:00 feat: Byte Beats! by Ghost in: [#1354](https://codeberg.org/uzu/strudel/pulls/1354) +- 2025-05-18T21:43:05+02:00 fix: typo on docs causing problems with autocompletion. by Ghost in: [#1350](https://codeberg.org/uzu/strudel/pulls/1350) +- 2025-05-15T01:12:07+02:00 feat: Create pulsewidth (pw) and pulsewidth lfo parameters by Ghost in: [#1343](https://codeberg.org/uzu/strudel/pulls/1343) +- 2025-05-14T18:25:17+02:00 feat: Multi Channel Orbits by Ghost in: [#1344](https://codeberg.org/uzu/strudel/pulls/1344) +- 2025-05-13T17:45:29+02:00 Fix typo by Ghost in: [#1346](https://codeberg.org/uzu/strudel/pulls/1346) +- 2025-05-02T00:01:27+02:00 Fix web + repl package builds by froos in: [#1339](https://codeberg.org/uzu/strudel/pulls/1339) +- 2025-05-01T23:50:57+02:00 fix: superdough worklets bundling by froos in: [#1338](https://codeberg.org/uzu/strudel/pulls/1338) + +## april 2025 + +- 2025-04-27T22:25:54+02:00 FIX: sound import order by Ghost in: [#1333](https://codeberg.org/uzu/strudel/pulls/1333) +- 2025-04-27T18:39:49+02:00 update docs to reflect import sounds tab change by hpunq in: [#1332](https://codeberg.org/uzu/strudel/pulls/1332) +- 2025-04-27T18:38:26+02:00 feat: Improve gain curve by Ghost in: [#1318](https://codeberg.org/uzu/strudel/pulls/1318) +- 2025-04-27T07:54:49+02:00 Add Icon to import sample button by Ghost in: [#1331](https://codeberg.org/uzu/strudel/pulls/1331) +- 2025-04-27T07:53:41+02:00 Add new "import-sounds" tab with explanation on folder import by hpunq in: [#1329](https://codeberg.org/uzu/strudel/pulls/1329) +- 2025-04-22T00:04:38+02:00 feat: new themes + theme improvements by Ghost in: [#1326](https://codeberg.org/uzu/strudel/pulls/1326) +- 2025-04-20T18:24:15+02:00 feat: Create scrub function for scrubbing an audio file by Ghost in: [#1321](https://codeberg.org/uzu/strudel/pulls/1321) +- 2025-04-18T07:17:39+02:00 fix: disable astro toolbar by default by Ghost in: [#1324](https://codeberg.org/uzu/strudel/pulls/1324) +- 2025-04-18T07:17:38+02:00 fix: udels header by Ghost in: [#1325](https://codeberg.org/uzu/strudel/pulls/1325) +- 2025-04-11T09:18:58+02:00 Send delta in OSC message in seconds, to match tidal/superdirt by yaxu in: [#1323](https://codeberg.org/uzu/strudel/pulls/1323) +- 2025-04-08T06:08:57+02:00 FIX: Multichannel Audio by Ghost in: [#1322](https://codeberg.org/uzu/strudel/pulls/1322) +- 2025-04-08T04:18:03+02:00 feat: add max polyphony feature for superdough by Ghost in: [#1317](https://codeberg.org/uzu/strudel/pulls/1317) +- 2025-04-05T21:12:05+02:00 enhancement: make error messages easier to read by Ghost in: [#1315](https://codeberg.org/uzu/strudel/pulls/1315) +- 2025-04-04T06:26:57+02:00 fix: replace empty spaces in registered sound keys by Ghost in: [#1319](https://codeberg.org/uzu/strudel/pulls/1319) + +## march 2025 + +- 2025-03-26T17:01:05+01:00 small feat: Add alias for segment and ribbon by Ghost in: [#1314](https://codeberg.org/uzu/strudel/pulls/1314) +- 2025-03-26T14:54:18+01:00 Fix typo pattnr by Ghost in: [#1316](https://codeberg.org/uzu/strudel/pulls/1316) +- 2025-03-23T20:05:31+01:00 bugfix: Allow single param to be used in the as function by Ghost in: [#1312](https://codeberg.org/uzu/strudel/pulls/1312) +- 2025-03-20T23:35:18+01:00 Add MIDI Program Change, SysEx, NRPN, PitchBend and AfterTouch Output by nkymut in: [#1244](https://codeberg.org/uzu/strudel/pulls/1244) +- 2025-03-19T17:32:30+01:00 make soundfont base url configurable by Ghost in: [#1040](https://codeberg.org/uzu/strudel/pulls/1040) +- 2025-03-18T20:07:08+01:00 Add num samples from 0 up to 20 by yaxu in: [#1310](https://codeberg.org/uzu/strudel/pulls/1310) +- 2025-03-17T10:38:54+01:00 add num samples (edited numbers) by yaxu in: [#1309](https://codeberg.org/uzu/strudel/pulls/1309) +- 2025-03-08T22:17:47+01:00 @strudel/sampler improvements by froos in: [#1288](https://codeberg.org/uzu/strudel/pulls/1288) +- 2025-03-08T21:56:50+01:00 Add Gamepad module by nkymut in: [#1223](https://codeberg.org/uzu/strudel/pulls/1223) +- 2025-03-04T19:17:37+01:00 change behaviour of polymeter, and remove polymeterSteps by yaxu in: [#1302](https://codeberg.org/uzu/strudel/pulls/1302) +- 2025-03-04T03:36:57+01:00 feat: Create Pulse Oscillator with variable PWM by Ghost in: [#1304](https://codeberg.org/uzu/strudel/pulls/1304) +- 2025-03-02T17:08:31+01:00 feat: Theme improvements by Ghost in: [#1295](https://codeberg.org/uzu/strudel/pulls/1295) +- 2025-03-02T11:58:46+01:00 bugfix zoom stepcount by yaxu in: [#1301](https://codeberg.org/uzu/strudel/pulls/1301) + +## february 2025 + +- 2025-02-28T16:01:20+01:00 Fix test error #1297 by nkymut in: [#1298](https://codeberg.org/uzu/strudel/pulls/1298) +- 2025-02-28T02:53:42+01:00 Hotfix: prevent undefined pattern code from crashing strudel on load by Ghost in: [#1297](https://codeberg.org/uzu/strudel/pulls/1297) +- 2025-02-27T08:24:58+01:00 Fix misplaced ending sentence by Ghost in: [#1296](https://codeberg.org/uzu/strudel/pulls/1296) +- 2025-02-23T10:54:13+01:00 Signpost licenses for source code and samples a bit more, ref #1277 by yaxu in: [#1289](https://codeberg.org/uzu/strudel/pulls/1289) +- 2025-02-23T10:52:27+01:00 Allow wchooseCycles probabilities to be patterned by yaxu in: [#1292](https://codeberg.org/uzu/strudel/pulls/1292) +- 2025-02-22T02:18:59+01:00 Create Pattern Page Pagination by Ghost in: [#1287](https://codeberg.org/uzu/strudel/pulls/1287) +- 2025-02-21T22:38:10+01:00 showcase tweaks by yaxu in: [#1291](https://codeberg.org/uzu/strudel/pulls/1291) +- 2025-02-11T16:34:24+01:00 Fixes inverted triangle wave by renaming it to `itri`, making non-inverted `tri` by yaxu in: [#1283](https://codeberg.org/uzu/strudel/pulls/1283) +- 2025-02-11T10:02:34+01:00 Fix `squeezejoin` and functions using it, including `bite` by yaxu in: [#1286](https://codeberg.org/uzu/strudel/pulls/1286) +- 2025-02-09T16:18:28+01:00 Rename repeat back to extend by yaxu in: [#1285](https://codeberg.org/uzu/strudel/pulls/1285) +- 2025-02-07T18:11:52+01:00 mqtt bugfix - connection check by yaxu in: [#1282](https://codeberg.org/uzu/strudel/pulls/1282) +- 2025-02-07T17:38:29+01:00 Bugfix: update mqtt connections dictionary by yaxu in: [#1281](https://codeberg.org/uzu/strudel/pulls/1281) +- 2025-02-07T11:39:54+01:00 make mqtt topic patternable by yaxu in: [#1280](https://codeberg.org/uzu/strudel/pulls/1280) +- 2025-02-07T11:07:32+01:00 midimaps by froos in: [#1274](https://codeberg.org/uzu/strudel/pulls/1274) +- 2025-02-06T15:59:03+01:00 MQTT - support adding hap duration and cps metadata to JSON messages by yaxu in: [#1279](https://codeberg.org/uzu/strudel/pulls/1279) +- 2025-02-05T16:10:54+01:00 [breaking change] Sample signals from query onset, rather than midpoint by yaxu in: [#1278](https://codeberg.org/uzu/strudel/pulls/1278) +- 2025-02-03T00:55:36+01:00 Stepwise documentation tweaks, with mridangam samples by yaxu in: [#1275](https://codeberg.org/uzu/strudel/pulls/1275) +- 2025-02-02T21:26:44+01:00 Polish, rename, and document stepwise functions by yaxu in: [#1262](https://codeberg.org/uzu/strudel/pulls/1262) +- 2025-02-01T22:57:00+01:00 Fix sf2 timing by froos in: [#1272](https://codeberg.org/uzu/strudel/pulls/1272) +- 2025-02-01T22:32:59+01:00 Add bank aliasing and case insensitivity by TodePond in: [#1245](https://codeberg.org/uzu/strudel/pulls/1245) + +## january 2025 + +- 2025-01-31T09:39:54+01:00 Add Device Motion module by nkymut in: [#1217](https://codeberg.org/uzu/strudel/pulls/1217) +- 2025-01-29T15:40:01+01:00 add "as" function + getControlName by froos in: [#1247](https://codeberg.org/uzu/strudel/pulls/1247) +- 2025-01-29T15:30:04+01:00 Theme glowup by froos in: [#1268](https://codeberg.org/uzu/strudel/pulls/1268) +- 2025-01-29T15:22:18+01:00 patchday by froos in: [#1264](https://codeberg.org/uzu/strudel/pulls/1264) +- 2025-01-28T00:00:03+01:00 Revert "Fix sometimes" by yaxu in: [#1267](https://codeberg.org/uzu/strudel/pulls/1267) +- 2025-01-24T15:32:48+01:00 add reference package by froos in: [#1252](https://codeberg.org/uzu/strudel/pulls/1252) +- 2025-01-24T15:16:55+01:00 support `all(pianoroll)` and `all(pianoroll({labels: true}))` by yaxu in: [#1234](https://codeberg.org/uzu/strudel/pulls/1234) +- 2025-01-24T12:13:49+01:00 MQTT - if password isn't provided, prompt for one by yaxu in: [#1249](https://codeberg.org/uzu/strudel/pulls/1249) +- 2025-01-21T06:24:03+01:00 fix docs for beat function by Ghost in: [#1248](https://codeberg.org/uzu/strudel/pulls/1248) +- 2025-01-18T23:27:51+01:00 understand voicings page by froos in: [#1230](https://codeberg.org/uzu/strudel/pulls/1230) +- 2025-01-17T19:27:00+01:00 Add binary and binaryN by Ghost in: [#1226](https://codeberg.org/uzu/strudel/pulls/1226) +- 2025-01-16T12:18:33+01:00 Fix sometimes by yaxu in: [#1243](https://codeberg.org/uzu/strudel/pulls/1243) +- 2025-01-16T05:55:46+01:00 "beat" function for "step sequencer" style rhythm notation by Ghost in: [#1237](https://codeberg.org/uzu/strudel/pulls/1237) +- 2025-01-14T14:39:15+01:00 Add stepBind, and some toplevel aliases for binds and withValue by yaxu in: [#1241](https://codeberg.org/uzu/strudel/pulls/1241) +- 2025-01-14T06:11:10+01:00 Add onKey function for custom key commands for patterns by Ghost in: [#1235](https://codeberg.org/uzu/strudel/pulls/1235) +- 2025-01-12T11:32:24+01:00 Update documentation for param value modification by Ghost in: [#1238](https://codeberg.org/uzu/strudel/pulls/1238) + +## december 2024 + +- 2024-12-29T13:16:08+01:00 Documentation for all/each, and bugfix for each by yaxu in: [#1233](https://codeberg.org/uzu/strudel/pulls/1233) +- 2024-12-29T11:29:52+01:00 Make `all()` post-stack again, and add `each()` for pre-stack by yaxu in: [#1229](https://codeberg.org/uzu/strudel/pulls/1229) +- 2024-12-27T22:16:54+01:00 suggested changes to voicings.mdx by Ghost in: [#1231](https://codeberg.org/uzu/strudel/pulls/1231) +- 2024-12-23T11:53:11+01:00 add basic spectrum function by froos in: [#1213](https://codeberg.org/uzu/strudel/pulls/1213) +- 2024-12-22T21:04:45+01:00 Fix regression for d1, p1, p(n) by yaxu in: [#1227](https://codeberg.org/uzu/strudel/pulls/1227) + +## november 2024 + +- 2024-11-30T10:09:36+01:00 Make cps patternable by eefano in: [#1001](https://codeberg.org/uzu/strudel/pulls/1001) +- 2024-11-30T10:07:56+01:00 MQTT support by yaxu in: [#1224](https://codeberg.org/uzu/strudel/pulls/1224) +- 2024-11-30T09:46:14+01:00 Apply `all` function to individual patterns rather than final stack by yaxu in: [#1209](https://codeberg.org/uzu/strudel/pulls/1209) +- 2024-11-20T16:32:51+01:00 REPL: solo and sync configuration by Ghost in: [#1214](https://codeberg.org/uzu/strudel/pulls/1214) + +## october 2024 + +- 2024-10-30T21:29:43+01:00 Add s_zip for 'cat'-ing patterns together step-by-step, bugfix `steps` by yaxu in: [#1208](https://codeberg.org/uzu/strudel/pulls/1208) +- 2024-10-30T21:28:32+01:00 Preserve tactus for 'degrade' and friends, and tidy up 'pick' and friends by yaxu in: [#1205](https://codeberg.org/uzu/strudel/pulls/1205) +- 2024-10-30T17:11:53+01:00 chore: Edit run locally instructions in README.md by Ghost in: [#1206](https://codeberg.org/uzu/strudel/pulls/1206) +- 2024-10-23T23:08:14+02:00 colorize console + tweak header by froos in: [#1203](https://codeberg.org/uzu/strudel/pulls/1203) +- 2024-10-22T22:55:00+02:00 markcss by froos in: [#1202](https://codeberg.org/uzu/strudel/pulls/1202) +- 2024-10-21T22:56:37+02:00 add 2 new ui settings by froos in: [#1200](https://codeberg.org/uzu/strudel/pulls/1200) +- 2024-10-21T20:22:52+02:00 Make panel hover behavior optional by Ghost in: [#1199](https://codeberg.org/uzu/strudel/pulls/1199) +- 2024-10-19T04:28:36+02:00 Menu Panel Improvements! by Ghost in: [#1193](https://codeberg.org/uzu/strudel/pulls/1193) +- 2024-10-19T01:10:06+02:00 update lockfile + minor versions by froos in: [#1198](https://codeberg.org/uzu/strudel/pulls/1198) + +## september 2024 + +- 2024-09-27T00:17:00+02:00 remove redundant example for cat, update snapshot by Ghost in: [#1189](https://codeberg.org/uzu/strudel/pulls/1189) +- 2024-09-25T13:18:19+02:00 Adding search bar (soundtab.jsx) by Ghost in: [#1185](https://codeberg.org/uzu/strudel/pulls/1185) +- 2024-09-23T17:18:34+02:00 Screenreader improvements by yaxu in: [#1158](https://codeberg.org/uzu/strudel/pulls/1158) +- 2024-09-20T22:26:41+02:00 Fix serial timing by yaxu in: [#1188](https://codeberg.org/uzu/strudel/pulls/1188) +- 2024-09-20T00:26:30+02:00 Add bite function by yaxu in: [#1187](https://codeberg.org/uzu/strudel/pulls/1187) +- 2024-09-14T13:30:53+02:00 better spacing in zen mode by froos in: [#1147](https://codeberg.org/uzu/strudel/pulls/1147) +- 2024-09-14T10:50:24+02:00 add filter + filterWhen + within by froos in: [#1039](https://codeberg.org/uzu/strudel/pulls/1039) +- 2024-09-14T10:49:21+02:00 refactor sampler by froos in: [#1101](https://codeberg.org/uzu/strudel/pulls/1101) +- 2024-09-14T10:43:17+02:00 handle midin device not found error by froos in: [#1146](https://codeberg.org/uzu/strudel/pulls/1146) +- 2024-09-13T21:59:23+02:00 Add a search bar to the REPL Reference tab by Ghost in: [#1165](https://codeberg.org/uzu/strudel/pulls/1165) +- 2024-09-13T21:58:47+02:00 Correct spelling mistakes by Ghost in: [#1183](https://codeberg.org/uzu/strudel/pulls/1183) +- 2024-09-07T23:41:29+02:00 Add seqPLoop from Tidal by yaxu in: [#1182](https://codeberg.org/uzu/strudel/pulls/1182) +- 2024-09-05T05:52:50+02:00 make phaser control match superdirt by Ghost in: [#1180](https://codeberg.org/uzu/strudel/pulls/1180) +- 2024-09-05T05:33:41+02:00 Revert "Make phaser control consistent with superdirt" by Ghost in: [#1179](https://codeberg.org/uzu/strudel/pulls/1179) +- 2024-09-05T05:29:12+02:00 Make phaser control consistent with superdirt by Ghost in: [#1178](https://codeberg.org/uzu/strudel/pulls/1178) +- 2024-09-03T04:37:15+02:00 fix sample speed when using splice and fit with superdirt by Ghost in: [#1172](https://codeberg.org/uzu/strudel/pulls/1172) +- 2024-09-01T16:02:24+02:00 Create audio target selector for OSC/Superdirt by Ghost in: [#1160](https://codeberg.org/uzu/strudel/pulls/1160) +- 2024-09-01T15:03:47+02:00 Fixes fit so it works after a chop or slice by yaxu in: [#1171](https://codeberg.org/uzu/strudel/pulls/1171) + +## august 2024 + +- 2024-08-30T14:24:08+02:00 polyJoin by yaxu in: [#1168](https://codeberg.org/uzu/strudel/pulls/1168) +- 2024-08-23T17:05:10+02:00 Add scramble and shuffle by yaxu in: [#1167](https://codeberg.org/uzu/strudel/pulls/1167) +- 2024-08-18T18:22:20+02:00 Improve + simplify neocyclist timing by Ghost in: [#1164](https://codeberg.org/uzu/strudel/pulls/1164) +- 2024-08-15T05:18:33+02:00 containerize/seperate out boolean checks for repl types/Repl logic into bespoke components. by Ghost in: [#1163](https://codeberg.org/uzu/strudel/pulls/1163) +- 2024-08-12T18:57:21+02:00 [CORS HOTFIX] by Ghost in: [#1162](https://codeberg.org/uzu/strudel/pulls/1162) +- 2024-08-09T05:11:28+02:00 Fix OSC clock jitter by Ghost in: [#1157](https://codeberg.org/uzu/strudel/pulls/1157) + +## july 2024 + +- 2024-07-27T11:02:38+02:00 Fix loopAt tactus by yaxu in: [#1145](https://codeberg.org/uzu/strudel/pulls/1145) +- 2024-07-26T04:37:34+02:00 "stretch" function (phase vocoder) by Ghost in: [#1130](https://codeberg.org/uzu/strudel/pulls/1130) +- 2024-07-26T04:37:17+02:00 Udels (MultiFrame Strudel) Revisited by Ghost in: [#1132](https://codeberg.org/uzu/strudel/pulls/1132) +- 2024-07-24T11:40:28+02:00 Fix tactus marking in mininotation by yaxu in: [#1144](https://codeberg.org/uzu/strudel/pulls/1144) + +## june 2024 + +- 2024-06-25T18:13:28+02:00 export comment commands by froos in: [#1136](https://codeberg.org/uzu/strudel/pulls/1136) +- 2024-06-24T18:19:22+02:00 Chop chop by yaxu in: [#1078](https://codeberg.org/uzu/strudel/pulls/1078) +- 2024-06-18T23:58:08+02:00 Fix bug in Fraction.lcm by yaxu in: [#1133](https://codeberg.org/uzu/strudel/pulls/1133) +- 2024-06-18T05:32:12+02:00 Fix clock worker dependency path in module builds by Ghost in: [#1129](https://codeberg.org/uzu/strudel/pulls/1129) +- 2024-06-04T00:26:48+02:00 Labeled statements doc by froos in: [#1126](https://codeberg.org/uzu/strudel/pulls/1126) +- 2024-06-03T22:40:21+02:00 doc: visual functions + refactor onPaint by froos in: [#1125](https://codeberg.org/uzu/strudel/pulls/1125) +- 2024-06-02T18:36:29+02:00 Fix indexDB failing with large amount of files by Ghost in: [#1124](https://codeberg.org/uzu/strudel/pulls/1124) + +## may 2024 + +- 2024-05-31T12:17:34+02:00 Migrate tutorial fanchor by froos in: [#1122](https://codeberg.org/uzu/strudel/pulls/1122) +- 2024-05-31T10:46:47+02:00 [BUG FIX] Audio worklets sometimes dont load by Ghost in: [#1121](https://codeberg.org/uzu/strudel/pulls/1121) +- 2024-05-31T10:25:24+02:00 change fanchor to 0 by Ghost in: [#1107](https://codeberg.org/uzu/strudel/pulls/1107) +- 2024-05-30T14:39:30+02:00 fix: use full repl in web package by froos in: [#1119](https://codeberg.org/uzu/strudel/pulls/1119) +- 2024-05-30T09:36:32+02:00 can now access strudelMirror from repl by froos in: [#1117](https://codeberg.org/uzu/strudel/pulls/1117) +- 2024-05-29T13:06:34+02:00 Add the mousex and mousey signal by Ghost in: [#1112](https://codeberg.org/uzu/strudel/pulls/1112) +- 2024-05-29T13:02:00+02:00 Fixes drawPianoroll import in codemirror example by Ghost in: [#1116](https://codeberg.org/uzu/strudel/pulls/1116) +- 2024-05-28T22:43:46+02:00 clarify `off` in pattern-effects.mdx by Ghost in: [#1074](https://codeberg.org/uzu/strudel/pulls/1074) +- 2024-05-26T17:33:07+02:00 rollback phaser by Ghost in: [#1113](https://codeberg.org/uzu/strudel/pulls/1113) +- 2024-05-26T17:33:06+02:00 Fix audio worklets by Ghost in: [#1114](https://codeberg.org/uzu/strudel/pulls/1114) +- 2024-05-26T16:53:56+02:00 Calculate phaser modulation phase based on time by Ghost in: [#1110](https://codeberg.org/uzu/strudel/pulls/1110) +- 2024-05-23T15:06:01+02:00 Add analog-style ladder filter by Ghost in: [#1103](https://codeberg.org/uzu/strudel/pulls/1103) +- 2024-05-22T12:53:20+02:00 fix sampler on windows by Ghost in: [#1109](https://codeberg.org/uzu/strudel/pulls/1109) +- 2024-05-20T23:26:20+02:00 web package fixes by froos in: [#1044](https://codeberg.org/uzu/strudel/pulls/1044) +- 2024-05-20T22:41:28+02:00 Fix sampler windows by froos in: [#1108](https://codeberg.org/uzu/strudel/pulls/1108) +- 2024-05-19T12:07:26+02:00 hs2js package / tidal parser by froos in: [#870](https://codeberg.org/uzu/strudel/pulls/870) +- 2024-05-18T10:49:08+02:00 fix little dub tune example by Ghost in: [#1104](https://codeberg.org/uzu/strudel/pulls/1104) +- 2024-05-17T14:43:58+02:00 Samples tab improvements by Ghost in: [#1102](https://codeberg.org/uzu/strudel/pulls/1102) +- 2024-05-13T10:10:34+02:00 osc: couple of fixes by Ghost in: [#1093](https://codeberg.org/uzu/strudel/pulls/1093) +- 2024-05-13T06:31:20+02:00 Use sessionStorage for viewingPatternData and activePattern by Ghost in: [#1091](https://codeberg.org/uzu/strudel/pulls/1091) +- 2024-05-12T18:23:09+02:00 repl: set document.title from @title by Ghost in: [#1090](https://codeberg.org/uzu/strudel/pulls/1090) +- 2024-05-07T14:29:22+02:00 fix: missing events due to premature worklet cleanup by froos in: [#1089](https://codeberg.org/uzu/strudel/pulls/1089) +- 2024-05-03T11:52:57+02:00 Benchmarks by yaxu in: [#1079](https://codeberg.org/uzu/strudel/pulls/1079) +- 2024-05-03T08:46:52+02:00 fix: csound + dough timing by froos in: [#1086](https://codeberg.org/uzu/strudel/pulls/1086) +- 2024-05-03T00:37:32+02:00 Improve performance of ! (replicate) by yaxu in: [#1084](https://codeberg.org/uzu/strudel/pulls/1084) +- 2024-05-02T23:40:22+02:00 fix: url parsing with extra params by froos in: [#1083](https://codeberg.org/uzu/strudel/pulls/1083) + +## april 2024 + +- 2024-04-29T12:36:11+02:00 Tactus calculation toggle and breaking change to tactus calculation in fast/slow/hurry by yaxu in: [#1081](https://codeberg.org/uzu/strudel/pulls/1081) +- 2024-04-28T20:56:21+02:00 fix docs on alignment.mdx by Ghost in: [#1076](https://codeberg.org/uzu/strudel/pulls/1076) +- 2024-04-28T20:49:18+02:00 add signals to recap in first-effects.mdx by Ghost in: [#1073](https://codeberg.org/uzu/strudel/pulls/1073) +- 2024-04-28T20:44:57+02:00 fix translation issue in first-effects.mdx by Ghost in: [#1072](https://codeberg.org/uzu/strudel/pulls/1072) +- 2024-04-28T20:44:29+02:00 fix failing format test by Ghost in: [#1077](https://codeberg.org/uzu/strudel/pulls/1077) +- 2024-04-27T22:50:17+02:00 add nesting to `off` example variation in pattern-effects.mdx by Ghost in: [#1075](https://codeberg.org/uzu/strudel/pulls/1075) +- 2024-04-27T22:48:58+02:00 add `<...>` to first-sounds.mdx recap by Ghost in: [#1070](https://codeberg.org/uzu/strudel/pulls/1070) +- 2024-04-27T22:48:07+02:00 fix first sounds typo by Ghost in: [#1069](https://codeberg.org/uzu/strudel/pulls/1069) +- 2024-04-27T22:47:44+02:00 fix cr typo on first-sounds.mdx by Ghost in: [#1068](https://codeberg.org/uzu/strudel/pulls/1068) +- 2024-04-26T15:12:30+02:00 More tactus tidying by yaxu in: [#1071](https://codeberg.org/uzu/strudel/pulls/1071) +- 2024-04-23T23:37:21+02:00 Fix stepjoin by yaxu in: [#1067](https://codeberg.org/uzu/strudel/pulls/1067) +- 2024-04-23T23:32:00+02:00 clarify license by yaxu in: [#1064](https://codeberg.org/uzu/strudel/pulls/1064) +- 2024-04-23T15:14:30+02:00 Tactus tweaks - fixes for maintaining tactus and highlight locations by yaxu in: [#1065](https://codeberg.org/uzu/strudel/pulls/1065) +- 2024-04-21T23:57:57+02:00 fix OSC timing for recent scheduler updates by Ghost in: [#1062](https://codeberg.org/uzu/strudel/pulls/1062) +- 2024-04-21T22:17:07+02:00 Stepwise functions from Tidal by yaxu in: [#1060](https://codeberg.org/uzu/strudel/pulls/1060) +- 2024-04-21T11:03:55+02:00 Fix wchooseCycles not picking the whole pattern by Ghost in: [#1061](https://codeberg.org/uzu/strudel/pulls/1061) +- 2024-04-19T00:05:52+02:00 add swing + swingBy by froos in: [#1038](https://codeberg.org/uzu/strudel/pulls/1038) +- 2024-04-19T00:05:08+02:00 anonymous patterns + muting by froos in: [#1059](https://codeberg.org/uzu/strudel/pulls/1059) +- 2024-04-12T12:34:27+02:00 improve tutorial + custom samples doc by froos in: [#1053](https://codeberg.org/uzu/strudel/pulls/1053) +- 2024-04-12T12:31:49+02:00 fix: do not reset cc input values on each eval by froos in: [#1054](https://codeberg.org/uzu/strudel/pulls/1054) +- 2024-04-08T17:25:27+02:00 transpose: support all combinations of numbers and strings for notes and intervals by froos in: [#1048](https://codeberg.org/uzu/strudel/pulls/1048) +- 2024-04-08T10:46:18+02:00 Wax, wane, taper and taperlist by yaxu in: [#1042](https://codeberg.org/uzu/strudel/pulls/1042) +- 2024-04-06T23:44:57+02:00 Midi Time hotfix for scheduler updates by Ghost in: [#1047](https://codeberg.org/uzu/strudel/pulls/1047) +- 2024-04-05T12:48:03+02:00 pitchwheel visual by froos in: [#1041](https://codeberg.org/uzu/strudel/pulls/1041) +- 2024-04-05T12:47:19+02:00 fix cyclist fizzling out by froos in: [#1046](https://codeberg.org/uzu/strudel/pulls/1046) + +## march 2024 + +- 2024-03-30T16:05:59+01:00 remove dangerous arithmetic feature by froos in: [#1030](https://codeberg.org/uzu/strudel/pulls/1030) +- 2024-03-30T16:05:27+01:00 Fix sampler paths by froos in: [#1034](https://codeberg.org/uzu/strudel/pulls/1034) +- 2024-03-30T14:43:08+01:00 local sample server cli by froos in: [#1033](https://codeberg.org/uzu/strudel/pulls/1033) +- 2024-03-29T17:14:28+01:00 add font file types to offline cache by froos in: [#1032](https://codeberg.org/uzu/strudel/pulls/1032) +- 2024-03-29T17:10:16+01:00 add closeBrackets setting by froos in: [#1031](https://codeberg.org/uzu/strudel/pulls/1031) +- 2024-03-29T14:55:06+01:00 Tactus tidy by yaxu in: [#1027](https://codeberg.org/uzu/strudel/pulls/1027) +- 2024-03-28T17:06:44+01:00 add setting for sync flag by froos in: [#1025](https://codeberg.org/uzu/strudel/pulls/1025) +- 2024-03-28T11:37:57+01:00 better theme integration for visuals + various fixes by froos in: [#1024](https://codeberg.org/uzu/strudel/pulls/1024) +- 2024-03-28T11:33:25+01:00 More fonts by froos in: [#1023](https://codeberg.org/uzu/strudel/pulls/1023) +- 2024-03-27T13:06:05+01:00 Feature: tactus marking by yaxu in: [#1021](https://codeberg.org/uzu/strudel/pulls/1021) +- 2024-03-25T06:02:02+01:00 hotfix for 1017 by Ghost in: [#1020](https://codeberg.org/uzu/strudel/pulls/1020) +- 2024-03-24T15:06:33+01:00 Document signals by Ghost in: [#1015](https://codeberg.org/uzu/strudel/pulls/1015) +- 2024-03-24T10:16:11+01:00 Repl sync fixes by Ghost in: [#1014](https://codeberg.org/uzu/strudel/pulls/1014) +- 2024-03-23T20:21:51+01:00 eliminate chromium clock jitter by froos in: [#1004](https://codeberg.org/uzu/strudel/pulls/1004) +- 2024-03-23T15:51:07+01:00 update undocumented script by froos in: [#1013](https://codeberg.org/uzu/strudel/pulls/1013) +- 2024-03-23T15:26:52+01:00 fix: await injectPatternMethods by froos in: [#1012](https://codeberg.org/uzu/strudel/pulls/1012) +- 2024-03-23T15:18:12+01:00 rename trig -> reset, trigzero -> restart by froos in: [#1010](https://codeberg.org/uzu/strudel/pulls/1010) +- 2024-03-23T12:30:03+01:00 Inline punchcard + spiral by froos in: [#1008](https://codeberg.org/uzu/strudel/pulls/1008) +- 2024-03-23T12:27:22+01:00 Color in hap value by froos in: [#1007](https://codeberg.org/uzu/strudel/pulls/1007) +- 2024-03-22T23:53:51+01:00 using strudel in your project guide + cleanup examples by froos in: [#1006](https://codeberg.org/uzu/strudel/pulls/1006) +- 2024-03-22T23:41:50+01:00 accidentals in scale degrees by eefano in: [#1000](https://codeberg.org/uzu/strudel/pulls/1000) +- 2024-03-21T22:53:55+01:00 supersaw oscillator by Ghost in: [#978](https://codeberg.org/uzu/strudel/pulls/978) +- 2024-03-21T13:00:04+01:00 remove canvas, externalize samples, delete junk by froos in: [#1003](https://codeberg.org/uzu/strudel/pulls/1003) +- 2024-03-18T11:37:55+01:00 Fix pure mini highlight by yaxu in: [#994](https://codeberg.org/uzu/strudel/pulls/994) +- 2024-03-18T07:12:14+01:00 inline viz / widgets package by froos in: [#989](https://codeberg.org/uzu/strudel/pulls/989) +- 2024-03-17T04:07:00+01:00 Labeled statements by froos in: [#991](https://codeberg.org/uzu/strudel/pulls/991) +- 2024-03-16T18:24:38+01:00 Beat-oriented functionality by yaxu in: [#976](https://codeberg.org/uzu/strudel/pulls/976) +- 2024-03-15T01:47:35+01:00 REPL sync between windows by Ghost in: [#900](https://codeberg.org/uzu/strudel/pulls/900) +- 2024-03-14T00:20:07+01:00 Update synths.mdx by Ghost in: [#984](https://codeberg.org/uzu/strudel/pulls/984) +- 2024-03-13T00:36:22+01:00 fix: share now shares what's visible instead of active by froos in: [#985](https://codeberg.org/uzu/strudel/pulls/985) +- 2024-03-10T01:18:57+01:00 use ireal as default voicing dict by froos in: [#967](https://codeberg.org/uzu/strudel/pulls/967) +- 2024-03-10T00:50:23+01:00 little fix for withVal by eefano in: [#980](https://codeberg.org/uzu/strudel/pulls/980) +- 2024-03-10T00:46:51+01:00 Velocity in value by froos in: [#974](https://codeberg.org/uzu/strudel/pulls/974) +- 2024-03-10T00:42:50+01:00 fix: clear hydra on reset by froos in: [#983](https://codeberg.org/uzu/strudel/pulls/983) +- 2024-03-10T00:38:43+01:00 move canvas related helpers from core to new draw package by froos in: [#971](https://codeberg.org/uzu/strudel/pulls/971) +- 2024-03-08T05:38:07+01:00 replace shape with distort in learn doc by Ghost in: [#982](https://codeberg.org/uzu/strudel/pulls/982) +- 2024-03-06T12:14:49+01:00 alias - for ~ by yaxu in: [#981](https://codeberg.org/uzu/strudel/pulls/981) +- 2024-03-04T16:04:23+01:00 Worklet Improvents / fixes by Ghost in: [#963](https://codeberg.org/uzu/strudel/pulls/963) +- 2024-03-01T17:30:19+01:00 Nested controls by froos in: [#973](https://codeberg.org/uzu/strudel/pulls/973) + +## february 2024 + +- 2024-02-29T15:33:12+01:00 feat: can now invert euclid pulses with negative numbers by froos in: [#959](https://codeberg.org/uzu/strudel/pulls/959) +- 2024-02-29T15:31:23+01:00 pickOut(), pickRestart(), pickReset() by eefano in: [#950](https://codeberg.org/uzu/strudel/pulls/950) +- 2024-02-29T15:12:45+01:00 remove legacy legato + duration implementations by froos in: [#965](https://codeberg.org/uzu/strudel/pulls/965) +- 2024-02-29T08:47:53+01:00 fix for transpose(): preserve hap value object structure by eefano in: [#966](https://codeberg.org/uzu/strudel/pulls/966) +- 2024-02-29T08:32:00+01:00 add debounce to logger by froos in: [#968](https://codeberg.org/uzu/strudel/pulls/968) +- 2024-02-28T18:43:52+01:00 controls refactoring: simplify exports by froos in: [#962](https://codeberg.org/uzu/strudel/pulls/962) +- 2024-02-25T19:17:00+01:00 fix: reset global fx on pattern change by froos in: [#960](https://codeberg.org/uzu/strudel/pulls/960) +- 2024-02-25T14:15:30+01:00 fix midi issue on firefox and added quote error by Ghost in: [#936](https://codeberg.org/uzu/strudel/pulls/936) +- 2024-02-25T13:19:47+01:00 'Enable Bracket Matching' option in Codemirror by eefano in: [#956](https://codeberg.org/uzu/strudel/pulls/956) +- 2024-02-23T14:37:46+01:00 fix script importable packages (web + repl) by froos in: [#957](https://codeberg.org/uzu/strudel/pulls/957) +- 2024-02-21T16:17:37+01:00 account for cps in midi time duration by Ghost in: [#954](https://codeberg.org/uzu/strudel/pulls/954) +- 2024-02-21T10:27:12+01:00 Auto await samples by froos in: [#955](https://codeberg.org/uzu/strudel/pulls/955) +- 2024-02-08T13:16:15+01:00 remove cjs builds by froos in: [#945](https://codeberg.org/uzu/strudel/pulls/945) +- 2024-02-04T23:15:37+01:00 Minor documentation error: Update first-sounds.mdx by Ghost in: [#941](https://codeberg.org/uzu/strudel/pulls/941) + + +## january 2024 + +- 2024-01-24T16:48:57+01:00 fix: pianoroll sorting by froos in: [#938](https://codeberg.org/uzu/strudel/pulls/938) +- 2024-01-23T00:04:03+01:00 V1 release notes by froos in: [#935](https://codeberg.org/uzu/strudel/pulls/935) +- 2024-01-22T20:20:53+01:00 2 years blog post by froos in: [#929](https://codeberg.org/uzu/strudel/pulls/929) +- 2024-01-22T20:02:34+01:00 make 0.5hz cps the default by yaxu in: [#931](https://codeberg.org/uzu/strudel/pulls/931) +- 2024-01-22T00:52:01+01:00 Fix pattern tab not showing patterns without created date by Ghost in: [#934](https://codeberg.org/uzu/strudel/pulls/934) +- 2024-01-21T20:46:28+01:00 Add useful pattern selection behavior for performing. by Ghost in: [#897](https://codeberg.org/uzu/strudel/pulls/897) +- 2024-01-21T01:30:28+01:00 Refactor cps functions by froos in: [#933](https://codeberg.org/uzu/strudel/pulls/933) +- 2024-01-20T23:47:31+01:00 Make splice cps-aware by yaxu in: [#932](https://codeberg.org/uzu/strudel/pulls/932) +- 2024-01-19T18:50:57+01:00 community bakery by froos in: [#923](https://codeberg.org/uzu/strudel/pulls/923) +- 2024-01-19T18:49:54+01:00 add pickF and pickmodF by Ghost in: [#924](https://codeberg.org/uzu/strudel/pulls/924) +- 2024-01-19T15:10:48+01:00 Mini-notation additions towards tidal compatibility by yaxu in: [#926](https://codeberg.org/uzu/strudel/pulls/926) +- 2024-01-18T23:34:11+01:00 Blog improvements by froos in: [#919](https://codeberg.org/uzu/strudel/pulls/919) +- 2024-01-18T18:08:29+01:00 pick, pickmod, inhabit, inhabitmod by yaxu in: [#921](https://codeberg.org/uzu/strudel/pulls/921) +- 2024-01-18T18:04:27+01:00 Revert "`pick` now accepts lookup tables, with alternate cycle squeezing behaviour as new `inhabit` function" by yaxu in: [#920](https://codeberg.org/uzu/strudel/pulls/920) +- 2024-01-18T17:45:39+01:00 `pick` now accepts lookup tables, with alternate cycle squeezing behaviour as new `inhabit` function by yaxu in: [#918](https://codeberg.org/uzu/strudel/pulls/918) +- 2024-01-18T10:30:08+01:00 rename @strudel.cycles/* packages to @strudel/* by froos in: [#917](https://codeberg.org/uzu/strudel/pulls/917) +- 2024-01-18T06:54:33+01:00 pitch envelopes by froos in: [#913](https://codeberg.org/uzu/strudel/pulls/913) +- 2024-01-18T05:09:38+01:00 Fix: swatch/[name].png.js static path by Ghost in: [#916](https://codeberg.org/uzu/strudel/pulls/916) +- 2024-01-16T08:22:28+01:00 Add more vowel qualities for the vowels function by Ghost in: [#907](https://codeberg.org/uzu/strudel/pulls/907) +- 2024-01-14T23:56:36+01:00 adds a blog by froos in: [#911](https://codeberg.org/uzu/strudel/pulls/911) +- 2024-01-14T23:52:41+01:00 Remove hideHeader for better mobile UI and consistency by Ghost in: [#894](https://codeberg.org/uzu/strudel/pulls/894) +- 2024-01-14T22:43:06+01:00 Further Envelope improvements by Ghost in: [#868](https://codeberg.org/uzu/strudel/pulls/868) +- 2024-01-14T00:48:04+01:00 public sharing by froos in: [#910](https://codeberg.org/uzu/strudel/pulls/910) +- 2024-01-12T18:31:41+01:00 fix some build warnings by froos in: [#902](https://codeberg.org/uzu/strudel/pulls/902) +- 2024-01-12T18:01:55+01:00 prevent vite from complaining about additional exports in jsx files by Ghost in: [#891](https://codeberg.org/uzu/strudel/pulls/891) +- 2024-01-12T17:05:47+01:00 Update Vite version so hot reload works properly with newest pnpm version by Ghost in: [#892](https://codeberg.org/uzu/strudel/pulls/892) +- 2024-01-12T15:09:41+01:00 support , in < > by froos in: [#886](https://codeberg.org/uzu/strudel/pulls/886) +- 2024-01-10T13:55:39+01:00 fix: autocomplete / tooltip code example bug by froos in: [#898](https://codeberg.org/uzu/strudel/pulls/898) +- 2024-01-06T15:23:55+01:00 fix: invisible selection on vim + emacs mode by froos in: [#889](https://codeberg.org/uzu/strudel/pulls/889) +- 2024-01-05T22:29:20+01:00 scales can now be anchored by froos in: [#888](https://codeberg.org/uzu/strudel/pulls/888) +- 2024-01-04T11:21:42+01:00 add root mode for voicings by froos in: [#887](https://codeberg.org/uzu/strudel/pulls/887) +- 2024-01-01T18:32:17+01:00 Showcase by froos in: [#885](https://codeberg.org/uzu/strudel/pulls/885) +- 2024-01-01T14:22:11+01:00 bugfix: suspend and close exisiting audio context when changing interface by Ghost in: [#882](https://codeberg.org/uzu/strudel/pulls/882) +- 2024-01-01T14:21:52+01:00 add mastodon link by froos in: [#884](https://codeberg.org/uzu/strudel/pulls/884) + +## december 2023 + +- 2023-12-31T17:02:57+01:00 fix: make sure n is never undefined before nanFallback by froos in: [#881](https://codeberg.org/uzu/strudel/pulls/881) +- 2023-12-31T16:47:22+01:00 Error tolerance by froos in: [#880](https://codeberg.org/uzu/strudel/pulls/880) +- 2023-12-31T10:06:24+01:00 bugfix: sound select indexes out of bounds by Ghost in: [#871](https://codeberg.org/uzu/strudel/pulls/871) +- 2023-12-31T10:03:01+01:00 Audio device selection by Ghost in: [#854](https://codeberg.org/uzu/strudel/pulls/854) +- 2023-12-31T00:59:49+01:00 Dependency update by froos in: [#879](https://codeberg.org/uzu/strudel/pulls/879) +- 2023-12-29T16:53:41+01:00 move all examples to separate examples folder by froos in: [#878](https://codeberg.org/uzu/strudel/pulls/878) +- 2023-12-29T15:29:17+01:00 final vanillification by froos in: [#876](https://codeberg.org/uzu/strudel/pulls/876) +- 2023-12-27T18:38:09+01:00 Bug Fix #119: Clock drift by Ghost in: [#874](https://codeberg.org/uzu/strudel/pulls/874) +- 2023-12-27T13:17:03+01:00 main repl vanillification by froos in: [#873](https://codeberg.org/uzu/strudel/pulls/873) +- 2023-12-25T17:47:25+01:00 more work on vanilla repl: repl web component + package + MicroRepl by froos in: [#866](https://codeberg.org/uzu/strudel/pulls/866) +- 2023-12-15T00:11:35+01:00 Vanilla repl 3 by froos in: [#865](https://codeberg.org/uzu/strudel/pulls/865) +- 2023-12-14T21:28:49+01:00 Vanilla repl 2 by froos in: [#863](https://codeberg.org/uzu/strudel/pulls/863) +- 2023-12-12T21:49:23+01:00 fix: finally repair envelopes by froos in: [#861](https://codeberg.org/uzu/strudel/pulls/861) +- 2023-12-12T21:20:00+01:00 add missing trailing slashes by froos in: [#860](https://codeberg.org/uzu/strudel/pulls/860) +- 2023-12-12T19:08:31+01:00 Sound Import from local file system by Ghost in: [#839](https://codeberg.org/uzu/strudel/pulls/839) +- 2023-12-11T22:48:35+01:00 Pattern organization by froos in: [#858](https://codeberg.org/uzu/strudel/pulls/858) +- 2023-12-09T17:25:11+01:00 Export patterns + ui tweaks by froos in: [#855](https://codeberg.org/uzu/strudel/pulls/855) +- 2023-12-08T09:41:10+01:00 patterns tab: import patterns + style by froos in: [#852](https://codeberg.org/uzu/strudel/pulls/852) +- 2023-12-07T20:42:00+01:00 Patterns tab + Refactor Panel by froos in: [#769](https://codeberg.org/uzu/strudel/pulls/769) +- 2023-12-07T10:25:16+01:00 CHANGES: pnpm 8.1.3 to 8.11.0 by Ghost in: [#850](https://codeberg.org/uzu/strudel/pulls/850) +- 2023-12-07T09:35:14+01:00 Fix edge case with rehype-urls and trailing slashes in image file paths by Ghost in: [#849](https://codeberg.org/uzu/strudel/pulls/849) +- 2023-12-06T23:02:31+01:00 Fix examples page, piano() and a few workshop imgs by Ghost in: [#848](https://codeberg.org/uzu/strudel/pulls/848) +- 2023-12-06T22:30:59+01:00 fix: swatch png src by froos in: [#846](https://codeberg.org/uzu/strudel/pulls/846) +- 2023-12-06T22:19:30+01:00 fix: missing hash for links starting with / by froos in: [#845](https://codeberg.org/uzu/strudel/pulls/845) +- 2023-12-06T22:05:05+01:00 improve slashing + base href behavior by froos in: [#842](https://codeberg.org/uzu/strudel/pulls/842) +- 2023-12-06T21:26:49+01:00 Add in fixes from my fork to slashocalypse branch by Ghost in: [#843](https://codeberg.org/uzu/strudel/pulls/843) +- 2023-12-05T18:43:58+01:00 Prevent 404 on Algolia crawls by Ghost in: [#838](https://codeberg.org/uzu/strudel/pulls/838) +- 2023-12-05T18:26:47+01:00 Multichannel audio by Ghost in: [#820](https://codeberg.org/uzu/strudel/pulls/820) +- 2023-12-05T12:19:58+01:00 ADDS: JetBrains IDE files and directories to .gitignore by Ghost in: [#840](https://codeberg.org/uzu/strudel/pulls/840) +- 2023-12-05T12:19:27+01:00 CHANGES: github action pnpm version from 7 to 8.3.1 by Ghost in: [#835](https://codeberg.org/uzu/strudel/pulls/835) +- 2023-12-05T12:19:17+01:00 CHANGES: pin pnpm to version 8.3.1 by Ghost in: [#834](https://codeberg.org/uzu/strudel/pulls/834) +- 2023-12-05T12:19:06+01:00 CHANGES: github action checkout v2 -> v4 by Ghost in: [#837](https://codeberg.org/uzu/strudel/pulls/837) +- 2023-12-05T11:15:14+01:00 FIXES: palindrome abc -> abccba by Ghost in: [#831](https://codeberg.org/uzu/strudel/pulls/831) +- 2023-12-02T09:43:50+01:00 Fix a typo by Ghost in: [#830](https://codeberg.org/uzu/strudel/pulls/830) + +## november 2023 + +- 2023-11-30T10:42:41+01:00 Add and style algolia search by Ghost in: [#827](https://codeberg.org/uzu/strudel/pulls/827) +- 2023-11-25T15:50:44+01:00 Hydra fixes and improvements by Ghost in: [#818](https://codeberg.org/uzu/strudel/pulls/818) +- 2023-11-24T10:07:17+01:00 add options param to initHydra by Ghost in: [#808](https://codeberg.org/uzu/strudel/pulls/808) +- 2023-11-24T09:57:02+01:00 Improve documentation for synonym functions by Ghost in: [#800](https://codeberg.org/uzu/strudel/pulls/800) +- 2023-11-24T09:00:54+01:00 New noise type: "crackle" by Ghost in: [#806](https://codeberg.org/uzu/strudel/pulls/806) +- 2023-11-18T21:18:16+01:00 Color hsl by froos in: [#815](https://codeberg.org/uzu/strudel/pulls/815) +- 2023-11-17T23:18:23+01:00 fix: multiple repls by froos in: [#813](https://codeberg.org/uzu/strudel/pulls/813) +- 2023-11-17T20:52:25+01:00 upstream changes by yaxu in: [#809](https://codeberg.org/uzu/strudel/pulls/809) +- 2023-11-17T16:04:10+01:00 Add doc for euclidLegatoRot, wordfall and slider by Ghost in: [#801](https://codeberg.org/uzu/strudel/pulls/801) +- 2023-11-17T14:42:58+01:00 add option to disable active line highlighting in Code Settings by Ghost in: [#804](https://codeberg.org/uzu/strudel/pulls/804) +- 2023-11-17T14:37:52+01:00 tidal style d1 ... d9 functions + more by froos in: [#805](https://codeberg.org/uzu/strudel/pulls/805) +- 2023-11-15T20:12:23+01:00 remove unwanted cm6 outline for strudelTheme by Ghost in: [#802](https://codeberg.org/uzu/strudel/pulls/802) +- 2023-11-13T23:30:15+01:00 Create phaser effect by Ghost in: [#798](https://codeberg.org/uzu/strudel/pulls/798) +- 2023-11-10T12:17:35+01:00 support multiple named serial connections, change default baudrate by yaxu in: [#551](https://codeberg.org/uzu/strudel/pulls/551) +- 2023-11-09T09:27:57+01:00 Fix for #1. Enables named instruments for csoundm. by gogins in: [#662](https://codeberg.org/uzu/strudel/pulls/662) +- 2023-11-09T09:26:45+01:00 Adding vibrato to Superdough sampler by Ghost in: [#706](https://codeberg.org/uzu/strudel/pulls/706) +- 2023-11-09T08:46:03+01:00 Document pianoroll by Ghost in: [#784](https://codeberg.org/uzu/strudel/pulls/784) +- 2023-11-09T08:45:29+01:00 Update first-effects.mdx by Ghost in: [#795](https://codeberg.org/uzu/strudel/pulls/795) +- 2023-11-09T08:44:43+01:00 Update pattern-effects.mdx by Ghost in: [#796](https://codeberg.org/uzu/strudel/pulls/796) +- 2023-11-09T08:43:50+01:00 Update recap.mdx by Ghost in: [#797](https://codeberg.org/uzu/strudel/pulls/797) +- 2023-11-07T11:53:49+01:00 Update first-sounds.mdx by Ghost in: [#794](https://codeberg.org/uzu/strudel/pulls/794) +- 2023-11-06T23:17:32+01:00 don't use anchor links for reference by froos in: [#791](https://codeberg.org/uzu/strudel/pulls/791) +- 2023-11-06T22:40:44+01:00 samples loading shortcuts: by froos in: [#788](https://codeberg.org/uzu/strudel/pulls/788) +- 2023-11-05T22:47:48+01:00 Fix scope pos + document by froos in: [#786](https://codeberg.org/uzu/strudel/pulls/786) +- 2023-11-05T22:10:25+01:00 Implement optional hover tooltip with function documentation by Ghost in: [#783](https://codeberg.org/uzu/strudel/pulls/783) +- 2023-11-05T16:46:06+01:00 Add function params in reference tab by Ghost in: [#785](https://codeberg.org/uzu/strudel/pulls/785) +- 2023-11-05T12:42:00+01:00 fix: style issues by froos in: [#781](https://codeberg.org/uzu/strudel/pulls/781) +- 2023-11-05T12:21:28+01:00 add xfade by froos in: [#780](https://codeberg.org/uzu/strudel/pulls/780) +- 2023-11-02T09:30:26+01:00 Update to Astro 3 by froos in: [#775](https://codeberg.org/uzu/strudel/pulls/775) +- 2023-11-02T08:57:14+01:00 add vscode bindings by Ghost in: [#773](https://codeberg.org/uzu/strudel/pulls/773) +- 2023-11-02T08:31:30+01:00 fix: share copy to clipboard + alert by froos in: [#774](https://codeberg.org/uzu/strudel/pulls/774) +- 2023-11-01T22:22:34+01:00 Fix chunk, add fastChunk and repeatCycles by yaxu in: [#712](https://codeberg.org/uzu/strudel/pulls/712) +- 2023-11-01T22:12:49+01:00 Document adsr function by Ghost in: [#767](https://codeberg.org/uzu/strudel/pulls/767) +- 2023-11-01T22:11:49+01:00 Add pick and squeeze functions by Ghost in: [#771](https://codeberg.org/uzu/strudel/pulls/771) +- 2023-11-01T22:04:00+01:00 Update vite pwa by froos in: [#772](https://codeberg.org/uzu/strudel/pulls/772) + +## october 2023 + +- 2023-10-28T23:55:07+02:00 replace strudel.tidalcycles.org with strudel.cc by froos in: [#768](https://codeberg.org/uzu/strudel/pulls/768) +- 2023-10-28T12:52:37+02:00 Document striate function by Ghost in: [#766](https://codeberg.org/uzu/strudel/pulls/766) +- 2023-10-27T23:06:20+02:00 fix zen mode logo overlap by froos in: [#760](https://codeberg.org/uzu/strudel/pulls/760) +- 2023-10-27T23:01:18+02:00 fix: scale offset by froos in: [#764](https://codeberg.org/uzu/strudel/pulls/764) +- 2023-10-27T21:59:35+02:00 Fix addivite synthesis phases by froos in: [#762](https://codeberg.org/uzu/strudel/pulls/762) +- 2023-10-26T16:28:16+02:00 Hydra integration by froos in: [#759](https://codeberg.org/uzu/strudel/pulls/759) +- 2023-10-26T14:14:27+02:00 add play function by froos in: [#758](https://codeberg.org/uzu/strudel/pulls/758) +- 2023-10-26T13:11:00+02:00 Add shabda shortcut by Ghost in: [#740](https://codeberg.org/uzu/strudel/pulls/740) +- 2023-10-26T13:07:23+02:00 mini notation: international alphabets support by Ghost in: [#751](https://codeberg.org/uzu/strudel/pulls/751) +- 2023-10-22T23:00:52+02:00 hopefully fix trainling slashes bug by froos in: [#753](https://codeberg.org/uzu/strudel/pulls/753) +- 2023-10-21T23:38:50+02:00 Fix krill build command in README by Ghost in: [#748](https://codeberg.org/uzu/strudel/pulls/748) +- 2023-10-21T00:20:50+02:00 completely revert config mess by froos in: [#745](https://codeberg.org/uzu/strudel/pulls/745) +- 2023-10-20T22:41:39+02:00 fix: try different trailing slash behavior by froos in: [#744](https://codeberg.org/uzu/strudel/pulls/744) +- 2023-10-20T22:34:03+02:00 fix: trailing slash confusion by froos in: [#743](https://codeberg.org/uzu/strudel/pulls/743) +- 2023-10-20T12:07:04+02:00 [Bug Fix] chooseWith: prevent pattern from stopping audio when selection is >= 1 or < 0 by Ghost in: [#741](https://codeberg.org/uzu/strudel/pulls/741) +- 2023-10-20T11:34:26+02:00 Recipes by froos in: [#742](https://codeberg.org/uzu/strudel/pulls/742) +- 2023-10-13T12:57:24+02:00 vite-vanilla-repl readme fix by froos in: [#737](https://codeberg.org/uzu/strudel/pulls/737) +- 2023-10-10T00:17:59+02:00 Add support for using samples as impulse response buffers for the reverb by Ghost in: [#717](https://codeberg.org/uzu/strudel/pulls/717) +- 2023-10-09T21:43:42+02:00 fix: reverb sampleRate by froos in: [#732](https://codeberg.org/uzu/strudel/pulls/732) +- 2023-10-09T21:34:33+02:00 fix: reverb roomsize not required by froos in: [#731](https://codeberg.org/uzu/strudel/pulls/731) +- 2023-10-08T13:54:52+02:00 Compressor by froos in: [#729](https://codeberg.org/uzu/strudel/pulls/729) +- 2023-10-08T13:25:57+02:00 fix: hashes in urls by froos in: [#728](https://codeberg.org/uzu/strudel/pulls/728) +- 2023-10-07T15:48:06+02:00 consume n with scale by froos in: [#727](https://codeberg.org/uzu/strudel/pulls/727) +- 2023-10-07T00:27:21+02:00 fix: reverb regenerate loophole by froos in: [#726](https://codeberg.org/uzu/strudel/pulls/726) +- 2023-10-05T00:04:17+02:00 Better convolution reverb by generating impulse responses by Ghost in: [#718](https://codeberg.org/uzu/strudel/pulls/718) +- 2023-10-04T10:31:53+02:00 Slider afterthoughts by froos in: [#723](https://codeberg.org/uzu/strudel/pulls/723) +- 2023-10-03T16:39:06+02:00 Add 'white', 'pink' and 'brown' oscillators + refactor synth by Ghost in: [#713](https://codeberg.org/uzu/strudel/pulls/713) +- 2023-10-01T14:20:50+02:00 support mininotation '..' range operator, fixes #715 by yaxu in: [#716](https://codeberg.org/uzu/strudel/pulls/716) +- 2023-10-01T14:15:09+02:00 widgets by froos in: [#714](https://codeberg.org/uzu/strudel/pulls/714) + +## september 2023 + +- 2023-09-28T11:03:09+02:00 Midi in by froos in: [#699](https://codeberg.org/uzu/strudel/pulls/699) +- 2023-09-27T22:53:49+02:00 add midi clock support by froos in: [#710](https://codeberg.org/uzu/strudel/pulls/710) +- 2023-09-25T23:36:10+02:00 Update bournemouth.mdx by Ghost in: [#708](https://codeberg.org/uzu/strudel/pulls/708) +- 2023-09-25T23:06:30+02:00 add dough function for raw dsp by froos in: [#707](https://codeberg.org/uzu/strudel/pulls/707) +- 2023-09-17T15:53:27+02:00 Update tauri.yml workflow file by Ghost in: [#705](https://codeberg.org/uzu/strudel/pulls/705) +- 2023-09-17T11:05:06+02:00 Adding vibrato to base oscillators by Ghost in: [#693](https://codeberg.org/uzu/strudel/pulls/693) +- 2023-09-17T08:27:36+02:00 Adding loop points and thus wavetable synthesis by Ghost in: [#698](https://codeberg.org/uzu/strudel/pulls/698) +- 2023-09-16T02:03:49+02:00 Adding filter envelopes and filter order selection by Ghost in: [#692](https://codeberg.org/uzu/strudel/pulls/692) +- 2023-09-09T11:19:58+02:00 Add logging from tauri by Ghost in: [#697](https://codeberg.org/uzu/strudel/pulls/697) +- 2023-09-05T00:33:54+02:00 Direct OSC Support in Tauri by Ghost in: [#694](https://codeberg.org/uzu/strudel/pulls/694) +- 2023-09-03T22:19:22+02:00 fix MIDI CC messages by Ghost in: [#690](https://codeberg.org/uzu/strudel/pulls/690) +- 2023-09-03T10:22:15+02:00 add sleep timer + improve message iterating by Ghost in: [#688](https://codeberg.org/uzu/strudel/pulls/688) + +## august 2023 + +- 2023-08-31T13:00:31+02:00 ZZFX Synth support by Ghost in: [#684](https://codeberg.org/uzu/strudel/pulls/684) +- 2023-08-31T12:36:37+02:00 Wave Selection and Global Envelope on the FM Synth Modulator by Ghost in: [#683](https://codeberg.org/uzu/strudel/pulls/683) +- 2023-08-31T05:52:17+02:00 Create Midi Integration for Tauri Desktop app by Ghost in: [#685](https://codeberg.org/uzu/strudel/pulls/685) +- 2023-08-31T05:32:16+02:00 teletext theme + fonts by froos in: [#681](https://codeberg.org/uzu/strudel/pulls/681) +- 2023-08-31T05:20:55+02:00 control osc partial count with n by froos in: [#674](https://codeberg.org/uzu/strudel/pulls/674) +- 2023-08-27T22:18:06+02:00 add emoji support by froos in: [#680](https://codeberg.org/uzu/strudel/pulls/680) +- 2023-08-27T16:12:32+02:00 Pianoroll improvements by froos in: [#679](https://codeberg.org/uzu/strudel/pulls/679) +- 2023-08-26T21:22:17+02:00 Scope by froos in: [#677](https://codeberg.org/uzu/strudel/pulls/677) +- 2023-08-23T21:50:50+02:00 Midi time fixes by Ghost in: [#668](https://codeberg.org/uzu/strudel/pulls/668) +- 2023-08-20T23:19:08+02:00 basic fm by froos in: [#669](https://codeberg.org/uzu/strudel/pulls/669) +- 2023-08-18T23:56:20+02:00 togglable panel position by froos in: [#667](https://codeberg.org/uzu/strudel/pulls/667) +- 2023-08-18T15:59:20+02:00 fix osc bundle timestamp glitches caused by drifting clock by Ghost in: [#666](https://codeberg.org/uzu/strudel/pulls/666) +- 2023-08-17T11:36:07+02:00 superdough: encapsulates web audio output by froos in: [#664](https://codeberg.org/uzu/strudel/pulls/664) +- 2023-08-11T00:06:21+02:00 fix: always run previous trigger by froos in: [#660](https://codeberg.org/uzu/strudel/pulls/660) +- 2023-08-10T23:54:58+02:00 fix: welcome message for latestCode by froos in: [#659](https://codeberg.org/uzu/strudel/pulls/659) +- 2023-08-07T07:45:26+02:00 [Bug Fix] Midi: Don't treat note 0 as false by Ghost in: [#657](https://codeberg.org/uzu/strudel/pulls/657) + +## july 2023 + +- 2023-07-29T09:06:18+02:00 [Bug Fix] Account for numeral notation when converting to midi by Ghost in: [#656](https://codeberg.org/uzu/strudel/pulls/656) +- 2023-07-23T22:18:49+02:00 ireal voicings by froos in: [#653](https://codeberg.org/uzu/strudel/pulls/653) +- 2023-07-22T09:30:21+02:00 Understand pitch by froos in: [#652](https://codeberg.org/uzu/strudel/pulls/652) +- 2023-07-17T23:42:59+02:00 update vitest by froos in: [#651](https://codeberg.org/uzu/strudel/pulls/651) +- 2023-07-17T23:34:33+02:00 stateless voicings + tonleiter lib by froos in: [#647](https://codeberg.org/uzu/strudel/pulls/647) +- 2023-07-17T17:57:17+02:00 FIXES: TODO in rotateChroma by Ghost in: [#650](https://codeberg.org/uzu/strudel/pulls/650) +- 2023-07-10T19:07:45+02:00 slice: list mode by froos in: [#645](https://codeberg.org/uzu/strudel/pulls/645) +- 2023-07-04T23:50:30+02:00 Delete old packages by froos in: [#639](https://codeberg.org/uzu/strudel/pulls/639) +- 2023-07-04T23:38:42+02:00 Adaptive Highlighting by froos in: [#634](https://codeberg.org/uzu/strudel/pulls/634) +- 2023-07-04T21:55:57+02:00 More work on highlight IDs by Ghost in: [#636](https://codeberg.org/uzu/strudel/pulls/636) +- 2023-07-04T18:44:48+02:00 snapshot tests: sort haps by part by froos in: [#637](https://codeberg.org/uzu/strudel/pulls/637) + +## juny 2023 + +- 2023-06-30T22:47:40+02:00 fix: update canvas size on window resize by froos in: [#631](https://codeberg.org/uzu/strudel/pulls/631) +- 2023-06-30T22:45:41+02:00 fix: out of range error by froos in: [#630](https://codeberg.org/uzu/strudel/pulls/630) +- 2023-06-30T08:14:40+02:00 fix: midi clock drift by froos in: [#627](https://codeberg.org/uzu/strudel/pulls/627) +- 2023-06-29T22:02:28+02:00 desktop: play samples from disk by froos in: [#621](https://codeberg.org/uzu/strudel/pulls/621) +- 2023-06-29T21:59:43+02:00 cps dependent functions by froos in: [#620](https://codeberg.org/uzu/strudel/pulls/620) +- 2023-06-26T22:45:04+02:00 Fix typo on packages.mdx by Ghost in: [#520](https://codeberg.org/uzu/strudel/pulls/520) +- 2023-06-26T22:35:02+02:00 patterning ui settings by froos in: [#606](https://codeberg.org/uzu/strudel/pulls/606) +- 2023-06-26T22:32:46+02:00 add spiral viz by froos in: [#614](https://codeberg.org/uzu/strudel/pulls/614) +- 2023-06-26T22:17:46+02:00 tauri desktop app by Ghost in: [#613](https://codeberg.org/uzu/strudel/pulls/613) +- 2023-06-23T09:59:43+02:00 fix: doc links by froos in: [#612](https://codeberg.org/uzu/strudel/pulls/612) +- 2023-06-23T09:55:18+02:00 clip now works like legato in tidal by froos in: [#598](https://codeberg.org/uzu/strudel/pulls/598) +- 2023-06-18T10:36:19+02:00 fix: flatten scale lists by froos in: [#605](https://codeberg.org/uzu/strudel/pulls/605) +- 2023-06-18T10:36:17+02:00 tonal fixes by froos in: [#607](https://codeberg.org/uzu/strudel/pulls/607) +- 2023-06-15T12:51:40+02:00 editor: enable line wrapping by Ghost in: [#581](https://codeberg.org/uzu/strudel/pulls/581) +- 2023-06-15T10:22:46+02:00 add ratio function by froos in: [#602](https://codeberg.org/uzu/strudel/pulls/602) +- 2023-06-12T23:24:29+02:00 enable auto-completion by Ghost in: [#588](https://codeberg.org/uzu/strudel/pulls/588) +- 2023-06-11T22:04:17+02:00 improve cursor by froos in: [#597](https://codeberg.org/uzu/strudel/pulls/597) +- 2023-06-11T20:00:45+02:00 Solmization added by Ghost in: [#570](https://codeberg.org/uzu/strudel/pulls/570) +- 2023-06-11T19:45:00+02:00 fix: division by zero by froos in: [#591](https://codeberg.org/uzu/strudel/pulls/591) +- 2023-06-11T19:44:43+02:00 fix: allow f for flat notes like tidal by froos in: [#593](https://codeberg.org/uzu/strudel/pulls/593) +- 2023-06-11T19:44:41+02:00 Fix option dot by froos in: [#596](https://codeberg.org/uzu/strudel/pulls/596) +- 2023-06-09T21:02:12+02:00 New Workshop by froos in: [#587](https://codeberg.org/uzu/strudel/pulls/587) +- 2023-06-09T17:31:58+02:00 Music metadata by Ghost in: [#580](https://codeberg.org/uzu/strudel/pulls/580) +- 2023-06-08T09:33:34+02:00 learn/tonal: fix typo in "scaleTran[s]pose" by Ghost in: [#585](https://codeberg.org/uzu/strudel/pulls/585) +- 2023-06-07T20:19:11+02:00 repl: add option to display line numbers by Ghost in: [#582](https://codeberg.org/uzu/strudel/pulls/582) +- 2023-06-05T21:35:49+02:00 Vanilla JS Refactoring by froos in: [#563](https://codeberg.org/uzu/strudel/pulls/563) + +## may 2023 + +- 2023-05-05T08:34:37+02:00 Patchday by froos in: [#559](https://codeberg.org/uzu/strudel/pulls/559) +- 2023-05-02T21:48:33+02:00 add basic triads and guidetone voicings by froos in: [#557](https://codeberg.org/uzu/strudel/pulls/557) + +## april 2023 + +- 2023-04-28T12:46:56+02:00 fix: make soundfonts import dynamic by froos in: [#556](https://codeberg.org/uzu/strudel/pulls/556) +- 2023-04-22T15:43:22+02:00 fix: colorable highlighting by froos in: [#553](https://codeberg.org/uzu/strudel/pulls/553) +- 2023-04-06T00:11:02+02:00 fix: load soundfonts in prebake by froos in: [#550](https://codeberg.org/uzu/strudel/pulls/550) + +## march 2023 + +- 2023-03-29T22:23:07+02:00 fix: reset time on stop by froos in: [#548](https://codeberg.org/uzu/strudel/pulls/548) +- 2023-03-29T22:16:38+02:00 fix: allow whitespace at the end of a mini pattern by froos in: [#547](https://codeberg.org/uzu/strudel/pulls/547) +- 2023-03-24T22:10:56+01:00 add firacode font by froos in: [#544](https://codeberg.org/uzu/strudel/pulls/544) +- 2023-03-24T12:54:20+01:00 feat: add loader bar to animate loading state by froos in: [#542](https://codeberg.org/uzu/strudel/pulls/542) +- 2023-03-23T22:39:27+01:00 do not reset cps before eval #517 by froos in: [#539](https://codeberg.org/uzu/strudel/pulls/539) +- 2023-03-23T22:27:31+01:00 improve initial loading + wait before eval by froos in: [#538](https://codeberg.org/uzu/strudel/pulls/538) +- 2023-03-23T21:40:20+01:00 fix period key for dvorak + remove duplicated code by froos in: [#537](https://codeberg.org/uzu/strudel/pulls/537) +- 2023-03-23T11:44:56+01:00 Update lerna by froos in: [#535](https://codeberg.org/uzu/strudel/pulls/535) +- 2023-03-23T10:21:55+01:00 feat: add freq support to gm soundfonts by froos in: [#534](https://codeberg.org/uzu/strudel/pulls/534) +- 2023-03-23T10:18:58+01:00 Maintain random seed state in parser, not globally by Ghost in: [#531](https://codeberg.org/uzu/strudel/pulls/531) +- 2023-03-21T22:35:18+01:00 FIXES: alias pm for polymeter by Ghost in: [#527](https://codeberg.org/uzu/strudel/pulls/527) +- 2023-03-21T22:29:07+01:00 fix(footer): fix link to tidalcycles by julienbouquillon in: [#529](https://codeberg.org/uzu/strudel/pulls/529) +- 2023-03-18T20:25:21+01:00 Update intro.mdx by Ghost in: [#525](https://codeberg.org/uzu/strudel/pulls/525) +- 2023-03-18T20:24:38+01:00 Update samples.mdx by Ghost in: [#524](https://codeberg.org/uzu/strudel/pulls/524) +- 2023-03-17T09:03:11+01:00 fix: envelopes in chrome by froos in: [#521](https://codeberg.org/uzu/strudel/pulls/521) +- 2023-03-16T16:13:30+01:00 registerSound API + improved sounds tab + regroup soundfonts by froos in: [#516](https://codeberg.org/uzu/strudel/pulls/516) +- 2023-03-14T21:54:53+01:00 add 2 illegible fonts by froos in: [#518](https://codeberg.org/uzu/strudel/pulls/518) +- 2023-03-06T22:55:00+01:00 Update README.md by Ghost in: [#474](https://codeberg.org/uzu/strudel/pulls/474) +- 2023-03-05T14:52:30+01:00 add arrange function by froos in: [#508](https://codeberg.org/uzu/strudel/pulls/508) +- 2023-03-05T14:29:08+01:00 update react to 18 by froos in: [#514](https://codeberg.org/uzu/strudel/pulls/514) +- 2023-03-04T19:06:18+01:00 Support list syntax in mininotation by yaxu in: [#512](https://codeberg.org/uzu/strudel/pulls/512) +- 2023-03-03T12:40:23+01:00 can now use : as a replacement for space in scales by froos in: [#502](https://codeberg.org/uzu/strudel/pulls/502) +- 2023-03-02T15:44:41+01:00 Reinstate slice and splice by yaxu in: [#500](https://codeberg.org/uzu/strudel/pulls/500) +- 2023-03-02T14:49:30+01:00 fix: nano-repl highlighting by froos in: [#501](https://codeberg.org/uzu/strudel/pulls/501) +- 2023-03-02T14:17:13+01:00 Add control aliases by yaxu in: [#497](https://codeberg.org/uzu/strudel/pulls/497) +- 2023-03-01T09:27:27+01:00 implement cps in scheduler by froos in: [#493](https://codeberg.org/uzu/strudel/pulls/493) + +## february 2023 + +- 2023-02-28T23:06:29+01:00 react style fixes by froos in: [#491](https://codeberg.org/uzu/strudel/pulls/491) +- 2023-02-28T22:57:20+01:00 refactor react package by froos in: [#490](https://codeberg.org/uzu/strudel/pulls/490) +- 2023-02-27T23:47:34+01:00 add algolia creds + optimize sidebar for crawling by froos in: [#488](https://codeberg.org/uzu/strudel/pulls/488) +- 2023-02-27T19:28:10+01:00 fix app height by froos in: [#485](https://codeberg.org/uzu/strudel/pulls/485) +- 2023-02-27T16:04:21+01:00 Revert "Another attempt at composable functions - WIP (#390)" by froos in: [#484](https://codeberg.org/uzu/strudel/pulls/484) +- 2023-02-27T15:45:20+01:00 Update mini-notation.mdx by yaxu in: [#365](https://codeberg.org/uzu/strudel/pulls/365) +- 2023-02-27T12:52:09+01:00 docs: packages + offline by froos in: [#482](https://codeberg.org/uzu/strudel/pulls/482) +- 2023-02-25T14:26:49+01:00 Fix array args by froos in: [#480](https://codeberg.org/uzu/strudel/pulls/480) +- 2023-02-25T12:33:22+01:00 midi cc support by froos in: [#478](https://codeberg.org/uzu/strudel/pulls/478) +- 2023-02-23T00:11:05+01:00 fix: hash links by froos in: [#473](https://codeberg.org/uzu/strudel/pulls/473) +- 2023-02-22T22:54:39+01:00 settings tab with vim / emacs modes + additional themes and fonts by froos in: [#467](https://codeberg.org/uzu/strudel/pulls/467) +- 2023-02-22T22:53:22+01:00 Update input-output.mdx by Ghost in: [#471](https://codeberg.org/uzu/strudel/pulls/471) +- 2023-02-22T22:52:50+01:00 FIXES: freqs instead of pitches by Ghost in: [#464](https://codeberg.org/uzu/strudel/pulls/464) +- 2023-02-22T20:01:00+01:00 fix: osc should not return a promise by froos in: [#472](https://codeberg.org/uzu/strudel/pulls/472) +- 2023-02-22T12:51:31+01:00 slice and splice by yaxu in: [#466](https://codeberg.org/uzu/strudel/pulls/466) +- 2023-02-18T01:00:19+01:00 weave and weaveWith by yaxu in: [#465](https://codeberg.org/uzu/strudel/pulls/465) +- 2023-02-17T00:15:21+01:00 Composable functions by yaxu in: [#390](https://codeberg.org/uzu/strudel/pulls/390) +- 2023-02-16T20:38:45+01:00 FIXES: Warning about jsxBracketSameLine deprecation by Ghost in: [#461](https://codeberg.org/uzu/strudel/pulls/461) +- 2023-02-14T22:11:02+01:00 Update synths.mdx by Ghost in: [#438](https://codeberg.org/uzu/strudel/pulls/438) +- 2023-02-14T19:54:42+01:00 Update mini-notation.mdx by Ghost in: [#437](https://codeberg.org/uzu/strudel/pulls/437) +- 2023-02-14T19:54:25+01:00 Update code.mdx by Ghost in: [#436](https://codeberg.org/uzu/strudel/pulls/436) +- 2023-02-13T00:43:29+01:00 Fix anchors by froos in: [#433](https://codeberg.org/uzu/strudel/pulls/433) +- 2023-02-11T21:02:11+01:00 autocomplete preparations by froos in: [#427](https://codeberg.org/uzu/strudel/pulls/427) +- 2023-02-10T23:14:48+01:00 Themes by froos in: [#431](https://codeberg.org/uzu/strudel/pulls/431) +- 2023-02-09T19:22:56+01:00 minirepl: add keyboard shortcuts by froos in: [#429](https://codeberg.org/uzu/strudel/pulls/429) +- 2023-02-09T08:56:42+01:00 add cdn.freesound to cache list by froos in: [#425](https://codeberg.org/uzu/strudel/pulls/425) +- 2023-02-08T20:36:00+01:00 add more offline caching by froos in: [#421](https://codeberg.org/uzu/strudel/pulls/421) +- 2023-02-07T22:08:01+01:00 add caching strategy for missing file types + cache all samples loaded from github by froos in: [#419](https://codeberg.org/uzu/strudel/pulls/419) +- 2023-02-06T23:29:57+01:00 PWA with offline support by froos in: [#417](https://codeberg.org/uzu/strudel/pulls/417) +- 2023-02-05T17:27:32+01:00 improve samples doc by froos in: [#411](https://codeberg.org/uzu/strudel/pulls/411) +- 2023-02-05T17:27:10+01:00 google gtfo by froos in: [#413](https://codeberg.org/uzu/strudel/pulls/413) +- 2023-02-05T16:27:59+01:00 improve effects doc by froos in: [#409](https://codeberg.org/uzu/strudel/pulls/409) +- 2023-02-05T14:56:03+01:00 Update effects.mdx by Ghost in: [#410](https://codeberg.org/uzu/strudel/pulls/410) +- 2023-02-03T19:54:47+01:00 add shabda doc by froos in: [#407](https://codeberg.org/uzu/strudel/pulls/407) +- 2023-02-02T21:45:56+01:00 fix: share url on subpath by froos in: [#405](https://codeberg.org/uzu/strudel/pulls/405) +- 2023-02-02T21:35:45+01:00 update csound + fix sound output by froos in: [#404](https://codeberg.org/uzu/strudel/pulls/404) +- 2023-02-02T19:53:36+01:00 pin @csound/browser to 6.18.3 + bump by froos in: [#403](https://codeberg.org/uzu/strudel/pulls/403) +- 2023-02-01T22:47:27+01:00 release webaudio by froos in: [#400](https://codeberg.org/uzu/strudel/pulls/400) +- 2023-02-01T22:45:16+01:00 can now await initAudio + initAudioOnFirstClick by froos in: [#399](https://codeberg.org/uzu/strudel/pulls/399) +- 2023-02-01T22:32:18+01:00 fix: minirepl styles by froos in: [#398](https://codeberg.org/uzu/strudel/pulls/398) +- 2023-02-01T22:17:19+01:00 proper builds + use pnpm workspaces by froos in: [#396](https://codeberg.org/uzu/strudel/pulls/396) +- 2023-02-01T16:49:55+01:00 add pattern methods hurry, press and pressBy by yaxu in: [#397](https://codeberg.org/uzu/strudel/pulls/397) + +## january 2023 + +- 2023-01-27T14:40:40+01:00 Add tidal-drum-patterns to examples by Ghost in: [#379](https://codeberg.org/uzu/strudel/pulls/379) +- 2023-01-27T14:39:56+01:00 add ribbon + test + docs by froos in: [#388](https://codeberg.org/uzu/strudel/pulls/388) +- 2023-01-27T12:10:37+01:00 Notes are not essential :) by yaxu in: [#393](https://codeberg.org/uzu/strudel/pulls/393) +- 2023-01-21T17:18:13+01:00 document csound by froos in: [#391](https://codeberg.org/uzu/strudel/pulls/391) +- 2023-01-19T12:04:51+01:00 Rename a to angle by froos in: [#387](https://codeberg.org/uzu/strudel/pulls/387) +- 2023-01-19T11:49:39+01:00 add run + test + docs by froos in: [#386](https://codeberg.org/uzu/strudel/pulls/386) +- 2023-01-19T11:32:56+01:00 docs: use note instead of n to mitigate confusion by froos in: [#385](https://codeberg.org/uzu/strudel/pulls/385) +- 2023-01-18T17:10:09+01:00 update my-patterns instructions by froos in: [#384](https://codeberg.org/uzu/strudel/pulls/384) +- 2023-01-15T23:19:43+01:00 Draw fixes by froos in: [#377](https://codeberg.org/uzu/strudel/pulls/377) +- 2023-01-14T11:07:08+01:00 improve new draw logic by froos in: [#372](https://codeberg.org/uzu/strudel/pulls/372) +- 2023-01-12T14:40:59+01:00 document more functions + change arp join by froos in: [#369](https://codeberg.org/uzu/strudel/pulls/369) +- 2023-01-10T00:03:47+01:00 add https to url by Ghost in: [#364](https://codeberg.org/uzu/strudel/pulls/364) +- 2023-01-09T23:37:34+01:00 doc structuring by froos in: [#360](https://codeberg.org/uzu/strudel/pulls/360) +- 2023-01-09T23:23:28+01:00 Support for multiple mininotation operators by yaxu in: [#350](https://codeberg.org/uzu/strudel/pulls/350) +- 2023-01-09T00:40:15+01:00 Fix .out(), renaming webaudio's out() to webaudio() by yaxu in: [#361](https://codeberg.org/uzu/strudel/pulls/361) +- 2023-01-06T22:02:31+01:00 docs: tidal comparison + add global fx + add missing sampler fx by froos in: [#356](https://codeberg.org/uzu/strudel/pulls/356) +- 2023-01-06T12:31:32+01:00 Fix Bjorklund by yaxu in: [#343](https://codeberg.org/uzu/strudel/pulls/343) +- 2023-01-04T20:29:35+01:00 Fix prebake base path by froos in: [#345](https://codeberg.org/uzu/strudel/pulls/345) +- 2023-01-04T19:55:49+01:00 fixes #346 by froos in: [#347](https://codeberg.org/uzu/strudel/pulls/347) +- 2023-01-02T21:28:07+01:00 Patternify euclid, fast, slow and polymeter step parameters in mininotation by yaxu in: [#341](https://codeberg.org/uzu/strudel/pulls/341) +- 2023-01-02T00:30:16+01:00 more animate functions + mini repl fix by froos in: [#340](https://codeberg.org/uzu/strudel/pulls/340) +- 2023-01-01T12:34:11+01:00 move /my-patterns to /swatch by yaxu in: [#338](https://codeberg.org/uzu/strudel/pulls/338) +- 2023-01-01T12:22:55+01:00 animation options by froos in: [#337](https://codeberg.org/uzu/strudel/pulls/337) + +## december 2022 + +- 2022-12-31T22:42:49+01:00 Tidy parser, implement polymeters by yaxu in: [#336](https://codeberg.org/uzu/strudel/pulls/336) +- 2022-12-31T16:51:53+01:00 animate mvp by froos in: [#335](https://codeberg.org/uzu/strudel/pulls/335) +- 2022-12-30T20:16:10+01:00 testing + docs docs by froos in: [#334](https://codeberg.org/uzu/strudel/pulls/334) +- 2022-12-29T21:11:16+01:00 Embed mode improvements by froos in: [#333](https://codeberg.org/uzu/strudel/pulls/333) +- 2022-12-29T14:06:20+01:00 fix: can now multiply floats in mini notation by froos in: [#332](https://codeberg.org/uzu/strudel/pulls/332) +- 2022-12-29T13:50:07+01:00 improve displaying 's' in pianoroll by froos in: [#331](https://codeberg.org/uzu/strudel/pulls/331) +- 2022-12-28T17:35:35+01:00 my-patterns: fix paths + update readme by froos in: [#330](https://codeberg.org/uzu/strudel/pulls/330) +- 2022-12-28T17:11:40+01:00 my-patterns build + deploy by froos in: [#329](https://codeberg.org/uzu/strudel/pulls/329) +- 2022-12-28T15:43:31+01:00 add my-patterns by froos in: [#328](https://codeberg.org/uzu/strudel/pulls/328) +- 2022-12-28T14:47:14+01:00 add examples route by froos in: [#327](https://codeberg.org/uzu/strudel/pulls/327) +- 2022-12-28T14:44:31+01:00 fix: workaround Object.assign globalThis by froos in: [#326](https://codeberg.org/uzu/strudel/pulls/326) +- 2022-12-26T23:29:47+01:00 mini repl improvements by froos in: [#324](https://codeberg.org/uzu/strudel/pulls/324) +- 2022-12-26T23:00:34+01:00 support notes without octave by froos in: [#323](https://codeberg.org/uzu/strudel/pulls/323) +- 2022-12-26T12:37:36+01:00 tutorial updates by Ghost in: [#320](https://codeberg.org/uzu/strudel/pulls/320) +- 2022-12-23T18:26:30+01:00 Reference tab sort by froos in: [#318](https://codeberg.org/uzu/strudel/pulls/318) +- 2022-12-23T00:49:56+01:00 Astro build by froos in: [#315](https://codeberg.org/uzu/strudel/pulls/315) +- 2022-12-19T21:02:37+01:00 object support for .scale by froos in: [#307](https://codeberg.org/uzu/strudel/pulls/307) +- 2022-12-19T21:02:22+01:00 Jsdoc component by froos in: [#312](https://codeberg.org/uzu/strudel/pulls/312) +- 2022-12-19T20:31:46+01:00 fix: copy share link to clipboard was broken for some browers by froos in: [#311](https://codeberg.org/uzu/strudel/pulls/311) +- 2022-12-19T12:11:24+01:00 ICLC2023 paper WIP by yaxu in: [#306](https://codeberg.org/uzu/strudel/pulls/306) +- 2022-12-15T21:23:22+01:00 support freq in pianoroll by froos in: [#308](https://codeberg.org/uzu/strudel/pulls/308) +- 2022-12-13T22:07:27+01:00 Updated csoundm to use the register facility . by gogins in: [#303](https://codeberg.org/uzu/strudel/pulls/303) +- 2022-12-13T21:57:03+01:00 add lint + prettier check before test by froos in: [#305](https://codeberg.org/uzu/strudel/pulls/305) +- 2022-12-13T21:21:44+01:00 add freq support to sampler by froos in: [#301](https://codeberg.org/uzu/strudel/pulls/301) +- 2022-12-12T00:48:41+01:00 fix whitespace trimming by froos in: [#300](https://codeberg.org/uzu/strudel/pulls/300) +- 2022-12-12T00:21:53+01:00 .defragmentHaps() for merging touching haps that share a whole and value by yaxu in: [#299](https://codeberg.org/uzu/strudel/pulls/299) +- 2022-12-11T23:17:49+01:00 remove whitespace from highlighted region by froos in: [#298](https://codeberg.org/uzu/strudel/pulls/298) +- 2022-12-11T22:03:26+01:00 update vitest by froos in: [#297](https://codeberg.org/uzu/strudel/pulls/297) +- 2022-12-11T21:44:16+01:00 can now add bare numbers to numeral object props by froos in: [#287](https://codeberg.org/uzu/strudel/pulls/287) +- 2022-12-11T21:27:58+01:00 Move stuff to new register function by froos in: [#295](https://codeberg.org/uzu/strudel/pulls/295) +- 2022-12-11T20:07:12+01:00 add prettier task by froos in: [#296](https://codeberg.org/uzu/strudel/pulls/296) +- 2022-12-10T15:39:04+01:00 Reorganise pattern.mjs with a 'toplevel first' regime by yaxu in: [#286](https://codeberg.org/uzu/strudel/pulls/286) +- 2022-12-10T11:56:16+01:00 Fancy hap show, include part in snapshots by yaxu in: [#291](https://codeberg.org/uzu/strudel/pulls/291) +- 2022-12-07T20:07:55+01:00 Switch 'operators' from .whatHow to .what.how by yaxu in: [#285](https://codeberg.org/uzu/strudel/pulls/285) +- 2022-12-04T12:51:01+01:00 implement collect + arp function by froos in: [#281](https://codeberg.org/uzu/strudel/pulls/281) +- 2022-12-02T13:38:27+01:00 do not recompile orc by froos in: [#278](https://codeberg.org/uzu/strudel/pulls/278) +- 2022-12-02T12:49:26+01:00 add basic csound output by froos in: [#275](https://codeberg.org/uzu/strudel/pulls/275) +- 2022-12-02T12:18:41+01:00 add licenses / credits to all tunes + remove some by froos in: [#277](https://codeberg.org/uzu/strudel/pulls/277) +- 2022-12-02T11:45:02+01:00 Support sending CRC16 bytes with serial messages by yaxu in: [#276](https://codeberg.org/uzu/strudel/pulls/276) + +## november 2022 + +- 2022-11-29T23:41:39+01:00 release version bumps by froos in: [#273](https://codeberg.org/uzu/strudel/pulls/273) +- 2022-11-29T23:34:50+01:00 add eslint by froos in: [#271](https://codeberg.org/uzu/strudel/pulls/271) +- 2022-11-29T23:34:26+01:00 tonal update with fixed memory leak by froos in: [#272](https://codeberg.org/uzu/strudel/pulls/272) +- 2022-11-22T09:51:26+01:00 Tidying up core by yaxu in: [#256](https://codeberg.org/uzu/strudel/pulls/256) +- 2022-11-21T22:15:48+01:00 fix performance bottleneck by froos in: [#266](https://codeberg.org/uzu/strudel/pulls/266) +- 2022-11-17T11:08:38+01:00 fix tutorial bugs by froos in: [#263](https://codeberg.org/uzu/strudel/pulls/263) +- 2022-11-16T13:15:28+01:00 Binaries by froos in: [#254](https://codeberg.org/uzu/strudel/pulls/254) +- 2022-11-13T20:20:32+01:00 Repl refactoring by froos in: [#255](https://codeberg.org/uzu/strudel/pulls/255) +- 2022-11-08T23:35:11+01:00 Webaudio build by froos in: [#250](https://codeberg.org/uzu/strudel/pulls/250) +- 2022-11-08T22:43:32+01:00 new transpiler based on acorn by froos in: [#249](https://codeberg.org/uzu/strudel/pulls/249) +- 2022-11-06T19:03:19+01:00 General purpose scheduler by froos in: [#248](https://codeberg.org/uzu/strudel/pulls/248) +- 2022-11-06T11:05:23+01:00 snapshot tests on shared snippets by froos in: [#243](https://codeberg.org/uzu/strudel/pulls/243) +- 2022-11-06T11:03:42+01:00 Some tunes by froos in: [#247](https://codeberg.org/uzu/strudel/pulls/247) +- 2022-11-06T00:37:11+01:00 patchday by froos in: [#246](https://codeberg.org/uzu/strudel/pulls/246) +- 2022-11-04T20:28:23+01:00 Readme + TLC by froos in: [#244](https://codeberg.org/uzu/strudel/pulls/244) +- 2022-11-03T15:16:04+01:00 in source example tests by froos in: [#242](https://codeberg.org/uzu/strudel/pulls/242) +- 2022-11-02T23:16:43+01:00 feat: support github: links by froos in: [#240](https://codeberg.org/uzu/strudel/pulls/240) +- 2022-11-02T21:26:44+01:00 Load samples from url by froos in: [#239](https://codeberg.org/uzu/strudel/pulls/239) +- 2022-11-02T11:42:32+01:00 Object arithmetic by froos in: [#238](https://codeberg.org/uzu/strudel/pulls/238) +- 2022-11-01T00:36:14+01:00 Tidal drum machines by froos in: [#237](https://codeberg.org/uzu/strudel/pulls/237) +- 2022-10-31T21:39:23+01:00 fx on stereo speakers by froos in: [#236](https://codeberg.org/uzu/strudel/pulls/236) + +## october 2022 + +- 2022-10-30T19:10:33+01:00 add vcsl sample library by froos in: [#235](https://codeberg.org/uzu/strudel/pulls/235) +- 2022-10-30T00:23:10+02:00 Fix zero length queries WIP by yaxu in: [#234](https://codeberg.org/uzu/strudel/pulls/234) +- 2022-10-29T17:54:05+02:00 Out by default by froos in: [#232](https://codeberg.org/uzu/strudel/pulls/232) +- 2022-10-26T23:53:49+02:00 Patternify range by yaxu in: [#231](https://codeberg.org/uzu/strudel/pulls/231) +- 2022-10-26T21:42:12+02:00 Just another docs branch by froos in: [#228](https://codeberg.org/uzu/strudel/pulls/228) +- 2022-10-26T21:41:12+02:00 Refactor tunes away from tone by froos in: [#230](https://codeberg.org/uzu/strudel/pulls/230) +- 2022-10-20T09:26:28+02:00 Core util tests by Ghost in: [#226](https://codeberg.org/uzu/strudel/pulls/226) +- 2022-10-06T22:35:45+02:00 fix fastgap for events that go across cycle boundaries by yaxu in: [#225](https://codeberg.org/uzu/strudel/pulls/225) + +## september 2022 + +- 2022-09-25T21:15:36+02:00 Reverb by froos in: [#224](https://codeberg.org/uzu/strudel/pulls/224) +- 2022-09-25T00:27:26+02:00 focus tweak for squeezeJoin - another go at fixing #216 by yaxu in: [#221](https://codeberg.org/uzu/strudel/pulls/221) +- 2022-09-24T23:17:21+02:00 support negative speeds by froos in: [#222](https://codeberg.org/uzu/strudel/pulls/222) +- 2022-09-24T21:55:15+02:00 Feedback Delay by froos in: [#213](https://codeberg.org/uzu/strudel/pulls/213) +- 2022-09-23T12:06:17+02:00 Fix squeeze join by yaxu in: [#220](https://codeberg.org/uzu/strudel/pulls/220) +- 2022-09-22T21:23:11+02:00 encapsulate webaudio output by froos in: [#219](https://codeberg.org/uzu/strudel/pulls/219) +- 2022-09-22T19:18:18+02:00 samples now have envelopes by froos in: [#218](https://codeberg.org/uzu/strudel/pulls/218) +- 2022-09-22T00:03:30+02:00 sampler features + fixes by froos in: [#217](https://codeberg.org/uzu/strudel/pulls/217) +- 2022-09-19T23:32:55+02:00 Just another docs PR by froos in: [#215](https://codeberg.org/uzu/strudel/pulls/215) +- 2022-09-17T23:47:43+02:00 Even more docs by froos in: [#212](https://codeberg.org/uzu/strudel/pulls/212) +- 2022-09-17T15:36:53+02:00 Webaudio guide by froos in: [#207](https://codeberg.org/uzu/strudel/pulls/207) +- 2022-09-16T00:21:29+02:00 Coarse crush shape by froos in: [#205](https://codeberg.org/uzu/strudel/pulls/205) +- 2022-09-15T21:04:28+02:00 add vowel to .out by froos in: [#201](https://codeberg.org/uzu/strudel/pulls/201) +- 2022-09-15T16:55:52+02:00 add rollup-plugin-visualizer to build by froos in: [#200](https://codeberg.org/uzu/strudel/pulls/200) +- 2022-09-14T23:46:39+02:00 document random functions by froos in: [#199](https://codeberg.org/uzu/strudel/pulls/199) +- 2022-09-09T22:04:40+02:00 Fix numbers in sampler by froos in: [#196](https://codeberg.org/uzu/strudel/pulls/196) + +## august 2022 + +- 2022-08-14T17:40:41+02:00 change "stride"/"offset" of successive degradeBy/chooseIn by Ghost in: [#185](https://codeberg.org/uzu/strudel/pulls/185) +- 2022-08-14T16:04:14+02:00 Soundfont file support by froos in: [#183](https://codeberg.org/uzu/strudel/pulls/183) +- 2022-08-14T15:55:50+02:00 fix regression: old way of setting frequencies was broken by froos in: [#190](https://codeberg.org/uzu/strudel/pulls/190) +- 2022-08-14T15:45:53+02:00 wait for prebake to finish before evaluating by froos in: [#189](https://codeberg.org/uzu/strudel/pulls/189) +- 2022-08-14T11:31:00+02:00 Fix codemirror bug by froos in: [#186](https://codeberg.org/uzu/strudel/pulls/186) +- 2022-08-13T16:29:53+02:00 scheduler improvements by froos in: [#181](https://codeberg.org/uzu/strudel/pulls/181) +- 2022-08-12T23:10:36+02:00 replace mocha with vitest by froos in: [#175](https://codeberg.org/uzu/strudel/pulls/175) +- 2022-08-07T00:59:07+02:00 incorporate elements of randomness to the mini notation by Ghost in: [#165](https://codeberg.org/uzu/strudel/pulls/165) +- 2022-08-06T23:32:16+02:00 fix some annoying bugs by froos in: [#177](https://codeberg.org/uzu/strudel/pulls/177) +- 2022-08-06T00:27:17+02:00 Replace react-codemirror6 with @uiw/react-codemirror by froos in: [#173](https://codeberg.org/uzu/strudel/pulls/173) +- 2022-08-04T22:19:46+02:00 add more shapeshifter flags by froos in: [#99](https://codeberg.org/uzu/strudel/pulls/99) +- 2022-08-04T22:06:38+02:00 Amend shapeshifter to allow use of dynamic import by Ghost in: [#171](https://codeberg.org/uzu/strudel/pulls/171) +- 2022-08-02T23:43:07+02:00 Talk fixes by froos in: [#164](https://codeberg.org/uzu/strudel/pulls/164) +- 2022-08-02T23:04:34+02:00 Pianoroll fixes by froos in: [#163](https://codeberg.org/uzu/strudel/pulls/163) +- 2022-08-02T22:57:43+02:00 fix: jsdoc comments by froos in: [#169](https://codeberg.org/uzu/strudel/pulls/169) + +## july 2022 + +- 2022-07-30T23:51:11+02:00 add chooseInWith/chooseCycles by yaxu in: [#166](https://codeberg.org/uzu/strudel/pulls/166) +- 2022-07-28T19:26:42+02:00 update to tutorial documentation by Ghost in: [#162](https://codeberg.org/uzu/strudel/pulls/162) +- 2022-07-12T18:58:47+02:00 add webdirt drum samples to prebake for general availability by Ghost in: [#150](https://codeberg.org/uzu/strudel/pulls/150) +- 2022-07-12T08:34:17+02:00 Final update to demo.pdf by yaxu in: [#151](https://codeberg.org/uzu/strudel/pulls/151) + +## june 2022 + +- 2022-06-28T21:54:35+02:00 Sampler optimizations and more by froos in: [#148](https://codeberg.org/uzu/strudel/pulls/148) +- 2022-06-26T12:58:53+02:00 can now generate short link for sharing by froos in: [#146](https://codeberg.org/uzu/strudel/pulls/146) +- 2022-06-24T22:16:04+02:00 flash effect on ctrl enter by froos in: [#144](https://codeberg.org/uzu/strudel/pulls/144) +- 2022-06-22T20:18:17+02:00 Pianoroll Object Support by froos in: [#142](https://codeberg.org/uzu/strudel/pulls/142) +- 2022-06-21T22:57:28+02:00 Serial twiddles by yaxu in: [#141](https://codeberg.org/uzu/strudel/pulls/141) +- 2022-06-21T22:20:05+02:00 Soundfont Support by froos in: [#139](https://codeberg.org/uzu/strudel/pulls/139) +- 2022-06-21T14:11:50+02:00 Fix createParam() by yaxu in: [#140](https://codeberg.org/uzu/strudel/pulls/140) +- 2022-06-18T23:24:42+02:00 Webaudio rewrite by froos in: [#138](https://codeberg.org/uzu/strudel/pulls/138) +- 2022-06-16T21:58:38+02:00 add onTrigger helper by froos in: [#136](https://codeberg.org/uzu/strudel/pulls/136) +- 2022-06-16T20:48:23+02:00 Scheduler improvements by froos in: [#134](https://codeberg.org/uzu/strudel/pulls/134) +- 2022-06-16T20:48:07+02:00 remove cycle + delta from onTrigger by froos in: [#135](https://codeberg.org/uzu/strudel/pulls/135) +- 2022-06-13T21:28:27+02:00 add createParam + createParams by froos in: [#110](https://codeberg.org/uzu/strudel/pulls/110) +- 2022-06-13T21:27:09+02:00 Pianoroll enhancements by froos in: [#131](https://codeberg.org/uzu/strudel/pulls/131) +- 2022-06-05T13:06:03+02:00 Fix link to contributing to tutorial docs by Ghost in: [#129](https://codeberg.org/uzu/strudel/pulls/129) +- 2022-06-02T00:20:45+02:00 Webdirt by froos in: [#121](https://codeberg.org/uzu/strudel/pulls/121) +- 2022-06-01T23:56:04+02:00 fix: #122 ctrl enter would add newline by froos in: [#124](https://codeberg.org/uzu/strudel/pulls/124) +- 2022-06-01T18:21:56+02:00 fix: #108 by froos in: [#123](https://codeberg.org/uzu/strudel/pulls/123) + +## may 2022 + +- 2022-05-29T09:54:59+02:00 In source doc by froos in: [#117](https://codeberg.org/uzu/strudel/pulls/117) +- 2022-05-20T11:49:10+02:00 react package + vite build by froos in: [#116](https://codeberg.org/uzu/strudel/pulls/116) +- 2022-05-15T22:29:26+02:00 Osc timing improvements by yaxu in: [#113](https://codeberg.org/uzu/strudel/pulls/113) +- 2022-05-15T22:28:24+02:00 loopAt by yaxu in: [#114](https://codeberg.org/uzu/strudel/pulls/114) +- 2022-05-09T20:09:47+02:00 `.brak()`, `.inside()` and `.outside()` by yaxu in: [#112](https://codeberg.org/uzu/strudel/pulls/112) +- 2022-05-06T15:18:41+02:00 In source doc by yaxu in: [#105](https://codeberg.org/uzu/strudel/pulls/105) +- 2022-05-04T22:59:31+02:00 Embed style by froos in: [#109](https://codeberg.org/uzu/strudel/pulls/109) +- 2022-05-03T01:40:22+02:00 Reset, Restart and other composers by froos in: [#88](https://codeberg.org/uzu/strudel/pulls/88) +- 2022-05-02T22:47:21+02:00 /embed package: web component for repl by froos in: [#106](https://codeberg.org/uzu/strudel/pulls/106) +- 2022-05-02T22:05:34+02:00 Tune tests by froos in: [#104](https://codeberg.org/uzu/strudel/pulls/104) +- 2022-05-01T14:33:11+02:00 Codemirror 6 by froos in: [#97](https://codeberg.org/uzu/strudel/pulls/97) + +## april 2022 + +- 2022-04-28T23:45:53+02:00 Work on Codemirror 6 highlighting by Ghost in: [#102](https://codeberg.org/uzu/strudel/pulls/102) +- 2022-04-28T15:46:06+02:00 Change to Affero GPL by yaxu in: [#101](https://codeberg.org/uzu/strudel/pulls/101) +- 2022-04-26T00:13:07+02:00 Paper by froos in: [#98](https://codeberg.org/uzu/strudel/pulls/98) +- 2022-04-25T22:10:21+02:00 Fiddles with cat/stack by yaxu in: [#90](https://codeberg.org/uzu/strudel/pulls/90) +- 2022-04-22T13:08:11+02:00 Add pattern composers, implements #82 by yaxu in: [#83](https://codeberg.org/uzu/strudel/pulls/83) +- 2022-04-21T20:57:45+02:00 Tidy up a couple of old files by Ghost in: [#84](https://codeberg.org/uzu/strudel/pulls/84) +- 2022-04-21T15:21:29+02:00 add `striate()` by yaxu in: [#76](https://codeberg.org/uzu/strudel/pulls/76) +- 2022-04-20T20:17:27+02:00 Webaudio in REPL by froos in: [#77](https://codeberg.org/uzu/strudel/pulls/77) +- 2022-04-19T15:31:34+02:00 Basic webserial support by yaxu in: [#80](https://codeberg.org/uzu/strudel/pulls/80) +- 2022-04-17T19:29:49+02:00 Try to fix appLeft / appRight by yaxu in: [#75](https://codeberg.org/uzu/strudel/pulls/75) +- 2022-04-16T13:26:57+02:00 More random functions by yaxu in: [#74](https://codeberg.org/uzu/strudel/pulls/74) +- 2022-04-16T11:13:26+02:00 Port `perlin` noise, `rangex`, and `palindrome` by yaxu in: [#73](https://codeberg.org/uzu/strudel/pulls/73) +- 2022-04-16T10:26:07+02:00 webaudio package by froos in: [#26](https://codeberg.org/uzu/strudel/pulls/26) +- 2022-04-15T20:18:06+02:00 More randomness, fix `rand`, and add `brand`, `irand` and `choose` by yaxu in: [#70](https://codeberg.org/uzu/strudel/pulls/70) +- 2022-04-15T11:29:51+02:00 First effort at rand() by yaxu in: [#69](https://codeberg.org/uzu/strudel/pulls/69) +- 2022-04-14T17:41:18+02:00 use new fixed version of osc-js package by froos in: [#68](https://codeberg.org/uzu/strudel/pulls/68) +- 2022-04-14T00:09:18+02:00 Speech output by froos in: [#67](https://codeberg.org/uzu/strudel/pulls/67) +- 2022-04-13T23:55:33+02:00 Separate out strudel.mjs, make index.mjs aggregate module by yaxu in: [#62](https://codeberg.org/uzu/strudel/pulls/62) +- 2022-04-13T17:26:45+02:00 More functions by yaxu in: [#61](https://codeberg.org/uzu/strudel/pulls/61) +- 2022-04-13T10:04:29+02:00 More functions by yaxu in: [#56](https://codeberg.org/uzu/strudel/pulls/56) +- 2022-04-12T17:04:04+02:00 OSC and SuperDirt support by yaxu in: [#27](https://codeberg.org/uzu/strudel/pulls/27) +- 2022-04-12T13:24:14+02:00 Implement `chop()` by yaxu in: [#50](https://codeberg.org/uzu/strudel/pulls/50) +- 2022-04-12T12:37:32+02:00 First run at squeezeBind, ref #32 by yaxu in: [#48](https://codeberg.org/uzu/strudel/pulls/48) +- 2022-04-12T00:03:37+02:00 Fix polymeter by yaxu in: [#44](https://codeberg.org/uzu/strudel/pulls/44) +- 2022-04-11T22:39:44+02:00 Compose by froos in: [#40](https://codeberg.org/uzu/strudel/pulls/40) +- 2022-04-11T21:49:35+02:00 Update tutorial.mdx by Ghost in: [#38](https://codeberg.org/uzu/strudel/pulls/38) +- 2022-04-11T08:43:44+02:00 Update tutorial.mdx by Ghost in: [#37](https://codeberg.org/uzu/strudel/pulls/37) + +## march 2022 + +- 2022-03-28T17:27:22+02:00 Add chunk, chunkBack and iterBack by yaxu in: [#25](https://codeberg.org/uzu/strudel/pulls/25) +- 2022-03-28T11:48:22+02:00 packaging by froos in: [#24](https://codeberg.org/uzu/strudel/pulls/24) +- 2022-03-22T12:06:48+01:00 Update package.json by Ghost in: [#23](https://codeberg.org/uzu/strudel/pulls/23) +- 2022-03-06T20:35:20+01:00 added _asNumber + interprete numbers as midi by froos in: [#21](https://codeberg.org/uzu/strudel/pulls/21) + +## february 2022 + +- 2022-02-28T00:32:29+01:00 Fix resolveState by yaxu in: [#22](https://codeberg.org/uzu/strudel/pulls/22) +- 2022-02-27T20:52:52+01:00 Stateful queries and events (WIP) by yaxu in: [#14](https://codeberg.org/uzu/strudel/pulls/14) +- 2022-02-27T18:04:07+01:00 fix: 💄 Enhance visualisation of the Tutorial on mobile by Ghost in: [#19](https://codeberg.org/uzu/strudel/pulls/19) +- 2022-02-26T21:16:34+01:00 test: 📦 Add missing dependency and a CI check, to prevent oversights ;p by Ghost in: [#17](https://codeberg.org/uzu/strudel/pulls/17) +- 2022-02-26T13:29:19+01:00 higher latencyHint by froos in: [#16](https://codeberg.org/uzu/strudel/pulls/16) +- 2022-02-23T22:15:11+01:00 add apply and layer, and missing div/mul methods by yaxu in: [#15](https://codeberg.org/uzu/strudel/pulls/15) +- 2022-02-21T01:02:07+01:00 Add continuous signals (sine, cosine, saw, etc) by yaxu in: [#13](https://codeberg.org/uzu/strudel/pulls/13) +- 2022-02-19T21:30:04+01:00 Added mask() and struct() by yaxu in: [#11](https://codeberg.org/uzu/strudel/pulls/11) +- 2022-02-19T16:07:07+01:00 Failing test for `when` WIP by yaxu in: [#10](https://codeberg.org/uzu/strudel/pulls/10) +- 2022-02-10T17:21:31+01:00 Bugfix every, and create more top level functions by yaxu in: [#9](https://codeberg.org/uzu/strudel/pulls/9) +- 2022-02-10T00:51:21+01:00 timeCat by yaxu in: [#8](https://codeberg.org/uzu/strudel/pulls/8) +- 2022-02-07T23:07:58+01:00 fixed editor crash by froos in: [#7](https://codeberg.org/uzu/strudel/pulls/7) +- 2022-02-07T19:08:40+01:00 krill parser + improved repl by froos in: [#6](https://codeberg.org/uzu/strudel/pulls/6) +- 2022-02-06T00:59:16+01:00 Patternify all the things by yaxu in: [#5](https://codeberg.org/uzu/strudel/pulls/5) +- 2022-02-05T23:31:37+01:00 update readme for local dev by Ghost in: [#4](https://codeberg.org/uzu/strudel/pulls/4) +- 2022-02-05T22:36:06+01:00 Fix path by yaxu in: [#3](https://codeberg.org/uzu/strudel/pulls/3) +- 2022-02-05T21:52:51+01:00 repl + reify functions by froos in: [#2](https://codeberg.org/uzu/strudel/pulls/2) +[9:37:36] strudel % \ No newline at end of file diff --git a/warm.js b/warm.js index 0d148dee9..846d59a99 100644 --- a/warm.js +++ b/warm.js @@ -1,14 +1,30 @@ -fetch('https://codeberg.org/api/v1/repos/uzu/strudel/pulls?state=closed&page=1') - .then((res) => res.json()) - .then((pulls) => { - const r = pulls - .filter((pull) => pull.merged) - .sort((a, b) => new Date(b.closed_at) - new Date(a.closed_at)) - .map((pull) => `${pull.closed_at} ${pull.title} by ${pull.user.login || '?'} in: [#${pull.number}](${pull.url}) `) - .join('\n'); - console.log(r); - }); +// this script loads all merged PRs within the given page range +// it can be used to update the CHANGELOG.md file in a semi-automated way +// the problem: codeberg doesn't support loading merged PRs, so we have to filter them in memory +// luckily, we can sort after "recentupdate", which means we can do incremental changelog generation +// todo: support setting a "last_updated" date, so the script would automatically check how far it has to go -/* +async function main() { + let pageStart = 1; + let pageEnd = 1; + let prs = []; + for (let p = pageStart; p <= pageEnd; p++) { + console.log(`load page ${p}/${pageEnd}`); + const res = await fetch( + `https://codeberg.org/api/v1/repos/uzu/strudel/pulls?state=closed&sort=recentupdate&page=${p}`, + ); + const pulls = await res.json(); + const merged = pulls.filter((pull) => pull.merged); + prs = prs.concat(merged); + } + const output = prs + .sort((a, b) => new Date(b.closed_at) - new Date(a.closed_at)) + .map( + (pull) => `- ${pull.closed_at} ${pull.title} by @${pull.user.login || '?'} in: [#${pull.number}](${pull.url}) `, + ) + .join('\n'); + console.log('-------------'); + console.log(output); +} - */ +main(); From 3d13955c41cf9fa4e6ffafa1262d0d53b95c17a5 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 16 Jan 2026 10:08:41 +0100 Subject: [PATCH 106/124] fix: prefix usernames with @ --- CHANGELOG.md | 1854 +++++++++++++++++++++++++------------------------- 1 file changed, 927 insertions(+), 927 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3daf4e6cf..587343e43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,1076 +5,1076 @@ NOTE: you can generate this with `node warm.js`. it might still not be perfectly ## january 2026 -- 2026-01-15T16:59:14+01:00 mondo fix: add registered functions to scope automatically by daslyfe in: [#1896](https://codeberg.org/uzu/strudel/pulls/1896) -- 2026-01-15T16:58:14+01:00 Added docs for pattern search by JohnBjrk in: [#1905](https://codeberg.org/uzu/strudel/pulls/1905) -- 2026-01-15T16:57:19+01:00 Update kabelsalat dependency by jeromew in: [#1911](https://codeberg.org/uzu/strudel/pulls/1911) -- 2026-01-15T00:24:39+01:00 Bug Fix: Set pooled values immediately instead of scheduling by glossing in: [#1907](https://codeberg.org/uzu/strudel/pulls/1907) -- 2026-01-14T20:29:16+01:00 Feat: Kabelsalat integration by glossing in: [#1876](https://codeberg.org/uzu/strudel/pulls/1876) -- 2026-01-14T19:07:49+01:00 Feat: MIDI Keyboard 🎹🐈 by glossing in: [#1828](https://codeberg.org/uzu/strudel/pulls/1828) -- 2026-01-14T18:14:52+01:00 Bug Fix: Fix race condition between worklet termination and port messages by glossing in: [#1897](https://codeberg.org/uzu/strudel/pulls/1897) -- 2026-01-13T04:06:52+01:00 Add search/filter in patterns tab by JohnBjrk in: [#1842](https://codeberg.org/uzu/strudel/pulls/1842) -- 2026-01-13T00:18:10+01:00 Add shortcut for navigating through labels! by daslyfe in: [#1807](https://codeberg.org/uzu/strudel/pulls/1807) -- 2026-01-12T00:17:37+01:00 add warm.strudel.cc to faq by yaxu in: [#1891](https://codeberg.org/uzu/strudel/pulls/1891) -- 2026-01-11T19:00:25+01:00 Fix sounds example to work in the REPL by JesCoding in: [#1851](https://codeberg.org/uzu/strudel/pulls/1851) -- 2026-01-11T13:52:02+01:00 Improve hint text when sound search has no results by floy in: [#1883](https://codeberg.org/uzu/strudel/pulls/1883) -- 2026-01-11T13:19:46+01:00 New page FAQ in "More" by scrappy_fiddler in: [#1753](https://codeberg.org/uzu/strudel/pulls/1753) -- 2026-01-11T12:45:07+01:00 fix: remove faulty default readme by froos in: [#1889](https://codeberg.org/uzu/strudel/pulls/1889) -- 2026-01-11T12:15:34+01:00 fix/self-hosted-config by alienmind in: [#1880](https://codeberg.org/uzu/strudel/pulls/1880) -- 2026-01-11T12:07:48+01:00 Feat: Support External AudioContext Injection by 1d10t in: [#1833](https://codeberg.org/uzu/strudel/pulls/1833) -- 2026-01-11T11:37:37+01:00 fix: add trem to top level by froos in: [#1887](https://codeberg.org/uzu/strudel/pulls/1887) -- 2026-01-11T11:37:18+01:00 fix: export start cycle min 0 by froos in: [#1888](https://codeberg.org/uzu/strudel/pulls/1888) -- 2026-01-11T11:05:44+01:00 fix: repl package init audio properly by froos in: [#1836](https://codeberg.org/uzu/strudel/pulls/1836) -- 2026-01-11T06:51:24+01:00 Fix: show reload dialog when uploading prebake script by daslyfe in: [#1886](https://codeberg.org/uzu/strudel/pulls/1886) -- 2026-01-11T06:01:42+01:00 fixes Serial onTrigger() params #1633 by gueejla in: [#1885](https://codeberg.org/uzu/strudel/pulls/1885) -- 2026-01-10T23:12:48+01:00 Perf: Targeted node pools by glossing in: [#1810](https://codeberg.org/uzu/strudel/pulls/1810) -- 2026-01-10T20:52:40+01:00 Allow top level distortions for the purpose of FX by glossing in: [#1884](https://codeberg.org/uzu/strudel/pulls/1884) -- 2026-01-09T03:43:37+01:00 Bake in scaling by `freq` for FM with a gain node by glossing in: [#1878](https://codeberg.org/uzu/strudel/pulls/1878) -- 2026-01-09T02:53:38+01:00 Bug fix: Properly handle subcontrols by glossing in: [#1877](https://codeberg.org/uzu/strudel/pulls/1877) -- 2026-01-07T20:06:18+01:00 Feat: Add ability to turn mini parsing off with mini-off decorator by glossing in: [#1786](https://codeberg.org/uzu/strudel/pulls/1786) -- 2026-01-07T19:22:24+01:00 Bug Fix: Update loopStart/End to not be offset by glossing in: [#1826](https://codeberg.org/uzu/strudel/pulls/1826) -- 2026-01-06T06:35:49+01:00 Update modulator docstrings and allow ids to be patterns by glossing in: [#1874](https://codeberg.org/uzu/strudel/pulls/1874) -- 2026-01-04T17:14:11+01:00 Fix doc link in @strudel/osc README.md by forrcaho in: [#1872](https://codeberg.org/uzu/strudel/pulls/1872) -- 2026-01-04T02:01:07+01:00 Make stretch modulatable by glossing in: [#1870](https://codeberg.org/uzu/strudel/pulls/1870) -- 2026-01-02T18:44:53+01:00 Make pan modulatable by glossing in: [#1865](https://codeberg.org/uzu/strudel/pulls/1865) -- 2026-01-01T21:42:23+01:00 Fix transpilation example to have same mini-notation by JesCoding in: [#1850](https://codeberg.org/uzu/strudel/pulls/1850) -- 2026-01-01T21:39:05+01:00 fix: missing punctuation by eddyflux in: [#1860](https://codeberg.org/uzu/strudel/pulls/1860) -- 2026-01-01T20:20:34+01:00 Fix formatting of docstring by glossing in: [#1864](https://codeberg.org/uzu/strudel/pulls/1864) -- 2026-01-01T19:52:15+01:00 Feat: FX Chains by glossing in: [#1861](https://codeberg.org/uzu/strudel/pulls/1861) +- 2026-01-15T16:59:14+01:00 mondo fix: add registered functions to scope automatically by @daslyfe in: [#1896](https://codeberg.org/uzu/strudel/pulls/1896) +- 2026-01-15T16:58:14+01:00 Added docs for pattern search by @JohnBjrk in: [#1905](https://codeberg.org/uzu/strudel/pulls/1905) +- 2026-01-15T16:57:19+01:00 Update kabelsalat dependency by @jeromew in: [#1911](https://codeberg.org/uzu/strudel/pulls/1911) +- 2026-01-15T00:24:39+01:00 Bug Fix: Set pooled values immediately instead of scheduling by @glossing in: [#1907](https://codeberg.org/uzu/strudel/pulls/1907) +- 2026-01-14T20:29:16+01:00 Feat: Kabelsalat integration by @glossing in: [#1876](https://codeberg.org/uzu/strudel/pulls/1876) +- 2026-01-14T19:07:49+01:00 Feat: MIDI Keyboard 🎹🐈 by @glossing in: [#1828](https://codeberg.org/uzu/strudel/pulls/1828) +- 2026-01-14T18:14:52+01:00 Bug Fix: Fix race condition between worklet termination and port messages by @glossing in: [#1897](https://codeberg.org/uzu/strudel/pulls/1897) +- 2026-01-13T04:06:52+01:00 Add search/filter in patterns tab by @JohnBjrk in: [#1842](https://codeberg.org/uzu/strudel/pulls/1842) +- 2026-01-13T00:18:10+01:00 Add shortcut for navigating through labels! by @daslyfe in: [#1807](https://codeberg.org/uzu/strudel/pulls/1807) +- 2026-01-12T00:17:37+01:00 add warm.strudel.cc to faq by @yaxu in: [#1891](https://codeberg.org/uzu/strudel/pulls/1891) +- 2026-01-11T19:00:25+01:00 Fix sounds example to work in the REPL by @JesCoding in: [#1851](https://codeberg.org/uzu/strudel/pulls/1851) +- 2026-01-11T13:52:02+01:00 Improve hint text when sound search has no results by @floy in: [#1883](https://codeberg.org/uzu/strudel/pulls/1883) +- 2026-01-11T13:19:46+01:00 New page FAQ in "More" by @scrappy_fiddler in: [#1753](https://codeberg.org/uzu/strudel/pulls/1753) +- 2026-01-11T12:45:07+01:00 fix: remove faulty default readme by @froos in: [#1889](https://codeberg.org/uzu/strudel/pulls/1889) +- 2026-01-11T12:15:34+01:00 fix/self-hosted-config by @alienmind in: [#1880](https://codeberg.org/uzu/strudel/pulls/1880) +- 2026-01-11T12:07:48+01:00 Feat: Support External AudioContext Injection by @1d10t in: [#1833](https://codeberg.org/uzu/strudel/pulls/1833) +- 2026-01-11T11:37:37+01:00 fix: add trem to top level by @froos in: [#1887](https://codeberg.org/uzu/strudel/pulls/1887) +- 2026-01-11T11:37:18+01:00 fix: export start cycle min 0 by @froos in: [#1888](https://codeberg.org/uzu/strudel/pulls/1888) +- 2026-01-11T11:05:44+01:00 fix: repl package init audio properly by @froos in: [#1836](https://codeberg.org/uzu/strudel/pulls/1836) +- 2026-01-11T06:51:24+01:00 Fix: show reload dialog when uploading prebake script by @daslyfe in: [#1886](https://codeberg.org/uzu/strudel/pulls/1886) +- 2026-01-11T06:01:42+01:00 fixes Serial onTrigger() params #1633 by @gueejla in: [#1885](https://codeberg.org/uzu/strudel/pulls/1885) +- 2026-01-10T23:12:48+01:00 Perf: Targeted node pools by @glossing in: [#1810](https://codeberg.org/uzu/strudel/pulls/1810) +- 2026-01-10T20:52:40+01:00 Allow top level distortions for the purpose of FX by @glossing in: [#1884](https://codeberg.org/uzu/strudel/pulls/1884) +- 2026-01-09T03:43:37+01:00 Bake in scaling by `freq` for FM with a gain node by @glossing in: [#1878](https://codeberg.org/uzu/strudel/pulls/1878) +- 2026-01-09T02:53:38+01:00 Bug fix: Properly handle subcontrols by @glossing in: [#1877](https://codeberg.org/uzu/strudel/pulls/1877) +- 2026-01-07T20:06:18+01:00 Feat: Add ability to turn mini parsing off with mini-off decorator by @glossing in: [#1786](https://codeberg.org/uzu/strudel/pulls/1786) +- 2026-01-07T19:22:24+01:00 Bug Fix: Update loopStart/End to not be offset by @glossing in: [#1826](https://codeberg.org/uzu/strudel/pulls/1826) +- 2026-01-06T06:35:49+01:00 Update modulator docstrings and allow ids to be patterns by @glossing in: [#1874](https://codeberg.org/uzu/strudel/pulls/1874) +- 2026-01-04T17:14:11+01:00 Fix doc link in @strudel/osc README.md by @forrcaho in: [#1872](https://codeberg.org/uzu/strudel/pulls/1872) +- 2026-01-04T02:01:07+01:00 Make stretch modulatable by @glossing in: [#1870](https://codeberg.org/uzu/strudel/pulls/1870) +- 2026-01-02T18:44:53+01:00 Make pan modulatable by @glossing in: [#1865](https://codeberg.org/uzu/strudel/pulls/1865) +- 2026-01-01T21:42:23+01:00 Fix transpilation example to have same mini-notation by @JesCoding in: [#1850](https://codeberg.org/uzu/strudel/pulls/1850) +- 2026-01-01T21:39:05+01:00 fix: missing punctuation by @eddyflux in: [#1860](https://codeberg.org/uzu/strudel/pulls/1860) +- 2026-01-01T20:20:34+01:00 Fix formatting of docstring by @glossing in: [#1864](https://codeberg.org/uzu/strudel/pulls/1864) +- 2026-01-01T19:52:15+01:00 Feat: FX Chains by @glossing in: [#1861](https://codeberg.org/uzu/strudel/pulls/1861) ## december 2025 -- 2025-12-29T21:59:11+01:00 Bugfix: Fix modulator clamping when min/max not specified by glossing in: [#1859](https://codeberg.org/uzu/strudel/pulls/1859) -- 2025-12-29T20:54:11+01:00 Feature: LFOs and Envelopes by glossing in: [#1507](https://codeberg.org/uzu/strudel/pulls/1507) -- 2025-12-29T16:07:18+01:00 Fix AudioContext change detection. Use AudioNode.context by jeromew in: [#1858](https://codeberg.org/uzu/strudel/pulls/1858) -- 2025-12-28T22:58:38+01:00 Say that @license should use SPDX identifier by Wuzzy in: [#1817](https://codeberg.org/uzu/strudel/pulls/1817) -- 2025-12-28T22:56:07+01:00 Expose Vim object in order to create custom keybindings by JohnBjrk in: [#1816](https://codeberg.org/uzu/strudel/pulls/1816) -- 2025-12-28T14:20:03+01:00 dough repl fixes by froos in: [#1855](https://codeberg.org/uzu/strudel/pulls/1855) -- 2025-12-28T13:40:29+01:00 add basic dough repl by froos in: [#1749](https://codeberg.org/uzu/strudel/pulls/1749) -- 2025-12-20T22:27:52+01:00 Document "-" in mini-notation by Wuzzy in: [#1818](https://codeberg.org/uzu/strudel/pulls/1818) -- 2025-12-20T22:20:27+01:00 simplify envValAtTime and remove asymmetric behavior (fix #1653) by pulu in: [#1815](https://codeberg.org/uzu/strudel/pulls/1815) -- 2025-12-19T08:11:50+01:00 fix: visual block selection mode for vim bindings by Dsm0 in: [#1839](https://codeberg.org/uzu/strudel/pulls/1839) -- 2025-12-19T01:03:53+01:00 [perf] Add audiograph `await debugAudiograph()` feature by jeromew in: [#1763](https://codeberg.org/uzu/strudel/pulls/1763) -- 2025-12-19T00:45:41+01:00 Feature: non-realtime exporting by Ghost in: [#1674](https://codeberg.org/uzu/strudel/pulls/1674) -- 2025-12-14T19:33:37+01:00 Fix: wrong warning in build environments by jeromew in: [#1835](https://codeberg.org/uzu/strudel/pulls/1835) -- 2025-12-14T15:09:12+01:00 delta -> per / perx / cyclesPer refinements by yaxu in: [#1832](https://codeberg.org/uzu/strudel/pulls/1832) -- 2025-12-14T01:07:36+01:00 Improved randomness by glossing in: [#1505](https://codeberg.org/uzu/strudel/pulls/1505) -- 2025-12-12T10:28:27+01:00 Add delta signal for representing the duration of events in patterns that are combined with it by yaxu in: [#1831](https://codeberg.org/uzu/strudel/pulls/1831) -- 2025-12-11T18:00:49+01:00 Feature: stateful timeline function for jumping between timelines by yaxu in: [#1669](https://codeberg.org/uzu/strudel/pulls/1669) -- 2025-12-11T16:37:58+01:00 Updates relating to LLM, github, etc by yaxu in: [#1830](https://codeberg.org/uzu/strudel/pulls/1830) -- 2025-12-10T15:32:44+01:00 fix .as so it doesn't set undefined values by yaxu in: [#1827](https://codeberg.org/uzu/strudel/pulls/1827) -- 2025-12-08T19:27:31+01:00 [perf] propagate `onceEnded` and `releaseAudioNode` by jeromew in: [#1809](https://codeberg.org/uzu/strudel/pulls/1809) -- 2025-12-07T21:51:40+01:00 Feat: Add channel support to midi in by glossing in: [#1775](https://codeberg.org/uzu/strudel/pulls/1775) -- 2025-12-07T21:23:43+01:00 Feat: Transient shaper by glossing in: [#1777](https://codeberg.org/uzu/strudel/pulls/1777) -- 2025-12-07T19:15:43+01:00 Add vel as a synonym for velocity & update a few docstrings by glossing in: [#1781](https://codeberg.org/uzu/strudel/pulls/1781) -- 2025-12-07T18:53:04+01:00 [perf] fix phaser leak of unused biquads by jeromew in: [#1800](https://codeberg.org/uzu/strudel/pulls/1800) -- 2025-12-07T18:42:50+01:00 Bug fix: Remove failing tests due to shabda removal by glossing in: [#1820](https://codeberg.org/uzu/strudel/pulls/1820) -- 2025-12-03T18:27:28+01:00 Feature: Eight FMs by glossing in: [#1628](https://codeberg.org/uzu/strudel/pulls/1628) -- 2025-12-03T17:35:05+01:00 [perf] release unused AudioBufferSourceNode + releaseAudioNode by jeromew in: [#1805](https://codeberg.org/uzu/strudel/pulls/1805) +- 2025-12-29T21:59:11+01:00 Bugfix: Fix modulator clamping when min/max not specified by @glossing in: [#1859](https://codeberg.org/uzu/strudel/pulls/1859) +- 2025-12-29T20:54:11+01:00 Feature: LFOs and Envelopes by @glossing in: [#1507](https://codeberg.org/uzu/strudel/pulls/1507) +- 2025-12-29T16:07:18+01:00 Fix AudioContext change detection. Use AudioNode.context by @jeromew in: [#1858](https://codeberg.org/uzu/strudel/pulls/1858) +- 2025-12-28T22:58:38+01:00 Say that @license should use SPDX identifier by @Wuzzy in: [#1817](https://codeberg.org/uzu/strudel/pulls/1817) +- 2025-12-28T22:56:07+01:00 Expose Vim object in order to create custom keybindings by @JohnBjrk in: [#1816](https://codeberg.org/uzu/strudel/pulls/1816) +- 2025-12-28T14:20:03+01:00 dough repl fixes by @froos in: [#1855](https://codeberg.org/uzu/strudel/pulls/1855) +- 2025-12-28T13:40:29+01:00 add basic dough repl by @froos in: [#1749](https://codeberg.org/uzu/strudel/pulls/1749) +- 2025-12-20T22:27:52+01:00 Document "-" in mini-notation by @Wuzzy in: [#1818](https://codeberg.org/uzu/strudel/pulls/1818) +- 2025-12-20T22:20:27+01:00 simplify envValAtTime and remove asymmetric behavior (fix #1653) by @pulu in: [#1815](https://codeberg.org/uzu/strudel/pulls/1815) +- 2025-12-19T08:11:50+01:00 fix: visual block selection mode for vim bindings by @Dsm0 in: [#1839](https://codeberg.org/uzu/strudel/pulls/1839) +- 2025-12-19T01:03:53+01:00 [perf] Add audiograph `await debugAudiograph()` feature by @jeromew in: [#1763](https://codeberg.org/uzu/strudel/pulls/1763) +- 2025-12-19T00:45:41+01:00 Feature: non-realtime exporting by @Ghost in: [#1674](https://codeberg.org/uzu/strudel/pulls/1674) +- 2025-12-14T19:33:37+01:00 Fix: wrong warning in build environments by @jeromew in: [#1835](https://codeberg.org/uzu/strudel/pulls/1835) +- 2025-12-14T15:09:12+01:00 delta -> per / perx / cyclesPer refinements by @yaxu in: [#1832](https://codeberg.org/uzu/strudel/pulls/1832) +- 2025-12-14T01:07:36+01:00 Improved randomness by @glossing in: [#1505](https://codeberg.org/uzu/strudel/pulls/1505) +- 2025-12-12T10:28:27+01:00 Add delta signal for representing the duration of events in patterns that are combined with it by @yaxu in: [#1831](https://codeberg.org/uzu/strudel/pulls/1831) +- 2025-12-11T18:00:49+01:00 Feature: stateful timeline function for jumping between timelines by @yaxu in: [#1669](https://codeberg.org/uzu/strudel/pulls/1669) +- 2025-12-11T16:37:58+01:00 Updates relating to LLM, github, etc by @yaxu in: [#1830](https://codeberg.org/uzu/strudel/pulls/1830) +- 2025-12-10T15:32:44+01:00 fix .as so it doesn't set undefined values by @yaxu in: [#1827](https://codeberg.org/uzu/strudel/pulls/1827) +- 2025-12-08T19:27:31+01:00 [perf] propagate `onceEnded` and `releaseAudioNode` by @jeromew in: [#1809](https://codeberg.org/uzu/strudel/pulls/1809) +- 2025-12-07T21:51:40+01:00 Feat: Add channel support to midi in by @glossing in: [#1775](https://codeberg.org/uzu/strudel/pulls/1775) +- 2025-12-07T21:23:43+01:00 Feat: Transient shaper by @glossing in: [#1777](https://codeberg.org/uzu/strudel/pulls/1777) +- 2025-12-07T19:15:43+01:00 Add vel as a synonym for velocity & update a few docstrings by @glossing in: [#1781](https://codeberg.org/uzu/strudel/pulls/1781) +- 2025-12-07T18:53:04+01:00 [perf] fix phaser leak of unused biquads by @jeromew in: [#1800](https://codeberg.org/uzu/strudel/pulls/1800) +- 2025-12-07T18:42:50+01:00 Bug fix: Remove failing tests due to shabda removal by @glossing in: [#1820](https://codeberg.org/uzu/strudel/pulls/1820) +- 2025-12-03T18:27:28+01:00 Feature: Eight FMs by @glossing in: [#1628](https://codeberg.org/uzu/strudel/pulls/1628) +- 2025-12-03T17:35:05+01:00 [perf] release unused AudioBufferSourceNode + releaseAudioNode by @jeromew in: [#1805](https://codeberg.org/uzu/strudel/pulls/1805) - **2025-12-01 strudel.cc deployed** ## november 2025 -- 2025-11-29T01:00:42+01:00 added export to getSuperdoughAudioController() so that its possible to route superdough audio through other webaudio applications. by ndr0n in: [#1796](https://codeberg.org/uzu/strudel/pulls/1796) -- 2025-11-28T23:26:20+01:00 add revv() for reversing whole patterns by yaxu in: [#1791](https://codeberg.org/uzu/strudel/pulls/1791) -- 2025-11-28T20:19:16+01:00 [perf] Disconnect lfos for phaser and filters by glossing in: [#1787](https://codeberg.org/uzu/strudel/pulls/1787) -- 2025-11-27T23:08:52+01:00 fix: return silence when no pattern is returned by froos in: [#1795](https://codeberg.org/uzu/strudel/pulls/1795) -- 2025-11-27T22:47:03+01:00 fix(tool/dbpatch): add missing package name by peterpf in: [#1765](https://codeberg.org/uzu/strudel/pulls/1765) -- 2025-11-27T22:38:05+01:00 add CHANGELOG.md + basic script to generate by froos in: [#1794](https://codeberg.org/uzu/strudel/pulls/1794) -- 2025-11-27T22:03:53+01:00 [perf] in `noise`, let noiseMix do the disconnect when it exists by jeromew in: [#1783](https://codeberg.org/uzu/strudel/pulls/1783) -- 2025-11-27T22:01:01+01:00 Bug Fix: Retries for sounds tab by glossing in: [#1754](https://codeberg.org/uzu/strudel/pulls/1754) -- 2025-11-27T20:36:46+01:00 [hydra] return the hydra object when await initHydra(..) is called by jeromew in: [#1784](https://codeberg.org/uzu/strudel/pulls/1784) -- 2025-11-27T20:36:14+01:00 Use errorLogger for query and tonal errors by glossing in: [#1782](https://codeberg.org/uzu/strudel/pulls/1782) -- 2025-11-27T20:27:10+01:00 [perf] level 5 `connect-leak` on `vowel` by jeromew in: [#1779](https://codeberg.org/uzu/strudel/pulls/1779) -- 2025-11-26T08:23:38+01:00 feat: add prebake script import button for loading .strudel files by daslyfe in: [#1774](https://codeberg.org/uzu/strudel/pulls/1774) -- 2025-11-26T07:39:28+01:00 Fix Sampler port trampling by Dayglo in: [#1717](https://codeberg.org/uzu/strudel/pulls/1717) -- 2025-11-25T20:59:09+01:00 Feat: Hook up octave and fix example by glossing in: [#1773](https://codeberg.org/uzu/strudel/pulls/1773) -- 2025-11-25T20:48:09+01:00 [perf] fix `connect-leak` in `tremolo` param by jeromew in: [#1780](https://codeberg.org/uzu/strudel/pulls/1780) -- 2025-11-24T17:51:20+01:00 filter modulation improvements! by daslyfe in: [#1769](https://codeberg.org/uzu/strudel/pulls/1769) -- 2025-11-24T01:20:53+01:00 Add o as a synonym for orbit by daslyfe in: [#1766](https://codeberg.org/uzu/strudel/pulls/1766) -- 2025-11-23T22:23:26+01:00 [perf] fix `connect-leak` in `fm` modulation by jeromew in: [#1758](https://codeberg.org/uzu/strudel/pulls/1758) -- 2025-11-23T09:47:24+01:00 Fix interoperability issue between `all` and `await initHydra()` by jeromew in: [#1663](https://codeberg.org/uzu/strudel/pulls/1663) -- 2025-11-23T04:23:06+01:00 Bug fix: Swap l/r gains with temp variable by glossing in: [#1768](https://codeberg.org/uzu/strudel/pulls/1768) -- 2025-11-23T00:52:28+01:00 FIX: prevent LFO filter modulation from popping from negative values by daslyfe in: [#1767](https://codeberg.org/uzu/strudel/pulls/1767) -- 2025-11-23T00:03:32+01:00 prefix "S" for solo by froos in: [#1481](https://codeberg.org/uzu/strudel/pulls/1481) -- 2025-11-21T01:56:11+01:00 Bug Fix: Use frac due to negative frequencies from FM by glossing in: [#1759](https://codeberg.org/uzu/strudel/pulls/1759) -- 2025-11-20T19:48:16+01:00 [perf] fix `connect-leak` added by #1742 when noise() is not used by jeromew in: [#1757](https://codeberg.org/uzu/strudel/pulls/1757) -- 2025-11-20T14:51:00+01:00 [perf] fix `connect-leak` in `delay` effect by jeromew in: [#1755](https://codeberg.org/uzu/strudel/pulls/1755) -- 2025-11-20T14:49:41+01:00 [perf] fix `connect leak` when .noise() is in the mix by jeromew in: [#1742](https://codeberg.org/uzu/strudel/pulls/1742) -- 2025-11-18T23:52:45+01:00 wchooseCycles has now notes in an example by scrappy_fiddler in: [#1748](https://codeberg.org/uzu/strudel/pulls/1748) -- 2025-11-17T05:31:54+01:00 Feature: Partials by glossing in: [#1659](https://codeberg.org/uzu/strudel/pulls/1659) -- 2025-11-16T22:06:14+01:00 README: update superdough documentation by TristanMlct in: [#1741](https://codeberg.org/uzu/strudel/pulls/1741) -- 2025-11-16T22:00:43+01:00 Feature: Envelope worklet by glossing in: [#1524](https://codeberg.org/uzu/strudel/pulls/1524) -- 2025-11-16T21:08:54+01:00 Worklet optimizations by glossing in: [#1730](https://codeberg.org/uzu/strudel/pulls/1730) -- 2025-11-15T14:59:30+01:00 [perf] disconnect Offline AudioNode connections in generateReverb by jeromew in: [#1740](https://codeberg.org/uzu/strudel/pulls/1740) -- 2025-11-15T14:57:47+01:00 [perf] disconnect `send` effect AudioNode when `room` is used by jeromew in: [#1736](https://codeberg.org/uzu/strudel/pulls/1736) -- 2025-11-12T21:22:34+01:00 [supradough] fix: unstable filter by froos in: [#1593](https://codeberg.org/uzu/strudel/pulls/1593) -- 2025-11-12T21:12:07+01:00 Fix link syntax in `project-start` by Kissaki in: [#1724](https://codeberg.org/uzu/strudel/pulls/1724) -- 2025-11-12T21:06:13+01:00 Fix web README sample code by Kissaki in: [#1725](https://codeberg.org/uzu/strudel/pulls/1725) -- 2025-11-12T20:59:39+01:00 Fix typo: 'studel' -> 'strudel' by drhayes in: [#1726](https://codeberg.org/uzu/strudel/pulls/1726) -- 2025-11-12T15:23:41+01:00 fix for node 24 support in tests - #1718 by ausav in: [#1719](https://codeberg.org/uzu/strudel/pulls/1719) -- 2025-11-11T09:00:49+01:00 soundAlias example fix by PepsiiMan in: [#1720](https://codeberg.org/uzu/strudel/pulls/1720) -- 2025-11-10T08:18:36+01:00 fix: Make Supradough package build by munshkr in: [#1711](https://codeberg.org/uzu/strudel/pulls/1711) -- 2025-11-10T08:18:00+01:00 added docs for spaces in scale names by ondras in: [#1715](https://codeberg.org/uzu/strudel/pulls/1715) -- 2025-11-10T02:44:01+01:00 Feature: LFOs for Filters by glossing in: [#1636](https://codeberg.org/uzu/strudel/pulls/1636) -- 2025-11-05T17:07:00+01:00 improvement: sync midi messages to audio context clock by daslyfe in: [#1708](https://codeberg.org/uzu/strudel/pulls/1708) -- 2025-11-04T19:36:14+01:00 Add stick button mappings to gamepad implementation and improve docs by kaosuryoko in: [#1696](https://codeberg.org/uzu/strudel/pulls/1696) -- 2025-11-04T19:29:35+01:00 Refactor sound stopping and triggering logic in SoundsTab component by IJOL in: [#1688](https://codeberg.org/uzu/strudel/pulls/1688) -- 2025-11-04T19:13:21+01:00 Add setting to toggle pattern auto-start on pattern change by moumar in: [#1690](https://codeberg.org/uzu/strudel/pulls/1690) -- 2025-11-04T18:58:50+01:00 Voicings JSDoc by sharkeys_lunchbox in: [#1686](https://codeberg.org/uzu/strudel/pulls/1686) -- 2025-11-01T07:50:40+01:00 FIX: Loading local samples uses too much memory by daslyfe in: [#1706](https://codeberg.org/uzu/strudel/pulls/1706) +- 2025-11-29T01:00:42+01:00 added export to getSuperdoughAudioController() so that its possible to route superdough audio through other webaudio applications. by @ndr0n in: [#1796](https://codeberg.org/uzu/strudel/pulls/1796) +- 2025-11-28T23:26:20+01:00 add revv() for reversing whole patterns by @yaxu in: [#1791](https://codeberg.org/uzu/strudel/pulls/1791) +- 2025-11-28T20:19:16+01:00 [perf] Disconnect lfos for phaser and filters by @glossing in: [#1787](https://codeberg.org/uzu/strudel/pulls/1787) +- 2025-11-27T23:08:52+01:00 fix: return silence when no pattern is returned by @froos in: [#1795](https://codeberg.org/uzu/strudel/pulls/1795) +- 2025-11-27T22:47:03+01:00 fix(tool/dbpatch): add missing package name by @peterpf in: [#1765](https://codeberg.org/uzu/strudel/pulls/1765) +- 2025-11-27T22:38:05+01:00 add CHANGELOG.md + basic script to generate by @froos in: [#1794](https://codeberg.org/uzu/strudel/pulls/1794) +- 2025-11-27T22:03:53+01:00 [perf] in `noise`, let noiseMix do the disconnect when it exists by @jeromew in: [#1783](https://codeberg.org/uzu/strudel/pulls/1783) +- 2025-11-27T22:01:01+01:00 Bug Fix: Retries for sounds tab by @glossing in: [#1754](https://codeberg.org/uzu/strudel/pulls/1754) +- 2025-11-27T20:36:46+01:00 [hydra] return the hydra object when await initHydra(..) is called by @jeromew in: [#1784](https://codeberg.org/uzu/strudel/pulls/1784) +- 2025-11-27T20:36:14+01:00 Use errorLogger for query and tonal errors by @glossing in: [#1782](https://codeberg.org/uzu/strudel/pulls/1782) +- 2025-11-27T20:27:10+01:00 [perf] level 5 `connect-leak` on `vowel` by @jeromew in: [#1779](https://codeberg.org/uzu/strudel/pulls/1779) +- 2025-11-26T08:23:38+01:00 feat: add prebake script import button for loading .strudel files by @daslyfe in: [#1774](https://codeberg.org/uzu/strudel/pulls/1774) +- 2025-11-26T07:39:28+01:00 Fix Sampler port trampling by @Dayglo in: [#1717](https://codeberg.org/uzu/strudel/pulls/1717) +- 2025-11-25T20:59:09+01:00 Feat: Hook up octave and fix example by @glossing in: [#1773](https://codeberg.org/uzu/strudel/pulls/1773) +- 2025-11-25T20:48:09+01:00 [perf] fix `connect-leak` in `tremolo` param by @jeromew in: [#1780](https://codeberg.org/uzu/strudel/pulls/1780) +- 2025-11-24T17:51:20+01:00 filter modulation improvements! by @daslyfe in: [#1769](https://codeberg.org/uzu/strudel/pulls/1769) +- 2025-11-24T01:20:53+01:00 Add o as a synonym for orbit by @daslyfe in: [#1766](https://codeberg.org/uzu/strudel/pulls/1766) +- 2025-11-23T22:23:26+01:00 [perf] fix `connect-leak` in `fm` modulation by @jeromew in: [#1758](https://codeberg.org/uzu/strudel/pulls/1758) +- 2025-11-23T09:47:24+01:00 Fix interoperability issue between `all` and `await initHydra()` by @jeromew in: [#1663](https://codeberg.org/uzu/strudel/pulls/1663) +- 2025-11-23T04:23:06+01:00 Bug fix: Swap l/r gains with temp variable by @glossing in: [#1768](https://codeberg.org/uzu/strudel/pulls/1768) +- 2025-11-23T00:52:28+01:00 FIX: prevent LFO filter modulation from popping from negative values by @daslyfe in: [#1767](https://codeberg.org/uzu/strudel/pulls/1767) +- 2025-11-23T00:03:32+01:00 prefix "S" for solo by @froos in: [#1481](https://codeberg.org/uzu/strudel/pulls/1481) +- 2025-11-21T01:56:11+01:00 Bug Fix: Use frac due to negative frequencies from FM by @glossing in: [#1759](https://codeberg.org/uzu/strudel/pulls/1759) +- 2025-11-20T19:48:16+01:00 [perf] fix `connect-leak` added by #1742 when noise() is not used by @jeromew in: [#1757](https://codeberg.org/uzu/strudel/pulls/1757) +- 2025-11-20T14:51:00+01:00 [perf] fix `connect-leak` in `delay` effect by @jeromew in: [#1755](https://codeberg.org/uzu/strudel/pulls/1755) +- 2025-11-20T14:49:41+01:00 [perf] fix `connect leak` when .noise() is in the mix by @jeromew in: [#1742](https://codeberg.org/uzu/strudel/pulls/1742) +- 2025-11-18T23:52:45+01:00 wchooseCycles has now notes in an example by @scrappy_fiddler in: [#1748](https://codeberg.org/uzu/strudel/pulls/1748) +- 2025-11-17T05:31:54+01:00 Feature: Partials by @glossing in: [#1659](https://codeberg.org/uzu/strudel/pulls/1659) +- 2025-11-16T22:06:14+01:00 README: update superdough documentation by @TristanMlct in: [#1741](https://codeberg.org/uzu/strudel/pulls/1741) +- 2025-11-16T22:00:43+01:00 Feature: Envelope worklet by @glossing in: [#1524](https://codeberg.org/uzu/strudel/pulls/1524) +- 2025-11-16T21:08:54+01:00 Worklet optimizations by @glossing in: [#1730](https://codeberg.org/uzu/strudel/pulls/1730) +- 2025-11-15T14:59:30+01:00 [perf] disconnect Offline AudioNode connections in generateReverb by @jeromew in: [#1740](https://codeberg.org/uzu/strudel/pulls/1740) +- 2025-11-15T14:57:47+01:00 [perf] disconnect `send` effect AudioNode when `room` is used by @jeromew in: [#1736](https://codeberg.org/uzu/strudel/pulls/1736) +- 2025-11-12T21:22:34+01:00 [supradough] fix: unstable filter by @froos in: [#1593](https://codeberg.org/uzu/strudel/pulls/1593) +- 2025-11-12T21:12:07+01:00 Fix link syntax in `project-start` by @Kissaki in: [#1724](https://codeberg.org/uzu/strudel/pulls/1724) +- 2025-11-12T21:06:13+01:00 Fix web README sample code by @Kissaki in: [#1725](https://codeberg.org/uzu/strudel/pulls/1725) +- 2025-11-12T20:59:39+01:00 Fix typo: 'studel' -> 'strudel' by @drhayes in: [#1726](https://codeberg.org/uzu/strudel/pulls/1726) +- 2025-11-12T15:23:41+01:00 fix for node 24 support in tests - #1718 by @ausav in: [#1719](https://codeberg.org/uzu/strudel/pulls/1719) +- 2025-11-11T09:00:49+01:00 soundAlias example fix by @PepsiiMan in: [#1720](https://codeberg.org/uzu/strudel/pulls/1720) +- 2025-11-10T08:18:36+01:00 fix: Make Supradough package build by @munshkr in: [#1711](https://codeberg.org/uzu/strudel/pulls/1711) +- 2025-11-10T08:18:00+01:00 added docs for spaces in scale names by @ondras in: [#1715](https://codeberg.org/uzu/strudel/pulls/1715) +- 2025-11-10T02:44:01+01:00 Feature: LFOs for Filters by @glossing in: [#1636](https://codeberg.org/uzu/strudel/pulls/1636) +- 2025-11-05T17:07:00+01:00 improvement: sync midi messages to audio context clock by @daslyfe in: [#1708](https://codeberg.org/uzu/strudel/pulls/1708) +- 2025-11-04T19:36:14+01:00 Add stick button mappings to gamepad implementation and improve docs by @kaosuryoko in: [#1696](https://codeberg.org/uzu/strudel/pulls/1696) +- 2025-11-04T19:29:35+01:00 Refactor sound stopping and triggering logic in SoundsTab component by @IJOL in: [#1688](https://codeberg.org/uzu/strudel/pulls/1688) +- 2025-11-04T19:13:21+01:00 Add setting to toggle pattern auto-start on pattern change by @moumar in: [#1690](https://codeberg.org/uzu/strudel/pulls/1690) +- 2025-11-04T18:58:50+01:00 Voicings JSDoc by @sharkeys_lunchbox in: [#1686](https://codeberg.org/uzu/strudel/pulls/1686) +- 2025-11-01T07:50:40+01:00 FIX: Loading local samples uses too much memory by @daslyfe in: [#1706](https://codeberg.org/uzu/strudel/pulls/1706) ## october 2025 -- 2025-10-28T22:58:24+01:00 Bug Fix: Handle scale-for-notes when n also supplied by glossing in: [#1625](https://codeberg.org/uzu/strudel/pulls/1625) -- 2025-10-28T22:51:16+01:00 Repurpose vim shortcuts for usability by dtricks in: [#1624](https://codeberg.org/uzu/strudel/pulls/1624) -- 2025-10-28T22:35:21+01:00 Add support for euclidian in mondo with `bd&3:8` by TristanCacqueray in: [#1630](https://codeberg.org/uzu/strudel/pulls/1630) -- 2025-10-27T10:42:07+01:00 Use bunny cdn for all samples and json files by yaxu in: [#1701](https://codeberg.org/uzu/strudel/pulls/1701) +- 2025-10-28T22:58:24+01:00 Bug Fix: Handle scale-for-notes when n also supplied by @glossing in: [#1625](https://codeberg.org/uzu/strudel/pulls/1625) +- 2025-10-28T22:51:16+01:00 Repurpose vim shortcuts for usability by @dtricks in: [#1624](https://codeberg.org/uzu/strudel/pulls/1624) +- 2025-10-28T22:35:21+01:00 Add support for euclidian in mondo with `bd&3:8` by @TristanCacqueray in: [#1630](https://codeberg.org/uzu/strudel/pulls/1630) +- 2025-10-27T10:42:07+01:00 Use bunny cdn for all samples and json files by @yaxu in: [#1701](https://codeberg.org/uzu/strudel/pulls/1701) - **2025-10-27 @strudel/core@1.2.5** -- 2025-10-26T22:52:54+01:00 degithub - switch some samples to bunnycdn by yaxu in: [#1697](https://codeberg.org/uzu/strudel/pulls/1697) -- 2025-10-26T17:09:22+01:00 Fix ZZFX example by moumar in: [#1685](https://codeberg.org/uzu/strudel/pulls/1685) -- 2025-10-23T15:56:04+02:00 Make osc port and host configurable. Changes dependencies. by yaxu in: [#1682](https://codeberg.org/uzu/strudel/pulls/1682) -- 2025-10-23T03:47:31+02:00 Adds back shape to superdough by glossing in: [#1672](https://codeberg.org/uzu/strudel/pulls/1672) -- 2025-10-22T22:15:19+02:00 Fix sampler.mjs githubPath by jeromew in: [#1684](https://codeberg.org/uzu/strudel/pulls/1684) -- 2025-10-22T16:20:50+02:00 Fix onPaint for widgets by yaxu in: [#1658](https://codeberg.org/uzu/strudel/pulls/1658) -- 2025-10-22T15:19:12+02:00 Fix a bug introduced by #4e17cfbdd6 by milliganf in: [#1679](https://codeberg.org/uzu/strudel/pulls/1679) -- 2025-10-20T22:37:15+02:00 feature/autocomplete-sound-names by drdozer in: [#1564](https://codeberg.org/uzu/strudel/pulls/1564) -- 2025-10-20T21:58:12+02:00 add replicate + use it for ! in mondo by JoStro in: [#1436](https://codeberg.org/uzu/strudel/pulls/1436) -- 2025-10-18T04:29:20+02:00 Bug Fix: Wavetable: phase wrapping at 1 and detune by glossing in: [#1620](https://codeberg.org/uzu/strudel/pulls/1620) -- 2025-10-16T20:26:28+02:00 github samples: default to "samples" if repository is not specified by prezmop in: [#1644](https://codeberg.org/uzu/strudel/pulls/1644) -- 2025-10-16T17:33:39+02:00 Docs: add example of custom chained function by dariusk in: [#1642](https://codeberg.org/uzu/strudel/pulls/1642) -- 2025-10-14T12:39:48+02:00 textbox by yaxu in: [#1650](https://codeberg.org/uzu/strudel/pulls/1650) -- 2025-10-13T07:26:51+02:00 Feature: Distortion Modes by glossing in: [#1561](https://codeberg.org/uzu/strudel/pulls/1561) -- 2025-10-13T07:05:57+02:00 Feature: Wavetable FM by glossing in: [#1623](https://codeberg.org/uzu/strudel/pulls/1623) -- 2025-10-12T20:43:42+02:00 fix: repair REPL sample sources and URL concat (#1640) by erikfox in: [#1646](https://codeberg.org/uzu/strudel/pulls/1646) -- 2025-10-10T11:25:02+02:00 Add control-metadata by yaxu in: [#1634](https://codeberg.org/uzu/strudel/pulls/1634) -- 2025-10-07T22:59:39+02:00 Fix MQTT: change the trigger handler to match new hap by jurrchen in: [#1629](https://codeberg.org/uzu/strudel/pulls/1629) -- 2025-10-05T21:42:19+02:00 Fix case sensitivity for synonym search by vvolhejn in: [#1618](https://codeberg.org/uzu/strudel/pulls/1618) -- 2025-10-01T08:56:35+02:00 Optimize wavetable synth by glossing in: [#1613](https://codeberg.org/uzu/strudel/pulls/1613) -- 2025-10-01T02:02:33+02:00 fix: pattern switching by daslyfe in: [#1616](https://codeberg.org/uzu/strudel/pulls/1616) +- 2025-10-26T22:52:54+01:00 degithub - switch some samples to bunnycdn by @yaxu in: [#1697](https://codeberg.org/uzu/strudel/pulls/1697) +- 2025-10-26T17:09:22+01:00 Fix ZZFX example by @moumar in: [#1685](https://codeberg.org/uzu/strudel/pulls/1685) +- 2025-10-23T15:56:04+02:00 Make osc port and host configurable. Changes dependencies. by @yaxu in: [#1682](https://codeberg.org/uzu/strudel/pulls/1682) +- 2025-10-23T03:47:31+02:00 Adds back shape to superdough by @glossing in: [#1672](https://codeberg.org/uzu/strudel/pulls/1672) +- 2025-10-22T22:15:19+02:00 Fix sampler.mjs githubPath by @jeromew in: [#1684](https://codeberg.org/uzu/strudel/pulls/1684) +- 2025-10-22T16:20:50+02:00 Fix onPaint for widgets by @yaxu in: [#1658](https://codeberg.org/uzu/strudel/pulls/1658) +- 2025-10-22T15:19:12+02:00 Fix a bug introduced by #4e17cfbdd6 by @milliganf in: [#1679](https://codeberg.org/uzu/strudel/pulls/1679) +- 2025-10-20T22:37:15+02:00 feature/autocomplete-sound-names by @drdozer in: [#1564](https://codeberg.org/uzu/strudel/pulls/1564) +- 2025-10-20T21:58:12+02:00 add replicate + use it for ! in mondo by @JoStro in: [#1436](https://codeberg.org/uzu/strudel/pulls/1436) +- 2025-10-18T04:29:20+02:00 Bug Fix: Wavetable: phase wrapping at 1 and detune by @glossing in: [#1620](https://codeberg.org/uzu/strudel/pulls/1620) +- 2025-10-16T20:26:28+02:00 github samples: default to "samples" if repository is not specified by @prezmop in: [#1644](https://codeberg.org/uzu/strudel/pulls/1644) +- 2025-10-16T17:33:39+02:00 Docs: add example of custom chained function by @dariusk in: [#1642](https://codeberg.org/uzu/strudel/pulls/1642) +- 2025-10-14T12:39:48+02:00 textbox by @yaxu in: [#1650](https://codeberg.org/uzu/strudel/pulls/1650) +- 2025-10-13T07:26:51+02:00 Feature: Distortion Modes by @glossing in: [#1561](https://codeberg.org/uzu/strudel/pulls/1561) +- 2025-10-13T07:05:57+02:00 Feature: Wavetable FM by @glossing in: [#1623](https://codeberg.org/uzu/strudel/pulls/1623) +- 2025-10-12T20:43:42+02:00 fix: repair REPL sample sources and URL concat (#1640) by @erikfox in: [#1646](https://codeberg.org/uzu/strudel/pulls/1646) +- 2025-10-10T11:25:02+02:00 Add control-metadata by @yaxu in: [#1634](https://codeberg.org/uzu/strudel/pulls/1634) +- 2025-10-07T22:59:39+02:00 Fix MQTT: change the trigger handler to match new hap by @jurrchen in: [#1629](https://codeberg.org/uzu/strudel/pulls/1629) +- 2025-10-05T21:42:19+02:00 Fix case sensitivity for synonym search by @vvolhejn in: [#1618](https://codeberg.org/uzu/strudel/pulls/1618) +- 2025-10-01T08:56:35+02:00 Optimize wavetable synth by @glossing in: [#1613](https://codeberg.org/uzu/strudel/pulls/1613) +- 2025-10-01T02:02:33+02:00 fix: pattern switching by @daslyfe in: [#1616](https://codeberg.org/uzu/strudel/pulls/1616) ## september 2025 -- 2025-09-29T02:10:54+02:00 fix wavetable JSON by daslyfe in: [#1611](https://codeberg.org/uzu/strudel/pulls/1611) -- 2025-09-28T21:28:16+02:00 rename wavetable controls to match existing naming conventions by daslyfe in: [#1610](https://codeberg.org/uzu/strudel/pulls/1610) -- 2025-09-27T23:28:13+02:00 Feature: Wavetable synth improvements by glossing in: [#1607](https://codeberg.org/uzu/strudel/pulls/1607) -- 2025-09-27T21:43:10+02:00 wavetable improvements by daslyfe in: [#1608](https://codeberg.org/uzu/strudel/pulls/1608) -- 2025-09-26T08:48:46+02:00 create orbit based DJ filter by daslyfe in: [#1603](https://codeberg.org/uzu/strudel/pulls/1603) -- 2025-09-26T08:15:49+02:00 Feature: Wavetable synth by glossing in: [#1525](https://codeberg.org/uzu/strudel/pulls/1525) -- 2025-09-23T13:28:20+02:00 fix: time signal by daslyfe in: [#1583](https://codeberg.org/uzu/strudel/pulls/1583) -- 2025-09-21T16:16:06+02:00 mondo: fix all + setcpm + setcps by froos in: [#1600](https://codeberg.org/uzu/strudel/pulls/1600) -- 2025-09-19T21:59:43+02:00 fix: osc error message by froos in: [#1597](https://codeberg.org/uzu/strudel/pulls/1597) -- 2025-09-18T21:09:56+02:00 osc: --debug flag to see incoming messages by froos in: [#1595](https://codeberg.org/uzu/strudel/pulls/1595) -- 2025-09-17T14:12:10+02:00 simplify osc usage by froos in: [#1588](https://codeberg.org/uzu/strudel/pulls/1588) -- 2025-09-16T16:59:58+02:00 dev: logger errors to console in dev mode by daslyfe in: [#1585](https://codeberg.org/uzu/strudel/pulls/1585) -- 2025-09-16T03:19:37+02:00 Bug Fix / Feature: Updates to `duck` to avoid clicks and allow configuration over release/attack by glossing in: [#1514](https://codeberg.org/uzu/strudel/pulls/1514) -- 2025-09-15T23:36:31+02:00 Feat: Enhance `scale` function to quantize notes to a named scale by glossing in: [#1492](https://codeberg.org/uzu/strudel/pulls/1492) -- 2025-09-15T22:52:14+02:00 add tic80 font by froos in: [#1579](https://codeberg.org/uzu/strudel/pulls/1579) -- 2025-09-15T00:00:19+02:00 supradough: fix delay + add some jsdoc types by froos in: [#1578](https://codeberg.org/uzu/strudel/pulls/1578) -- 2025-09-14T12:42:58+02:00 added plyWith/plyWithClassic functions by Dsm0 in: [#1571](https://codeberg.org/uzu/strudel/pulls/1571) -- 2025-09-14T10:39:12+02:00 feat: sound alias by Options in: [#1494](https://codeberg.org/uzu/strudel/pulls/1494) -- 2025-09-14T01:20:15+02:00 Bug Fix: Allow penv values to be falsy by glossing in: [#1559](https://codeberg.org/uzu/strudel/pulls/1559) -- 2025-09-14T01:10:48+02:00 Update website/src/pages/workshop/first-sounds.mdx by fesmith in: [#1566](https://codeberg.org/uzu/strudel/pulls/1566) -- 2025-09-14T01:07:47+02:00 fix: autocomplete-styles + html rendering by froos in: [#1570](https://codeberg.org/uzu/strudel/pulls/1570) -- 2025-09-14T00:49:17+02:00 Feature: Synonyms in autocomplete and reference by glossing in: [#1535](https://codeberg.org/uzu/strudel/pulls/1535) -- 2025-09-13T20:01:48+02:00 Docs: add xen package examples by dudymas in: [#1446](https://codeberg.org/uzu/strudel/pulls/1446) -- 2025-09-13T19:51:10+02:00 Update website/src/pages/learn/code.mdx by anecondev in: [#1391](https://codeberg.org/uzu/strudel/pulls/1391) -- 2025-09-11T22:52:30+02:00 supradough poc by froos in: [#1362](https://codeberg.org/uzu/strudel/pulls/1362) -- 2025-09-11T17:29:35+02:00 fix: exclude mondough dependencies by froos in: [#1563](https://codeberg.org/uzu/strudel/pulls/1563) -- 2025-09-11T00:27:21+02:00 add basicSetup for keybindings by Dsm0 in: [#1462](https://codeberg.org/uzu/strudel/pulls/1462) -- 2025-09-10T17:25:26+02:00 feat: add delete user samples button for convenience by daslyfe in: [#1556](https://codeberg.org/uzu/strudel/pulls/1556) -- 2025-09-08T08:54:01+02:00 Fix formatting of REPL footnote by ddbeck in: [#1547](https://codeberg.org/uzu/strudel/pulls/1547) -- 2025-09-08T05:38:08+02:00 fix: OSC by daslyfe in: [#1557](https://codeberg.org/uzu/strudel/pulls/1557) -- 2025-09-07T22:47:13+02:00 Add examples for ? and | operators to documentation by james-collapse in: [#1532](https://codeberg.org/uzu/strudel/pulls/1532) -- 2025-09-07T21:48:19+02:00 Signal flow documentation by glossing in: [#1504](https://codeberg.org/uzu/strudel/pulls/1504) -- 2025-09-07T21:27:29+02:00 Allow flattening of dirs with sample server by glossing in: [#1529](https://codeberg.org/uzu/strudel/pulls/1529) -- 2025-09-07T21:05:24+02:00 fix: prevent orbit clicking by defining max number of channels in an orbit by daslyfe in: [#1552](https://codeberg.org/uzu/strudel/pulls/1552) -- 2025-09-02T08:42:42+02:00 Padding for tooltips, preserving linebreaks in descriptions, not closing autocompletes on click by glossing in: [#1521](https://codeberg.org/uzu/strudel/pulls/1521) -- 2025-09-01T17:50:11+02:00 Update "restore defaults" to not delete patterns by glossing in: [#1519](https://codeberg.org/uzu/strudel/pulls/1519) +- 2025-09-29T02:10:54+02:00 fix wavetable JSON by @daslyfe in: [#1611](https://codeberg.org/uzu/strudel/pulls/1611) +- 2025-09-28T21:28:16+02:00 rename wavetable controls to match existing naming conventions by @daslyfe in: [#1610](https://codeberg.org/uzu/strudel/pulls/1610) +- 2025-09-27T23:28:13+02:00 Feature: Wavetable synth improvements by @glossing in: [#1607](https://codeberg.org/uzu/strudel/pulls/1607) +- 2025-09-27T21:43:10+02:00 wavetable improvements by @daslyfe in: [#1608](https://codeberg.org/uzu/strudel/pulls/1608) +- 2025-09-26T08:48:46+02:00 create orbit based DJ filter by @daslyfe in: [#1603](https://codeberg.org/uzu/strudel/pulls/1603) +- 2025-09-26T08:15:49+02:00 Feature: Wavetable synth by @glossing in: [#1525](https://codeberg.org/uzu/strudel/pulls/1525) +- 2025-09-23T13:28:20+02:00 fix: time signal by @daslyfe in: [#1583](https://codeberg.org/uzu/strudel/pulls/1583) +- 2025-09-21T16:16:06+02:00 mondo: fix all + setcpm + setcps by @froos in: [#1600](https://codeberg.org/uzu/strudel/pulls/1600) +- 2025-09-19T21:59:43+02:00 fix: osc error message by @froos in: [#1597](https://codeberg.org/uzu/strudel/pulls/1597) +- 2025-09-18T21:09:56+02:00 osc: --debug flag to see incoming messages by @froos in: [#1595](https://codeberg.org/uzu/strudel/pulls/1595) +- 2025-09-17T14:12:10+02:00 simplify osc usage by @froos in: [#1588](https://codeberg.org/uzu/strudel/pulls/1588) +- 2025-09-16T16:59:58+02:00 dev: logger errors to console in dev mode by @daslyfe in: [#1585](https://codeberg.org/uzu/strudel/pulls/1585) +- 2025-09-16T03:19:37+02:00 Bug Fix / Feature: Updates to `duck` to avoid clicks and allow configuration over release/attack by @glossing in: [#1514](https://codeberg.org/uzu/strudel/pulls/1514) +- 2025-09-15T23:36:31+02:00 Feat: Enhance `scale` function to quantize notes to a named scale by @glossing in: [#1492](https://codeberg.org/uzu/strudel/pulls/1492) +- 2025-09-15T22:52:14+02:00 add tic80 font by @froos in: [#1579](https://codeberg.org/uzu/strudel/pulls/1579) +- 2025-09-15T00:00:19+02:00 supradough: fix delay + add some jsdoc types by @froos in: [#1578](https://codeberg.org/uzu/strudel/pulls/1578) +- 2025-09-14T12:42:58+02:00 added plyWith/plyWithClassic functions by @Dsm0 in: [#1571](https://codeberg.org/uzu/strudel/pulls/1571) +- 2025-09-14T10:39:12+02:00 feat: sound alias by @Options in: [#1494](https://codeberg.org/uzu/strudel/pulls/1494) +- 2025-09-14T01:20:15+02:00 Bug Fix: Allow penv values to be falsy by @glossing in: [#1559](https://codeberg.org/uzu/strudel/pulls/1559) +- 2025-09-14T01:10:48+02:00 Update website/src/pages/workshop/first-sounds.mdx by @fesmith in: [#1566](https://codeberg.org/uzu/strudel/pulls/1566) +- 2025-09-14T01:07:47+02:00 fix: autocomplete-styles + html rendering by @froos in: [#1570](https://codeberg.org/uzu/strudel/pulls/1570) +- 2025-09-14T00:49:17+02:00 Feature: Synonyms in autocomplete and reference by @glossing in: [#1535](https://codeberg.org/uzu/strudel/pulls/1535) +- 2025-09-13T20:01:48+02:00 Docs: add xen package examples by @dudymas in: [#1446](https://codeberg.org/uzu/strudel/pulls/1446) +- 2025-09-13T19:51:10+02:00 Update website/src/pages/learn/code.mdx by @anecondev in: [#1391](https://codeberg.org/uzu/strudel/pulls/1391) +- 2025-09-11T22:52:30+02:00 supradough poc by @froos in: [#1362](https://codeberg.org/uzu/strudel/pulls/1362) +- 2025-09-11T17:29:35+02:00 fix: exclude mondough dependencies by @froos in: [#1563](https://codeberg.org/uzu/strudel/pulls/1563) +- 2025-09-11T00:27:21+02:00 add basicSetup for keybindings by @Dsm0 in: [#1462](https://codeberg.org/uzu/strudel/pulls/1462) +- 2025-09-10T17:25:26+02:00 feat: add delete user samples button for convenience by @daslyfe in: [#1556](https://codeberg.org/uzu/strudel/pulls/1556) +- 2025-09-08T08:54:01+02:00 Fix formatting of REPL footnote by @ddbeck in: [#1547](https://codeberg.org/uzu/strudel/pulls/1547) +- 2025-09-08T05:38:08+02:00 fix: OSC by @daslyfe in: [#1557](https://codeberg.org/uzu/strudel/pulls/1557) +- 2025-09-07T22:47:13+02:00 Add examples for ? and | operators to documentation by james-@collapse in: [#1532](https://codeberg.org/uzu/strudel/pulls/1532) +- 2025-09-07T21:48:19+02:00 Signal flow documentation by @glossing in: [#1504](https://codeberg.org/uzu/strudel/pulls/1504) +- 2025-09-07T21:27:29+02:00 Allow flattening of dirs with sample server by @glossing in: [#1529](https://codeberg.org/uzu/strudel/pulls/1529) +- 2025-09-07T21:05:24+02:00 fix: prevent orbit clicking by defining max number of channels in an orbit by @daslyfe in: [#1552](https://codeberg.org/uzu/strudel/pulls/1552) +- 2025-09-02T08:42:42+02:00 Padding for tooltips, preserving linebreaks in descriptions, not closing autocompletes on click by @glossing in: [#1521](https://codeberg.org/uzu/strudel/pulls/1521) +- 2025-09-01T17:50:11+02:00 Update "restore defaults" to not delete patterns by @glossing in: [#1519](https://codeberg.org/uzu/strudel/pulls/1519) ## august 2025 -- 2025-08-31T19:10:04+02:00 feat: add the ability to control the speed and start time of the reverb IR by daslyfe in: [#1501](https://codeberg.org/uzu/strudel/pulls/1501) -- 2025-08-25T02:31:22+02:00 Bug Fix: Duck on Mobile / Safari by glossing in: [#1503](https://codeberg.org/uzu/strudel/pulls/1503) -- 2025-08-23T18:39:53+02:00 fix benchmarks by yaxu in: [#1517](https://codeberg.org/uzu/strudel/pulls/1517) -- 2025-08-21T20:57:31+02:00 add --json flag to @strudel/sampler to generate a strudel.json by froos in: [#1513](https://codeberg.org/uzu/strudel/pulls/1513) -- 2025-08-21T10:04:28+02:00 remove hs2js postinstall by froos in: [#1510](https://codeberg.org/uzu/strudel/pulls/1510) -- 2025-08-20T15:15:34+02:00 doc: scrub, duckorbit, duckattack, duckdepth + fix: arp, arpWidth, hush by fyynn in: [#1502](https://codeberg.org/uzu/strudel/pulls/1502) -- 2025-08-20T12:31:37+02:00 fix: repl autocomplete not rendering correctly by robase in: [#1480](https://codeberg.org/uzu/strudel/pulls/1480) -- 2025-08-20T12:10:02+02:00 BUG FIX: make 'midin' initialization work with multiple controllers by cbabraham in: [#1469](https://codeberg.org/uzu/strudel/pulls/1469) -- 2025-08-17T23:18:46+02:00 euclidish by yaxu in: [#1482](https://codeberg.org/uzu/strudel/pulls/1482) -- 2025-08-17T06:37:23+02:00 feat: Create a duck (sidechain) orbit effect by daslyfe in: [#1470](https://codeberg.org/uzu/strudel/pulls/1470) -- 2025-08-17T04:40:15+02:00 Bug Fix: Restore `log` functionality after onTrigger arg removal by glossing in: [#1491](https://codeberg.org/uzu/strudel/pulls/1491) -- 2025-08-17T04:33:39+02:00 Bug Fix: Handle zero appearing first in FM's ADSR by glossing in: [#1496](https://codeberg.org/uzu/strudel/pulls/1496) -- 2025-08-17T04:27:19+02:00 Bug Fix: FM (and vibrato) for Supersaws by glossing in: [#1495](https://codeberg.org/uzu/strudel/pulls/1495) -- 2025-08-04T07:51:31+02:00 Fix incorrect stack Mini Notation by samyk in: [#1484](https://codeberg.org/uzu/strudel/pulls/1484) -- 2025-08-03T18:57:45+02:00 fix: regression caused by incorrectly exported alias for transpose and scaleTranspose by daslyfe in: [#1489](https://codeberg.org/uzu/strudel/pulls/1489) -- 2025-08-03T18:34:13+02:00 Add `strans` and `scaleTrans` as synonyms of `scaleTranspose` by TodePond in: [#1488](https://codeberg.org/uzu/strudel/pulls/1488) -- 2025-08-02T18:18:28+02:00 Add `trans` alias for `transpose` by TodePond in: [#1487](https://codeberg.org/uzu/strudel/pulls/1487) -- 2025-08-02T03:27:26+02:00 feat: Create Accurate 909 bass drum synthesizer by daslyfe in: [#1478](https://codeberg.org/uzu/strudel/pulls/1478) -- 2025-08-02T03:23:49+02:00 feat: Add fmwave control and ability to fm with noise by daslyfe in: [#1472](https://codeberg.org/uzu/strudel/pulls/1472) +- 2025-08-31T19:10:04+02:00 feat: add the ability to control the speed and start time of the reverb IR by @daslyfe in: [#1501](https://codeberg.org/uzu/strudel/pulls/1501) +- 2025-08-25T02:31:22+02:00 Bug Fix: Duck on Mobile / Safari by @glossing in: [#1503](https://codeberg.org/uzu/strudel/pulls/1503) +- 2025-08-23T18:39:53+02:00 fix benchmarks by @yaxu in: [#1517](https://codeberg.org/uzu/strudel/pulls/1517) +- 2025-08-21T20:57:31+02:00 add --json flag to @strudel/sampler to generate a strudel.json by @froos in: [#1513](https://codeberg.org/uzu/strudel/pulls/1513) +- 2025-08-21T10:04:28+02:00 remove hs2js postinstall by @froos in: [#1510](https://codeberg.org/uzu/strudel/pulls/1510) +- 2025-08-20T15:15:34+02:00 doc: scrub, duckorbit, duckattack, duckdepth + fix: arp, arpWidth, hush by @fyynn in: [#1502](https://codeberg.org/uzu/strudel/pulls/1502) +- 2025-08-20T12:31:37+02:00 fix: repl autocomplete not rendering correctly by @robase in: [#1480](https://codeberg.org/uzu/strudel/pulls/1480) +- 2025-08-20T12:10:02+02:00 BUG FIX: make 'midin' initialization work with multiple controllers by @cbabraham in: [#1469](https://codeberg.org/uzu/strudel/pulls/1469) +- 2025-08-17T23:18:46+02:00 euclidish by @yaxu in: [#1482](https://codeberg.org/uzu/strudel/pulls/1482) +- 2025-08-17T06:37:23+02:00 feat: Create a duck (sidechain) orbit effect by @daslyfe in: [#1470](https://codeberg.org/uzu/strudel/pulls/1470) +- 2025-08-17T04:40:15+02:00 Bug Fix: Restore `log` functionality after onTrigger arg removal by @glossing in: [#1491](https://codeberg.org/uzu/strudel/pulls/1491) +- 2025-08-17T04:33:39+02:00 Bug Fix: Handle zero appearing first in FM's ADSR by @glossing in: [#1496](https://codeberg.org/uzu/strudel/pulls/1496) +- 2025-08-17T04:27:19+02:00 Bug Fix: FM (and vibrato) for Supersaws by @glossing in: [#1495](https://codeberg.org/uzu/strudel/pulls/1495) +- 2025-08-04T07:51:31+02:00 Fix incorrect stack Mini Notation by @samyk in: [#1484](https://codeberg.org/uzu/strudel/pulls/1484) +- 2025-08-03T18:57:45+02:00 fix: regression caused by incorrectly exported alias for transpose and scaleTranspose by @daslyfe in: [#1489](https://codeberg.org/uzu/strudel/pulls/1489) +- 2025-08-03T18:34:13+02:00 Add `strans` and `scaleTrans` as synonyms of `scaleTranspose` by @TodePond in: [#1488](https://codeberg.org/uzu/strudel/pulls/1488) +- 2025-08-02T18:18:28+02:00 Add `trans` alias for `transpose` by @TodePond in: [#1487](https://codeberg.org/uzu/strudel/pulls/1487) +- 2025-08-02T03:27:26+02:00 feat: Create Accurate 909 bass drum synthesizer by @daslyfe in: [#1478](https://codeberg.org/uzu/strudel/pulls/1478) +- 2025-08-02T03:23:49+02:00 feat: Add fmwave control and ability to fm with noise by @daslyfe in: [#1472](https://codeberg.org/uzu/strudel/pulls/1472) ## july 2025 -- 2025-07-30T00:22:41+02:00 hotfix: uzu kit JSON should be in the repo to avoid offline loading errors by daslyfe in: [#1485](https://codeberg.org/uzu/strudel/pulls/1485) -- 2025-07-24T05:28:23+02:00 feat: make uzu-drumkit the default drumkit by daslyfe in: [#1422](https://codeberg.org/uzu/strudel/pulls/1422) -- 2025-07-21T07:16:19+02:00 FIX: PWM modulation by daslyfe in: [#1467](https://codeberg.org/uzu/strudel/pulls/1467) -- 2025-07-19T17:16:22+02:00 Tremolo modulation by Ghost in: [#1095](https://codeberg.org/uzu/strudel/pulls/1095) -- 2025-07-15T09:39:33+02:00 fix: can now use def in mondough by froos in: [#1461](https://codeberg.org/uzu/strudel/pulls/1461) -- 2025-07-11T11:10:24+02:00 fixed keybinding presedence issue by Dsm0 in: [#1456](https://codeberg.org/uzu/strudel/pulls/1456) -- 2025-07-11T11:09:05+02:00 added multicursor support on ctrl/cmd + click by Dsm0 in: [#1457](https://codeberg.org/uzu/strudel/pulls/1457) -- 2025-07-09T09:39:46+02:00 added tab-indent setting by Dsm0 in: [#1454](https://codeberg.org/uzu/strudel/pulls/1454) -- 2025-07-08T15:48:41+02:00 ontrigger-refactoring by froos in: [#1442](https://codeberg.org/uzu/strudel/pulls/1442) -- 2025-07-07T23:14:24+02:00 fix: remove first gm_synth_bass_1, as it doesn't work in safari by froos in: [#1452](https://codeberg.org/uzu/strudel/pulls/1452) -- 2025-07-07T00:06:16+02:00 fix: minor doc changes by Aurel300 in: [#1435](https://codeberg.org/uzu/strudel/pulls/1435) -- 2025-07-05T12:53:45+02:00 Fix randrun and deps including shuffle. Fixes #1441 by yaxu in: [#1447](https://codeberg.org/uzu/strudel/pulls/1447) -- 2025-07-05T12:15:52+02:00 add setDefault + resetDefaults to superdough by froos in: [#1433](https://codeberg.org/uzu/strudel/pulls/1433) -- 2025-07-04T22:36:19+02:00 disable fm for supersaw by froos in: [#1443](https://codeberg.org/uzu/strudel/pulls/1443) +- 2025-07-30T00:22:41+02:00 hotfix: uzu kit JSON should be in the repo to avoid offline loading errors by @daslyfe in: [#1485](https://codeberg.org/uzu/strudel/pulls/1485) +- 2025-07-24T05:28:23+02:00 feat: make uzu-drumkit the default drumkit by @daslyfe in: [#1422](https://codeberg.org/uzu/strudel/pulls/1422) +- 2025-07-21T07:16:19+02:00 FIX: PWM modulation by @daslyfe in: [#1467](https://codeberg.org/uzu/strudel/pulls/1467) +- 2025-07-19T17:16:22+02:00 Tremolo modulation by @Ghost in: [#1095](https://codeberg.org/uzu/strudel/pulls/1095) +- 2025-07-15T09:39:33+02:00 fix: can now use def in mondough by @froos in: [#1461](https://codeberg.org/uzu/strudel/pulls/1461) +- 2025-07-11T11:10:24+02:00 fixed keybinding presedence issue by @Dsm0 in: [#1456](https://codeberg.org/uzu/strudel/pulls/1456) +- 2025-07-11T11:09:05+02:00 added multicursor support on ctrl/cmd + click by @Dsm0 in: [#1457](https://codeberg.org/uzu/strudel/pulls/1457) +- 2025-07-09T09:39:46+02:00 added tab-indent setting by @Dsm0 in: [#1454](https://codeberg.org/uzu/strudel/pulls/1454) +- 2025-07-08T15:48:41+02:00 ontrigger-refactoring by @froos in: [#1442](https://codeberg.org/uzu/strudel/pulls/1442) +- 2025-07-07T23:14:24+02:00 fix: remove first gm_synth_bass_1, as it doesn't work in safari by @froos in: [#1452](https://codeberg.org/uzu/strudel/pulls/1452) +- 2025-07-07T00:06:16+02:00 fix: minor doc changes by @Aurel300 in: [#1435](https://codeberg.org/uzu/strudel/pulls/1435) +- 2025-07-05T12:53:45+02:00 Fix randrun and deps including shuffle. Fixes #1441 by @yaxu in: [#1447](https://codeberg.org/uzu/strudel/pulls/1447) +- 2025-07-05T12:15:52+02:00 add setDefault + resetDefaults to superdough by @froos in: [#1433](https://codeberg.org/uzu/strudel/pulls/1433) +- 2025-07-04T22:36:19+02:00 disable fm for supersaw by @froos in: [#1443](https://codeberg.org/uzu/strudel/pulls/1443) ## june 2025 -- 2025-06-30T13:28:10+02:00 corrected minor spelling mistake (#1425) by tj-mueller in: [#1434](https://codeberg.org/uzu/strudel/pulls/1434) -- 2025-06-30T05:26:11+02:00 Add browser cache explanation in samples.mdx by Ghost in: [#1369](https://codeberg.org/uzu/strudel/pulls/1369) -- 2025-06-30T05:18:42+02:00 add-release-notes by froos in: [#1432](https://codeberg.org/uzu/strudel/pulls/1432) -- 2025-06-28T20:30:49+02:00 feat: delaytime in cycles by Ghost in: [#1341](https://codeberg.org/uzu/strudel/pulls/1341) -- 2025-06-28T13:44:49+02:00 Case insensitive search in the reference tab by kdiab in: [#1420](https://codeberg.org/uzu/strudel/pulls/1420) -- 2025-06-28T00:04:13+02:00 add unjoin, into and chunkinto by yaxu in: [#1418](https://codeberg.org/uzu/strudel/pulls/1418) -- 2025-06-26T15:29:46+02:00 mondo notation by froos in: [#1311](https://codeberg.org/uzu/strudel/pulls/1311) -- 2025-06-24T06:14:15+02:00 allow calling `all` multiple times by froos in: [#1417](https://codeberg.org/uzu/strudel/pulls/1417) -- 2025-06-24T05:53:22+02:00 tonal: allow scales without tonic (default to C) by Ghost in: [#1092](https://codeberg.org/uzu/strudel/pulls/1092) -- 2025-06-20T20:00:12+02:00 website intro: fix whitespace and code hosting name by trofi in: [#1400](https://codeberg.org/uzu/strudel/pulls/1400) -- 2025-06-19T10:15:07+02:00 fix stepcat #1396 by yaxu in: [#1398](https://codeberg.org/uzu/strudel/pulls/1398) -- 2025-06-19T09:52:38+02:00 deprecate cpm + refactor docs to use setcpm by froos in: [#1397](https://codeberg.org/uzu/strudel/pulls/1397) -- 2025-06-19T02:49:09+02:00 feat: 3 new codemirror themes by daslyfe in: [#1394](https://codeberg.org/uzu/strudel/pulls/1394) -- 2025-06-14T16:23:00+02:00 link back to tech manual in wiki by yaxu in: [#1382](https://codeberg.org/uzu/strudel/pulls/1382) -- 2025-06-14T14:55:37+02:00 degithub-links by froos in: [#1380](https://codeberg.org/uzu/strudel/pulls/1380) -- 2025-06-13T15:24:42+02:00 fix euclidLegatoRot by bwagner in: [#1378](https://codeberg.org/uzu/strudel/pulls/1378) -- 2025-06-13T10:07:51+02:00 remove-ms by yaxu in: [#1377](https://codeberg.org/uzu/strudel/pulls/1377) -- 2025-06-12T10:25:48+02:00 Share fat URL by Ghost in: [#1375](https://codeberg.org/uzu/strudel/pulls/1375) -- 2025-06-05T20:39:16+02:00 feat: onTriggerTime for interfacing with random APIs and doing illegal things on event time by Ghost in: [#1364](https://codeberg.org/uzu/strudel/pulls/1364) -- 2025-06-05T01:08:18+02:00 feat: berlin noise by Ghost in: [#1363](https://codeberg.org/uzu/strudel/pulls/1363) +- 2025-06-30T13:28:10+02:00 corrected minor spelling mistake (#1425) by tj-@mueller in: [#1434](https://codeberg.org/uzu/strudel/pulls/1434) +- 2025-06-30T05:26:11+02:00 Add browser cache explanation in samples.mdx by @Ghost in: [#1369](https://codeberg.org/uzu/strudel/pulls/1369) +- 2025-06-30T05:18:42+02:00 add-release-notes by @froos in: [#1432](https://codeberg.org/uzu/strudel/pulls/1432) +- 2025-06-28T20:30:49+02:00 feat: delaytime in cycles by @Ghost in: [#1341](https://codeberg.org/uzu/strudel/pulls/1341) +- 2025-06-28T13:44:49+02:00 Case insensitive search in the reference tab by @kdiab in: [#1420](https://codeberg.org/uzu/strudel/pulls/1420) +- 2025-06-28T00:04:13+02:00 add unjoin, into and chunkinto by @yaxu in: [#1418](https://codeberg.org/uzu/strudel/pulls/1418) +- 2025-06-26T15:29:46+02:00 mondo notation by @froos in: [#1311](https://codeberg.org/uzu/strudel/pulls/1311) +- 2025-06-24T06:14:15+02:00 allow calling `all` multiple times by @froos in: [#1417](https://codeberg.org/uzu/strudel/pulls/1417) +- 2025-06-24T05:53:22+02:00 tonal: allow scales without tonic (default to C) by @Ghost in: [#1092](https://codeberg.org/uzu/strudel/pulls/1092) +- 2025-06-20T20:00:12+02:00 website intro: fix whitespace and code hosting name by @trofi in: [#1400](https://codeberg.org/uzu/strudel/pulls/1400) +- 2025-06-19T10:15:07+02:00 fix stepcat #1396 by @yaxu in: [#1398](https://codeberg.org/uzu/strudel/pulls/1398) +- 2025-06-19T09:52:38+02:00 deprecate cpm + refactor docs to use setcpm by @froos in: [#1397](https://codeberg.org/uzu/strudel/pulls/1397) +- 2025-06-19T02:49:09+02:00 feat: 3 new codemirror themes by @daslyfe in: [#1394](https://codeberg.org/uzu/strudel/pulls/1394) +- 2025-06-14T16:23:00+02:00 link back to tech manual in wiki by @yaxu in: [#1382](https://codeberg.org/uzu/strudel/pulls/1382) +- 2025-06-14T14:55:37+02:00 degithub-links by @froos in: [#1380](https://codeberg.org/uzu/strudel/pulls/1380) +- 2025-06-13T15:24:42+02:00 fix euclidLegatoRot by @bwagner in: [#1378](https://codeberg.org/uzu/strudel/pulls/1378) +- 2025-06-13T10:07:51+02:00 remove-ms by @yaxu in: [#1377](https://codeberg.org/uzu/strudel/pulls/1377) +- 2025-06-12T10:25:48+02:00 Share fat URL by @Ghost in: [#1375](https://codeberg.org/uzu/strudel/pulls/1375) +- 2025-06-05T20:39:16+02:00 feat: onTriggerTime for interfacing with random APIs and doing illegal things on event time by @Ghost in: [#1364](https://codeberg.org/uzu/strudel/pulls/1364) +- 2025-06-05T01:08:18+02:00 feat: berlin noise by @Ghost in: [#1363](https://codeberg.org/uzu/strudel/pulls/1363) ## may 2025 -- 2025-05-29T14:36:43+02:00 Fix bytebeat sample offset by Ghost in: [#1359](https://codeberg.org/uzu/strudel/pulls/1359) -- 2025-05-29T12:14:18+02:00 preserve stepcount in chunks by yaxu in: [#1358](https://codeberg.org/uzu/strudel/pulls/1358) -- 2025-05-29T02:34:30+02:00 Byte Beat improvements -> 2 by Ghost in: [#1357](https://codeberg.org/uzu/strudel/pulls/1357) -- 2025-05-28T16:40:32+02:00 Bytebeat improvements by Ghost in: [#1356](https://codeberg.org/uzu/strudel/pulls/1356) -- 2025-05-27T17:42:39+02:00 feat: Byte Beats! by Ghost in: [#1354](https://codeberg.org/uzu/strudel/pulls/1354) -- 2025-05-18T21:43:05+02:00 fix: typo on docs causing problems with autocompletion. by Ghost in: [#1350](https://codeberg.org/uzu/strudel/pulls/1350) -- 2025-05-15T01:12:07+02:00 feat: Create pulsewidth (pw) and pulsewidth lfo parameters by Ghost in: [#1343](https://codeberg.org/uzu/strudel/pulls/1343) -- 2025-05-14T18:25:17+02:00 feat: Multi Channel Orbits by Ghost in: [#1344](https://codeberg.org/uzu/strudel/pulls/1344) -- 2025-05-13T17:45:29+02:00 Fix typo by Ghost in: [#1346](https://codeberg.org/uzu/strudel/pulls/1346) -- 2025-05-02T00:01:27+02:00 Fix web + repl package builds by froos in: [#1339](https://codeberg.org/uzu/strudel/pulls/1339) -- 2025-05-01T23:50:57+02:00 fix: superdough worklets bundling by froos in: [#1338](https://codeberg.org/uzu/strudel/pulls/1338) +- 2025-05-29T14:36:43+02:00 Fix bytebeat sample offset by @Ghost in: [#1359](https://codeberg.org/uzu/strudel/pulls/1359) +- 2025-05-29T12:14:18+02:00 preserve stepcount in chunks by @yaxu in: [#1358](https://codeberg.org/uzu/strudel/pulls/1358) +- 2025-05-29T02:34:30+02:00 Byte Beat improvements -> 2 by @Ghost in: [#1357](https://codeberg.org/uzu/strudel/pulls/1357) +- 2025-05-28T16:40:32+02:00 Bytebeat improvements by @Ghost in: [#1356](https://codeberg.org/uzu/strudel/pulls/1356) +- 2025-05-27T17:42:39+02:00 feat: Byte Beats! by @Ghost in: [#1354](https://codeberg.org/uzu/strudel/pulls/1354) +- 2025-05-18T21:43:05+02:00 fix: typo on docs causing problems with autocompletion. by @Ghost in: [#1350](https://codeberg.org/uzu/strudel/pulls/1350) +- 2025-05-15T01:12:07+02:00 feat: Create pulsewidth (pw) and pulsewidth lfo parameters by @Ghost in: [#1343](https://codeberg.org/uzu/strudel/pulls/1343) +- 2025-05-14T18:25:17+02:00 feat: Multi Channel Orbits by @Ghost in: [#1344](https://codeberg.org/uzu/strudel/pulls/1344) +- 2025-05-13T17:45:29+02:00 Fix typo by @Ghost in: [#1346](https://codeberg.org/uzu/strudel/pulls/1346) +- 2025-05-02T00:01:27+02:00 Fix web + repl package builds by @froos in: [#1339](https://codeberg.org/uzu/strudel/pulls/1339) +- 2025-05-01T23:50:57+02:00 fix: superdough worklets bundling by @froos in: [#1338](https://codeberg.org/uzu/strudel/pulls/1338) ## april 2025 -- 2025-04-27T22:25:54+02:00 FIX: sound import order by Ghost in: [#1333](https://codeberg.org/uzu/strudel/pulls/1333) -- 2025-04-27T18:39:49+02:00 update docs to reflect import sounds tab change by hpunq in: [#1332](https://codeberg.org/uzu/strudel/pulls/1332) -- 2025-04-27T18:38:26+02:00 feat: Improve gain curve by Ghost in: [#1318](https://codeberg.org/uzu/strudel/pulls/1318) -- 2025-04-27T07:54:49+02:00 Add Icon to import sample button by Ghost in: [#1331](https://codeberg.org/uzu/strudel/pulls/1331) -- 2025-04-27T07:53:41+02:00 Add new "import-sounds" tab with explanation on folder import by hpunq in: [#1329](https://codeberg.org/uzu/strudel/pulls/1329) -- 2025-04-22T00:04:38+02:00 feat: new themes + theme improvements by Ghost in: [#1326](https://codeberg.org/uzu/strudel/pulls/1326) -- 2025-04-20T18:24:15+02:00 feat: Create scrub function for scrubbing an audio file by Ghost in: [#1321](https://codeberg.org/uzu/strudel/pulls/1321) -- 2025-04-18T07:17:39+02:00 fix: disable astro toolbar by default by Ghost in: [#1324](https://codeberg.org/uzu/strudel/pulls/1324) -- 2025-04-18T07:17:38+02:00 fix: udels header by Ghost in: [#1325](https://codeberg.org/uzu/strudel/pulls/1325) -- 2025-04-11T09:18:58+02:00 Send delta in OSC message in seconds, to match tidal/superdirt by yaxu in: [#1323](https://codeberg.org/uzu/strudel/pulls/1323) -- 2025-04-08T06:08:57+02:00 FIX: Multichannel Audio by Ghost in: [#1322](https://codeberg.org/uzu/strudel/pulls/1322) -- 2025-04-08T04:18:03+02:00 feat: add max polyphony feature for superdough by Ghost in: [#1317](https://codeberg.org/uzu/strudel/pulls/1317) -- 2025-04-05T21:12:05+02:00 enhancement: make error messages easier to read by Ghost in: [#1315](https://codeberg.org/uzu/strudel/pulls/1315) -- 2025-04-04T06:26:57+02:00 fix: replace empty spaces in registered sound keys by Ghost in: [#1319](https://codeberg.org/uzu/strudel/pulls/1319) +- 2025-04-27T22:25:54+02:00 FIX: sound import order by @Ghost in: [#1333](https://codeberg.org/uzu/strudel/pulls/1333) +- 2025-04-27T18:39:49+02:00 update docs to reflect import sounds tab change by @hpunq in: [#1332](https://codeberg.org/uzu/strudel/pulls/1332) +- 2025-04-27T18:38:26+02:00 feat: Improve gain curve by @Ghost in: [#1318](https://codeberg.org/uzu/strudel/pulls/1318) +- 2025-04-27T07:54:49+02:00 Add Icon to import sample button by @Ghost in: [#1331](https://codeberg.org/uzu/strudel/pulls/1331) +- 2025-04-27T07:53:41+02:00 Add new "import-sounds" tab with explanation on folder import by @hpunq in: [#1329](https://codeberg.org/uzu/strudel/pulls/1329) +- 2025-04-22T00:04:38+02:00 feat: new themes + theme improvements by @Ghost in: [#1326](https://codeberg.org/uzu/strudel/pulls/1326) +- 2025-04-20T18:24:15+02:00 feat: Create scrub function for scrubbing an audio file by @Ghost in: [#1321](https://codeberg.org/uzu/strudel/pulls/1321) +- 2025-04-18T07:17:39+02:00 fix: disable astro toolbar by default by @Ghost in: [#1324](https://codeberg.org/uzu/strudel/pulls/1324) +- 2025-04-18T07:17:38+02:00 fix: udels header by @Ghost in: [#1325](https://codeberg.org/uzu/strudel/pulls/1325) +- 2025-04-11T09:18:58+02:00 Send delta in OSC message in seconds, to match tidal/superdirt by @yaxu in: [#1323](https://codeberg.org/uzu/strudel/pulls/1323) +- 2025-04-08T06:08:57+02:00 FIX: Multichannel Audio by @Ghost in: [#1322](https://codeberg.org/uzu/strudel/pulls/1322) +- 2025-04-08T04:18:03+02:00 feat: add max polyphony feature for superdough by @Ghost in: [#1317](https://codeberg.org/uzu/strudel/pulls/1317) +- 2025-04-05T21:12:05+02:00 enhancement: make error messages easier to read by @Ghost in: [#1315](https://codeberg.org/uzu/strudel/pulls/1315) +- 2025-04-04T06:26:57+02:00 fix: replace empty spaces in registered sound keys by @Ghost in: [#1319](https://codeberg.org/uzu/strudel/pulls/1319) ## march 2025 -- 2025-03-26T17:01:05+01:00 small feat: Add alias for segment and ribbon by Ghost in: [#1314](https://codeberg.org/uzu/strudel/pulls/1314) -- 2025-03-26T14:54:18+01:00 Fix typo pattnr by Ghost in: [#1316](https://codeberg.org/uzu/strudel/pulls/1316) -- 2025-03-23T20:05:31+01:00 bugfix: Allow single param to be used in the as function by Ghost in: [#1312](https://codeberg.org/uzu/strudel/pulls/1312) -- 2025-03-20T23:35:18+01:00 Add MIDI Program Change, SysEx, NRPN, PitchBend and AfterTouch Output by nkymut in: [#1244](https://codeberg.org/uzu/strudel/pulls/1244) -- 2025-03-19T17:32:30+01:00 make soundfont base url configurable by Ghost in: [#1040](https://codeberg.org/uzu/strudel/pulls/1040) -- 2025-03-18T20:07:08+01:00 Add num samples from 0 up to 20 by yaxu in: [#1310](https://codeberg.org/uzu/strudel/pulls/1310) -- 2025-03-17T10:38:54+01:00 add num samples (edited numbers) by yaxu in: [#1309](https://codeberg.org/uzu/strudel/pulls/1309) -- 2025-03-08T22:17:47+01:00 @strudel/sampler improvements by froos in: [#1288](https://codeberg.org/uzu/strudel/pulls/1288) -- 2025-03-08T21:56:50+01:00 Add Gamepad module by nkymut in: [#1223](https://codeberg.org/uzu/strudel/pulls/1223) -- 2025-03-04T19:17:37+01:00 change behaviour of polymeter, and remove polymeterSteps by yaxu in: [#1302](https://codeberg.org/uzu/strudel/pulls/1302) -- 2025-03-04T03:36:57+01:00 feat: Create Pulse Oscillator with variable PWM by Ghost in: [#1304](https://codeberg.org/uzu/strudel/pulls/1304) -- 2025-03-02T17:08:31+01:00 feat: Theme improvements by Ghost in: [#1295](https://codeberg.org/uzu/strudel/pulls/1295) -- 2025-03-02T11:58:46+01:00 bugfix zoom stepcount by yaxu in: [#1301](https://codeberg.org/uzu/strudel/pulls/1301) +- 2025-03-26T17:01:05+01:00 small feat: Add alias for segment and ribbon by @Ghost in: [#1314](https://codeberg.org/uzu/strudel/pulls/1314) +- 2025-03-26T14:54:18+01:00 Fix typo pattnr by @Ghost in: [#1316](https://codeberg.org/uzu/strudel/pulls/1316) +- 2025-03-23T20:05:31+01:00 bugfix: Allow single param to be used in the as function by @Ghost in: [#1312](https://codeberg.org/uzu/strudel/pulls/1312) +- 2025-03-20T23:35:18+01:00 Add MIDI Program Change, SysEx, NRPN, PitchBend and AfterTouch Output by @nkymut in: [#1244](https://codeberg.org/uzu/strudel/pulls/1244) +- 2025-03-19T17:32:30+01:00 make soundfont base url configurable by @Ghost in: [#1040](https://codeberg.org/uzu/strudel/pulls/1040) +- 2025-03-18T20:07:08+01:00 Add num samples from 0 up to 20 by @yaxu in: [#1310](https://codeberg.org/uzu/strudel/pulls/1310) +- 2025-03-17T10:38:54+01:00 add num samples (edited numbers) by @yaxu in: [#1309](https://codeberg.org/uzu/strudel/pulls/1309) +- 2025-03-08T22:17:47+01:00 @strudel/sampler improvements by @froos in: [#1288](https://codeberg.org/uzu/strudel/pulls/1288) +- 2025-03-08T21:56:50+01:00 Add Gamepad module by @nkymut in: [#1223](https://codeberg.org/uzu/strudel/pulls/1223) +- 2025-03-04T19:17:37+01:00 change behaviour of polymeter, and remove polymeterSteps by @yaxu in: [#1302](https://codeberg.org/uzu/strudel/pulls/1302) +- 2025-03-04T03:36:57+01:00 feat: Create Pulse Oscillator with variable PWM by @Ghost in: [#1304](https://codeberg.org/uzu/strudel/pulls/1304) +- 2025-03-02T17:08:31+01:00 feat: Theme improvements by @Ghost in: [#1295](https://codeberg.org/uzu/strudel/pulls/1295) +- 2025-03-02T11:58:46+01:00 bugfix zoom stepcount by @yaxu in: [#1301](https://codeberg.org/uzu/strudel/pulls/1301) ## february 2025 -- 2025-02-28T16:01:20+01:00 Fix test error #1297 by nkymut in: [#1298](https://codeberg.org/uzu/strudel/pulls/1298) -- 2025-02-28T02:53:42+01:00 Hotfix: prevent undefined pattern code from crashing strudel on load by Ghost in: [#1297](https://codeberg.org/uzu/strudel/pulls/1297) -- 2025-02-27T08:24:58+01:00 Fix misplaced ending sentence by Ghost in: [#1296](https://codeberg.org/uzu/strudel/pulls/1296) -- 2025-02-23T10:54:13+01:00 Signpost licenses for source code and samples a bit more, ref #1277 by yaxu in: [#1289](https://codeberg.org/uzu/strudel/pulls/1289) -- 2025-02-23T10:52:27+01:00 Allow wchooseCycles probabilities to be patterned by yaxu in: [#1292](https://codeberg.org/uzu/strudel/pulls/1292) -- 2025-02-22T02:18:59+01:00 Create Pattern Page Pagination by Ghost in: [#1287](https://codeberg.org/uzu/strudel/pulls/1287) -- 2025-02-21T22:38:10+01:00 showcase tweaks by yaxu in: [#1291](https://codeberg.org/uzu/strudel/pulls/1291) -- 2025-02-11T16:34:24+01:00 Fixes inverted triangle wave by renaming it to `itri`, making non-inverted `tri` by yaxu in: [#1283](https://codeberg.org/uzu/strudel/pulls/1283) -- 2025-02-11T10:02:34+01:00 Fix `squeezejoin` and functions using it, including `bite` by yaxu in: [#1286](https://codeberg.org/uzu/strudel/pulls/1286) -- 2025-02-09T16:18:28+01:00 Rename repeat back to extend by yaxu in: [#1285](https://codeberg.org/uzu/strudel/pulls/1285) -- 2025-02-07T18:11:52+01:00 mqtt bugfix - connection check by yaxu in: [#1282](https://codeberg.org/uzu/strudel/pulls/1282) -- 2025-02-07T17:38:29+01:00 Bugfix: update mqtt connections dictionary by yaxu in: [#1281](https://codeberg.org/uzu/strudel/pulls/1281) -- 2025-02-07T11:39:54+01:00 make mqtt topic patternable by yaxu in: [#1280](https://codeberg.org/uzu/strudel/pulls/1280) -- 2025-02-07T11:07:32+01:00 midimaps by froos in: [#1274](https://codeberg.org/uzu/strudel/pulls/1274) -- 2025-02-06T15:59:03+01:00 MQTT - support adding hap duration and cps metadata to JSON messages by yaxu in: [#1279](https://codeberg.org/uzu/strudel/pulls/1279) -- 2025-02-05T16:10:54+01:00 [breaking change] Sample signals from query onset, rather than midpoint by yaxu in: [#1278](https://codeberg.org/uzu/strudel/pulls/1278) -- 2025-02-03T00:55:36+01:00 Stepwise documentation tweaks, with mridangam samples by yaxu in: [#1275](https://codeberg.org/uzu/strudel/pulls/1275) -- 2025-02-02T21:26:44+01:00 Polish, rename, and document stepwise functions by yaxu in: [#1262](https://codeberg.org/uzu/strudel/pulls/1262) -- 2025-02-01T22:57:00+01:00 Fix sf2 timing by froos in: [#1272](https://codeberg.org/uzu/strudel/pulls/1272) -- 2025-02-01T22:32:59+01:00 Add bank aliasing and case insensitivity by TodePond in: [#1245](https://codeberg.org/uzu/strudel/pulls/1245) +- 2025-02-28T16:01:20+01:00 Fix test error #1297 by @nkymut in: [#1298](https://codeberg.org/uzu/strudel/pulls/1298) +- 2025-02-28T02:53:42+01:00 Hotfix: prevent undefined pattern code from crashing strudel on load by @Ghost in: [#1297](https://codeberg.org/uzu/strudel/pulls/1297) +- 2025-02-27T08:24:58+01:00 Fix misplaced ending sentence by @Ghost in: [#1296](https://codeberg.org/uzu/strudel/pulls/1296) +- 2025-02-23T10:54:13+01:00 Signpost licenses for source code and samples a bit more, ref #1277 by @yaxu in: [#1289](https://codeberg.org/uzu/strudel/pulls/1289) +- 2025-02-23T10:52:27+01:00 Allow wchooseCycles probabilities to be patterned by @yaxu in: [#1292](https://codeberg.org/uzu/strudel/pulls/1292) +- 2025-02-22T02:18:59+01:00 Create Pattern Page Pagination by @Ghost in: [#1287](https://codeberg.org/uzu/strudel/pulls/1287) +- 2025-02-21T22:38:10+01:00 showcase tweaks by @yaxu in: [#1291](https://codeberg.org/uzu/strudel/pulls/1291) +- 2025-02-11T16:34:24+01:00 Fixes inverted triangle wave by renaming it to `itri`, making non-inverted `tri` by @yaxu in: [#1283](https://codeberg.org/uzu/strudel/pulls/1283) +- 2025-02-11T10:02:34+01:00 Fix `squeezejoin` and functions using it, including `bite` by @yaxu in: [#1286](https://codeberg.org/uzu/strudel/pulls/1286) +- 2025-02-09T16:18:28+01:00 Rename repeat back to extend by @yaxu in: [#1285](https://codeberg.org/uzu/strudel/pulls/1285) +- 2025-02-07T18:11:52+01:00 mqtt bugfix - connection check by @yaxu in: [#1282](https://codeberg.org/uzu/strudel/pulls/1282) +- 2025-02-07T17:38:29+01:00 Bugfix: update mqtt connections dictionary by @yaxu in: [#1281](https://codeberg.org/uzu/strudel/pulls/1281) +- 2025-02-07T11:39:54+01:00 make mqtt topic patternable by @yaxu in: [#1280](https://codeberg.org/uzu/strudel/pulls/1280) +- 2025-02-07T11:07:32+01:00 midimaps by @froos in: [#1274](https://codeberg.org/uzu/strudel/pulls/1274) +- 2025-02-06T15:59:03+01:00 MQTT - support adding hap duration and cps metadata to JSON messages by @yaxu in: [#1279](https://codeberg.org/uzu/strudel/pulls/1279) +- 2025-02-05T16:10:54+01:00 [breaking change] Sample signals from query onset, rather than midpoint by @yaxu in: [#1278](https://codeberg.org/uzu/strudel/pulls/1278) +- 2025-02-03T00:55:36+01:00 Stepwise documentation tweaks, with mridangam samples by @yaxu in: [#1275](https://codeberg.org/uzu/strudel/pulls/1275) +- 2025-02-02T21:26:44+01:00 Polish, rename, and document stepwise functions by @yaxu in: [#1262](https://codeberg.org/uzu/strudel/pulls/1262) +- 2025-02-01T22:57:00+01:00 Fix sf2 timing by @froos in: [#1272](https://codeberg.org/uzu/strudel/pulls/1272) +- 2025-02-01T22:32:59+01:00 Add bank aliasing and case insensitivity by @TodePond in: [#1245](https://codeberg.org/uzu/strudel/pulls/1245) ## january 2025 -- 2025-01-31T09:39:54+01:00 Add Device Motion module by nkymut in: [#1217](https://codeberg.org/uzu/strudel/pulls/1217) -- 2025-01-29T15:40:01+01:00 add "as" function + getControlName by froos in: [#1247](https://codeberg.org/uzu/strudel/pulls/1247) -- 2025-01-29T15:30:04+01:00 Theme glowup by froos in: [#1268](https://codeberg.org/uzu/strudel/pulls/1268) -- 2025-01-29T15:22:18+01:00 patchday by froos in: [#1264](https://codeberg.org/uzu/strudel/pulls/1264) -- 2025-01-28T00:00:03+01:00 Revert "Fix sometimes" by yaxu in: [#1267](https://codeberg.org/uzu/strudel/pulls/1267) -- 2025-01-24T15:32:48+01:00 add reference package by froos in: [#1252](https://codeberg.org/uzu/strudel/pulls/1252) -- 2025-01-24T15:16:55+01:00 support `all(pianoroll)` and `all(pianoroll({labels: true}))` by yaxu in: [#1234](https://codeberg.org/uzu/strudel/pulls/1234) -- 2025-01-24T12:13:49+01:00 MQTT - if password isn't provided, prompt for one by yaxu in: [#1249](https://codeberg.org/uzu/strudel/pulls/1249) -- 2025-01-21T06:24:03+01:00 fix docs for beat function by Ghost in: [#1248](https://codeberg.org/uzu/strudel/pulls/1248) -- 2025-01-18T23:27:51+01:00 understand voicings page by froos in: [#1230](https://codeberg.org/uzu/strudel/pulls/1230) -- 2025-01-17T19:27:00+01:00 Add binary and binaryN by Ghost in: [#1226](https://codeberg.org/uzu/strudel/pulls/1226) -- 2025-01-16T12:18:33+01:00 Fix sometimes by yaxu in: [#1243](https://codeberg.org/uzu/strudel/pulls/1243) -- 2025-01-16T05:55:46+01:00 "beat" function for "step sequencer" style rhythm notation by Ghost in: [#1237](https://codeberg.org/uzu/strudel/pulls/1237) -- 2025-01-14T14:39:15+01:00 Add stepBind, and some toplevel aliases for binds and withValue by yaxu in: [#1241](https://codeberg.org/uzu/strudel/pulls/1241) -- 2025-01-14T06:11:10+01:00 Add onKey function for custom key commands for patterns by Ghost in: [#1235](https://codeberg.org/uzu/strudel/pulls/1235) -- 2025-01-12T11:32:24+01:00 Update documentation for param value modification by Ghost in: [#1238](https://codeberg.org/uzu/strudel/pulls/1238) +- 2025-01-31T09:39:54+01:00 Add Device Motion module by @nkymut in: [#1217](https://codeberg.org/uzu/strudel/pulls/1217) +- 2025-01-29T15:40:01+01:00 add "as" function + getControlName by @froos in: [#1247](https://codeberg.org/uzu/strudel/pulls/1247) +- 2025-01-29T15:30:04+01:00 Theme glowup by @froos in: [#1268](https://codeberg.org/uzu/strudel/pulls/1268) +- 2025-01-29T15:22:18+01:00 patchday by @froos in: [#1264](https://codeberg.org/uzu/strudel/pulls/1264) +- 2025-01-28T00:00:03+01:00 Revert "Fix sometimes" by @yaxu in: [#1267](https://codeberg.org/uzu/strudel/pulls/1267) +- 2025-01-24T15:32:48+01:00 add reference package by @froos in: [#1252](https://codeberg.org/uzu/strudel/pulls/1252) +- 2025-01-24T15:16:55+01:00 support `all(pianoroll)` and `all(pianoroll({labels: true}))` by @yaxu in: [#1234](https://codeberg.org/uzu/strudel/pulls/1234) +- 2025-01-24T12:13:49+01:00 MQTT - if password isn't provided, prompt for one by @yaxu in: [#1249](https://codeberg.org/uzu/strudel/pulls/1249) +- 2025-01-21T06:24:03+01:00 fix docs for beat function by @Ghost in: [#1248](https://codeberg.org/uzu/strudel/pulls/1248) +- 2025-01-18T23:27:51+01:00 understand voicings page by @froos in: [#1230](https://codeberg.org/uzu/strudel/pulls/1230) +- 2025-01-17T19:27:00+01:00 Add binary and binaryN by @Ghost in: [#1226](https://codeberg.org/uzu/strudel/pulls/1226) +- 2025-01-16T12:18:33+01:00 Fix sometimes by @yaxu in: [#1243](https://codeberg.org/uzu/strudel/pulls/1243) +- 2025-01-16T05:55:46+01:00 "beat" function for "step sequencer" style rhythm notation by @Ghost in: [#1237](https://codeberg.org/uzu/strudel/pulls/1237) +- 2025-01-14T14:39:15+01:00 Add stepBind, and some toplevel aliases for binds and withValue by @yaxu in: [#1241](https://codeberg.org/uzu/strudel/pulls/1241) +- 2025-01-14T06:11:10+01:00 Add onKey function for custom key commands for patterns by @Ghost in: [#1235](https://codeberg.org/uzu/strudel/pulls/1235) +- 2025-01-12T11:32:24+01:00 Update documentation for param value modification by @Ghost in: [#1238](https://codeberg.org/uzu/strudel/pulls/1238) ## december 2024 -- 2024-12-29T13:16:08+01:00 Documentation for all/each, and bugfix for each by yaxu in: [#1233](https://codeberg.org/uzu/strudel/pulls/1233) -- 2024-12-29T11:29:52+01:00 Make `all()` post-stack again, and add `each()` for pre-stack by yaxu in: [#1229](https://codeberg.org/uzu/strudel/pulls/1229) -- 2024-12-27T22:16:54+01:00 suggested changes to voicings.mdx by Ghost in: [#1231](https://codeberg.org/uzu/strudel/pulls/1231) -- 2024-12-23T11:53:11+01:00 add basic spectrum function by froos in: [#1213](https://codeberg.org/uzu/strudel/pulls/1213) -- 2024-12-22T21:04:45+01:00 Fix regression for d1, p1, p(n) by yaxu in: [#1227](https://codeberg.org/uzu/strudel/pulls/1227) +- 2024-12-29T13:16:08+01:00 Documentation for all/each, and bugfix for each by @yaxu in: [#1233](https://codeberg.org/uzu/strudel/pulls/1233) +- 2024-12-29T11:29:52+01:00 Make `all()` post-stack again, and add `each()` for pre-stack by @yaxu in: [#1229](https://codeberg.org/uzu/strudel/pulls/1229) +- 2024-12-27T22:16:54+01:00 suggested changes to voicings.mdx by @Ghost in: [#1231](https://codeberg.org/uzu/strudel/pulls/1231) +- 2024-12-23T11:53:11+01:00 add basic spectrum function by @froos in: [#1213](https://codeberg.org/uzu/strudel/pulls/1213) +- 2024-12-22T21:04:45+01:00 Fix regression for d1, p1, p(n) by @yaxu in: [#1227](https://codeberg.org/uzu/strudel/pulls/1227) ## november 2024 -- 2024-11-30T10:09:36+01:00 Make cps patternable by eefano in: [#1001](https://codeberg.org/uzu/strudel/pulls/1001) -- 2024-11-30T10:07:56+01:00 MQTT support by yaxu in: [#1224](https://codeberg.org/uzu/strudel/pulls/1224) -- 2024-11-30T09:46:14+01:00 Apply `all` function to individual patterns rather than final stack by yaxu in: [#1209](https://codeberg.org/uzu/strudel/pulls/1209) -- 2024-11-20T16:32:51+01:00 REPL: solo and sync configuration by Ghost in: [#1214](https://codeberg.org/uzu/strudel/pulls/1214) +- 2024-11-30T10:09:36+01:00 Make cps patternable by @eefano in: [#1001](https://codeberg.org/uzu/strudel/pulls/1001) +- 2024-11-30T10:07:56+01:00 MQTT support by @yaxu in: [#1224](https://codeberg.org/uzu/strudel/pulls/1224) +- 2024-11-30T09:46:14+01:00 Apply `all` function to individual patterns rather than final stack by @yaxu in: [#1209](https://codeberg.org/uzu/strudel/pulls/1209) +- 2024-11-20T16:32:51+01:00 REPL: solo and sync configuration by @Ghost in: [#1214](https://codeberg.org/uzu/strudel/pulls/1214) ## october 2024 -- 2024-10-30T21:29:43+01:00 Add s_zip for 'cat'-ing patterns together step-by-step, bugfix `steps` by yaxu in: [#1208](https://codeberg.org/uzu/strudel/pulls/1208) -- 2024-10-30T21:28:32+01:00 Preserve tactus for 'degrade' and friends, and tidy up 'pick' and friends by yaxu in: [#1205](https://codeberg.org/uzu/strudel/pulls/1205) -- 2024-10-30T17:11:53+01:00 chore: Edit run locally instructions in README.md by Ghost in: [#1206](https://codeberg.org/uzu/strudel/pulls/1206) -- 2024-10-23T23:08:14+02:00 colorize console + tweak header by froos in: [#1203](https://codeberg.org/uzu/strudel/pulls/1203) -- 2024-10-22T22:55:00+02:00 markcss by froos in: [#1202](https://codeberg.org/uzu/strudel/pulls/1202) -- 2024-10-21T22:56:37+02:00 add 2 new ui settings by froos in: [#1200](https://codeberg.org/uzu/strudel/pulls/1200) -- 2024-10-21T20:22:52+02:00 Make panel hover behavior optional by Ghost in: [#1199](https://codeberg.org/uzu/strudel/pulls/1199) -- 2024-10-19T04:28:36+02:00 Menu Panel Improvements! by Ghost in: [#1193](https://codeberg.org/uzu/strudel/pulls/1193) -- 2024-10-19T01:10:06+02:00 update lockfile + minor versions by froos in: [#1198](https://codeberg.org/uzu/strudel/pulls/1198) +- 2024-10-30T21:29:43+01:00 Add s_zip for 'cat'-ing patterns together step-by-step, bugfix `steps` by @yaxu in: [#1208](https://codeberg.org/uzu/strudel/pulls/1208) +- 2024-10-30T21:28:32+01:00 Preserve tactus for 'degrade' and friends, and tidy up 'pick' and friends by @yaxu in: [#1205](https://codeberg.org/uzu/strudel/pulls/1205) +- 2024-10-30T17:11:53+01:00 chore: Edit run locally instructions in README.md by @Ghost in: [#1206](https://codeberg.org/uzu/strudel/pulls/1206) +- 2024-10-23T23:08:14+02:00 colorize console + tweak header by @froos in: [#1203](https://codeberg.org/uzu/strudel/pulls/1203) +- 2024-10-22T22:55:00+02:00 markcss by @froos in: [#1202](https://codeberg.org/uzu/strudel/pulls/1202) +- 2024-10-21T22:56:37+02:00 add 2 new ui settings by @froos in: [#1200](https://codeberg.org/uzu/strudel/pulls/1200) +- 2024-10-21T20:22:52+02:00 Make panel hover behavior optional by @Ghost in: [#1199](https://codeberg.org/uzu/strudel/pulls/1199) +- 2024-10-19T04:28:36+02:00 Menu Panel Improvements! by @Ghost in: [#1193](https://codeberg.org/uzu/strudel/pulls/1193) +- 2024-10-19T01:10:06+02:00 update lockfile + minor versions by @froos in: [#1198](https://codeberg.org/uzu/strudel/pulls/1198) ## september 2024 -- 2024-09-27T00:17:00+02:00 remove redundant example for cat, update snapshot by Ghost in: [#1189](https://codeberg.org/uzu/strudel/pulls/1189) -- 2024-09-25T13:18:19+02:00 Adding search bar (soundtab.jsx) by Ghost in: [#1185](https://codeberg.org/uzu/strudel/pulls/1185) -- 2024-09-23T17:18:34+02:00 Screenreader improvements by yaxu in: [#1158](https://codeberg.org/uzu/strudel/pulls/1158) -- 2024-09-20T22:26:41+02:00 Fix serial timing by yaxu in: [#1188](https://codeberg.org/uzu/strudel/pulls/1188) -- 2024-09-20T00:26:30+02:00 Add bite function by yaxu in: [#1187](https://codeberg.org/uzu/strudel/pulls/1187) -- 2024-09-14T13:30:53+02:00 better spacing in zen mode by froos in: [#1147](https://codeberg.org/uzu/strudel/pulls/1147) -- 2024-09-14T10:50:24+02:00 add filter + filterWhen + within by froos in: [#1039](https://codeberg.org/uzu/strudel/pulls/1039) -- 2024-09-14T10:49:21+02:00 refactor sampler by froos in: [#1101](https://codeberg.org/uzu/strudel/pulls/1101) -- 2024-09-14T10:43:17+02:00 handle midin device not found error by froos in: [#1146](https://codeberg.org/uzu/strudel/pulls/1146) -- 2024-09-13T21:59:23+02:00 Add a search bar to the REPL Reference tab by Ghost in: [#1165](https://codeberg.org/uzu/strudel/pulls/1165) -- 2024-09-13T21:58:47+02:00 Correct spelling mistakes by Ghost in: [#1183](https://codeberg.org/uzu/strudel/pulls/1183) -- 2024-09-07T23:41:29+02:00 Add seqPLoop from Tidal by yaxu in: [#1182](https://codeberg.org/uzu/strudel/pulls/1182) -- 2024-09-05T05:52:50+02:00 make phaser control match superdirt by Ghost in: [#1180](https://codeberg.org/uzu/strudel/pulls/1180) -- 2024-09-05T05:33:41+02:00 Revert "Make phaser control consistent with superdirt" by Ghost in: [#1179](https://codeberg.org/uzu/strudel/pulls/1179) -- 2024-09-05T05:29:12+02:00 Make phaser control consistent with superdirt by Ghost in: [#1178](https://codeberg.org/uzu/strudel/pulls/1178) -- 2024-09-03T04:37:15+02:00 fix sample speed when using splice and fit with superdirt by Ghost in: [#1172](https://codeberg.org/uzu/strudel/pulls/1172) -- 2024-09-01T16:02:24+02:00 Create audio target selector for OSC/Superdirt by Ghost in: [#1160](https://codeberg.org/uzu/strudel/pulls/1160) -- 2024-09-01T15:03:47+02:00 Fixes fit so it works after a chop or slice by yaxu in: [#1171](https://codeberg.org/uzu/strudel/pulls/1171) +- 2024-09-27T00:17:00+02:00 remove redundant example for cat, update snapshot by @Ghost in: [#1189](https://codeberg.org/uzu/strudel/pulls/1189) +- 2024-09-25T13:18:19+02:00 Adding search bar (soundtab.jsx) by @Ghost in: [#1185](https://codeberg.org/uzu/strudel/pulls/1185) +- 2024-09-23T17:18:34+02:00 Screenreader improvements by @yaxu in: [#1158](https://codeberg.org/uzu/strudel/pulls/1158) +- 2024-09-20T22:26:41+02:00 Fix serial timing by @yaxu in: [#1188](https://codeberg.org/uzu/strudel/pulls/1188) +- 2024-09-20T00:26:30+02:00 Add bite function by @yaxu in: [#1187](https://codeberg.org/uzu/strudel/pulls/1187) +- 2024-09-14T13:30:53+02:00 better spacing in zen mode by @froos in: [#1147](https://codeberg.org/uzu/strudel/pulls/1147) +- 2024-09-14T10:50:24+02:00 add filter + filterWhen + within by @froos in: [#1039](https://codeberg.org/uzu/strudel/pulls/1039) +- 2024-09-14T10:49:21+02:00 refactor sampler by @froos in: [#1101](https://codeberg.org/uzu/strudel/pulls/1101) +- 2024-09-14T10:43:17+02:00 handle midin device not found error by @froos in: [#1146](https://codeberg.org/uzu/strudel/pulls/1146) +- 2024-09-13T21:59:23+02:00 Add a search bar to the REPL Reference tab by @Ghost in: [#1165](https://codeberg.org/uzu/strudel/pulls/1165) +- 2024-09-13T21:58:47+02:00 Correct spelling mistakes by @Ghost in: [#1183](https://codeberg.org/uzu/strudel/pulls/1183) +- 2024-09-07T23:41:29+02:00 Add seqPLoop from Tidal by @yaxu in: [#1182](https://codeberg.org/uzu/strudel/pulls/1182) +- 2024-09-05T05:52:50+02:00 make phaser control match superdirt by @Ghost in: [#1180](https://codeberg.org/uzu/strudel/pulls/1180) +- 2024-09-05T05:33:41+02:00 Revert "Make phaser control consistent with superdirt" by @Ghost in: [#1179](https://codeberg.org/uzu/strudel/pulls/1179) +- 2024-09-05T05:29:12+02:00 Make phaser control consistent with superdirt by @Ghost in: [#1178](https://codeberg.org/uzu/strudel/pulls/1178) +- 2024-09-03T04:37:15+02:00 fix sample speed when using splice and fit with superdirt by @Ghost in: [#1172](https://codeberg.org/uzu/strudel/pulls/1172) +- 2024-09-01T16:02:24+02:00 Create audio target selector for OSC/Superdirt by @Ghost in: [#1160](https://codeberg.org/uzu/strudel/pulls/1160) +- 2024-09-01T15:03:47+02:00 Fixes fit so it works after a chop or slice by @yaxu in: [#1171](https://codeberg.org/uzu/strudel/pulls/1171) ## august 2024 -- 2024-08-30T14:24:08+02:00 polyJoin by yaxu in: [#1168](https://codeberg.org/uzu/strudel/pulls/1168) -- 2024-08-23T17:05:10+02:00 Add scramble and shuffle by yaxu in: [#1167](https://codeberg.org/uzu/strudel/pulls/1167) -- 2024-08-18T18:22:20+02:00 Improve + simplify neocyclist timing by Ghost in: [#1164](https://codeberg.org/uzu/strudel/pulls/1164) -- 2024-08-15T05:18:33+02:00 containerize/seperate out boolean checks for repl types/Repl logic into bespoke components. by Ghost in: [#1163](https://codeberg.org/uzu/strudel/pulls/1163) -- 2024-08-12T18:57:21+02:00 [CORS HOTFIX] by Ghost in: [#1162](https://codeberg.org/uzu/strudel/pulls/1162) -- 2024-08-09T05:11:28+02:00 Fix OSC clock jitter by Ghost in: [#1157](https://codeberg.org/uzu/strudel/pulls/1157) +- 2024-08-30T14:24:08+02:00 polyJoin by @yaxu in: [#1168](https://codeberg.org/uzu/strudel/pulls/1168) +- 2024-08-23T17:05:10+02:00 Add scramble and shuffle by @yaxu in: [#1167](https://codeberg.org/uzu/strudel/pulls/1167) +- 2024-08-18T18:22:20+02:00 Improve + simplify neocyclist timing by @Ghost in: [#1164](https://codeberg.org/uzu/strudel/pulls/1164) +- 2024-08-15T05:18:33+02:00 containerize/seperate out boolean checks for repl types/Repl logic into bespoke components. by @Ghost in: [#1163](https://codeberg.org/uzu/strudel/pulls/1163) +- 2024-08-12T18:57:21+02:00 [CORS HOTFIX] by @Ghost in: [#1162](https://codeberg.org/uzu/strudel/pulls/1162) +- 2024-08-09T05:11:28+02:00 Fix OSC clock jitter by @Ghost in: [#1157](https://codeberg.org/uzu/strudel/pulls/1157) ## july 2024 -- 2024-07-27T11:02:38+02:00 Fix loopAt tactus by yaxu in: [#1145](https://codeberg.org/uzu/strudel/pulls/1145) -- 2024-07-26T04:37:34+02:00 "stretch" function (phase vocoder) by Ghost in: [#1130](https://codeberg.org/uzu/strudel/pulls/1130) -- 2024-07-26T04:37:17+02:00 Udels (MultiFrame Strudel) Revisited by Ghost in: [#1132](https://codeberg.org/uzu/strudel/pulls/1132) -- 2024-07-24T11:40:28+02:00 Fix tactus marking in mininotation by yaxu in: [#1144](https://codeberg.org/uzu/strudel/pulls/1144) +- 2024-07-27T11:02:38+02:00 Fix loopAt tactus by @yaxu in: [#1145](https://codeberg.org/uzu/strudel/pulls/1145) +- 2024-07-26T04:37:34+02:00 "stretch" function (phase vocoder) by @Ghost in: [#1130](https://codeberg.org/uzu/strudel/pulls/1130) +- 2024-07-26T04:37:17+02:00 Udels (MultiFrame Strudel) Revisited by @Ghost in: [#1132](https://codeberg.org/uzu/strudel/pulls/1132) +- 2024-07-24T11:40:28+02:00 Fix tactus marking in mininotation by @yaxu in: [#1144](https://codeberg.org/uzu/strudel/pulls/1144) ## june 2024 -- 2024-06-25T18:13:28+02:00 export comment commands by froos in: [#1136](https://codeberg.org/uzu/strudel/pulls/1136) -- 2024-06-24T18:19:22+02:00 Chop chop by yaxu in: [#1078](https://codeberg.org/uzu/strudel/pulls/1078) -- 2024-06-18T23:58:08+02:00 Fix bug in Fraction.lcm by yaxu in: [#1133](https://codeberg.org/uzu/strudel/pulls/1133) -- 2024-06-18T05:32:12+02:00 Fix clock worker dependency path in module builds by Ghost in: [#1129](https://codeberg.org/uzu/strudel/pulls/1129) -- 2024-06-04T00:26:48+02:00 Labeled statements doc by froos in: [#1126](https://codeberg.org/uzu/strudel/pulls/1126) -- 2024-06-03T22:40:21+02:00 doc: visual functions + refactor onPaint by froos in: [#1125](https://codeberg.org/uzu/strudel/pulls/1125) -- 2024-06-02T18:36:29+02:00 Fix indexDB failing with large amount of files by Ghost in: [#1124](https://codeberg.org/uzu/strudel/pulls/1124) +- 2024-06-25T18:13:28+02:00 export comment commands by @froos in: [#1136](https://codeberg.org/uzu/strudel/pulls/1136) +- 2024-06-24T18:19:22+02:00 Chop chop by @yaxu in: [#1078](https://codeberg.org/uzu/strudel/pulls/1078) +- 2024-06-18T23:58:08+02:00 Fix bug in Fraction.lcm by @yaxu in: [#1133](https://codeberg.org/uzu/strudel/pulls/1133) +- 2024-06-18T05:32:12+02:00 Fix clock worker dependency path in module builds by @Ghost in: [#1129](https://codeberg.org/uzu/strudel/pulls/1129) +- 2024-06-04T00:26:48+02:00 Labeled statements doc by @froos in: [#1126](https://codeberg.org/uzu/strudel/pulls/1126) +- 2024-06-03T22:40:21+02:00 doc: visual functions + refactor onPaint by @froos in: [#1125](https://codeberg.org/uzu/strudel/pulls/1125) +- 2024-06-02T18:36:29+02:00 Fix indexDB failing with large amount of files by @Ghost in: [#1124](https://codeberg.org/uzu/strudel/pulls/1124) ## may 2024 -- 2024-05-31T12:17:34+02:00 Migrate tutorial fanchor by froos in: [#1122](https://codeberg.org/uzu/strudel/pulls/1122) -- 2024-05-31T10:46:47+02:00 [BUG FIX] Audio worklets sometimes dont load by Ghost in: [#1121](https://codeberg.org/uzu/strudel/pulls/1121) -- 2024-05-31T10:25:24+02:00 change fanchor to 0 by Ghost in: [#1107](https://codeberg.org/uzu/strudel/pulls/1107) -- 2024-05-30T14:39:30+02:00 fix: use full repl in web package by froos in: [#1119](https://codeberg.org/uzu/strudel/pulls/1119) -- 2024-05-30T09:36:32+02:00 can now access strudelMirror from repl by froos in: [#1117](https://codeberg.org/uzu/strudel/pulls/1117) -- 2024-05-29T13:06:34+02:00 Add the mousex and mousey signal by Ghost in: [#1112](https://codeberg.org/uzu/strudel/pulls/1112) -- 2024-05-29T13:02:00+02:00 Fixes drawPianoroll import in codemirror example by Ghost in: [#1116](https://codeberg.org/uzu/strudel/pulls/1116) -- 2024-05-28T22:43:46+02:00 clarify `off` in pattern-effects.mdx by Ghost in: [#1074](https://codeberg.org/uzu/strudel/pulls/1074) -- 2024-05-26T17:33:07+02:00 rollback phaser by Ghost in: [#1113](https://codeberg.org/uzu/strudel/pulls/1113) -- 2024-05-26T17:33:06+02:00 Fix audio worklets by Ghost in: [#1114](https://codeberg.org/uzu/strudel/pulls/1114) -- 2024-05-26T16:53:56+02:00 Calculate phaser modulation phase based on time by Ghost in: [#1110](https://codeberg.org/uzu/strudel/pulls/1110) -- 2024-05-23T15:06:01+02:00 Add analog-style ladder filter by Ghost in: [#1103](https://codeberg.org/uzu/strudel/pulls/1103) -- 2024-05-22T12:53:20+02:00 fix sampler on windows by Ghost in: [#1109](https://codeberg.org/uzu/strudel/pulls/1109) -- 2024-05-20T23:26:20+02:00 web package fixes by froos in: [#1044](https://codeberg.org/uzu/strudel/pulls/1044) -- 2024-05-20T22:41:28+02:00 Fix sampler windows by froos in: [#1108](https://codeberg.org/uzu/strudel/pulls/1108) -- 2024-05-19T12:07:26+02:00 hs2js package / tidal parser by froos in: [#870](https://codeberg.org/uzu/strudel/pulls/870) -- 2024-05-18T10:49:08+02:00 fix little dub tune example by Ghost in: [#1104](https://codeberg.org/uzu/strudel/pulls/1104) -- 2024-05-17T14:43:58+02:00 Samples tab improvements by Ghost in: [#1102](https://codeberg.org/uzu/strudel/pulls/1102) -- 2024-05-13T10:10:34+02:00 osc: couple of fixes by Ghost in: [#1093](https://codeberg.org/uzu/strudel/pulls/1093) -- 2024-05-13T06:31:20+02:00 Use sessionStorage for viewingPatternData and activePattern by Ghost in: [#1091](https://codeberg.org/uzu/strudel/pulls/1091) -- 2024-05-12T18:23:09+02:00 repl: set document.title from @title by Ghost in: [#1090](https://codeberg.org/uzu/strudel/pulls/1090) -- 2024-05-07T14:29:22+02:00 fix: missing events due to premature worklet cleanup by froos in: [#1089](https://codeberg.org/uzu/strudel/pulls/1089) -- 2024-05-03T11:52:57+02:00 Benchmarks by yaxu in: [#1079](https://codeberg.org/uzu/strudel/pulls/1079) -- 2024-05-03T08:46:52+02:00 fix: csound + dough timing by froos in: [#1086](https://codeberg.org/uzu/strudel/pulls/1086) -- 2024-05-03T00:37:32+02:00 Improve performance of ! (replicate) by yaxu in: [#1084](https://codeberg.org/uzu/strudel/pulls/1084) -- 2024-05-02T23:40:22+02:00 fix: url parsing with extra params by froos in: [#1083](https://codeberg.org/uzu/strudel/pulls/1083) +- 2024-05-31T12:17:34+02:00 Migrate tutorial fanchor by @froos in: [#1122](https://codeberg.org/uzu/strudel/pulls/1122) +- 2024-05-31T10:46:47+02:00 [BUG FIX] Audio worklets sometimes dont load by @Ghost in: [#1121](https://codeberg.org/uzu/strudel/pulls/1121) +- 2024-05-31T10:25:24+02:00 change fanchor to 0 by @Ghost in: [#1107](https://codeberg.org/uzu/strudel/pulls/1107) +- 2024-05-30T14:39:30+02:00 fix: use full repl in web package by @froos in: [#1119](https://codeberg.org/uzu/strudel/pulls/1119) +- 2024-05-30T09:36:32+02:00 can now access strudelMirror from repl by @froos in: [#1117](https://codeberg.org/uzu/strudel/pulls/1117) +- 2024-05-29T13:06:34+02:00 Add the mousex and mousey signal by @Ghost in: [#1112](https://codeberg.org/uzu/strudel/pulls/1112) +- 2024-05-29T13:02:00+02:00 Fixes drawPianoroll import in codemirror example by @Ghost in: [#1116](https://codeberg.org/uzu/strudel/pulls/1116) +- 2024-05-28T22:43:46+02:00 clarify `off` in pattern-effects.mdx by @Ghost in: [#1074](https://codeberg.org/uzu/strudel/pulls/1074) +- 2024-05-26T17:33:07+02:00 rollback phaser by @Ghost in: [#1113](https://codeberg.org/uzu/strudel/pulls/1113) +- 2024-05-26T17:33:06+02:00 Fix audio worklets by @Ghost in: [#1114](https://codeberg.org/uzu/strudel/pulls/1114) +- 2024-05-26T16:53:56+02:00 Calculate phaser modulation phase based on time by @Ghost in: [#1110](https://codeberg.org/uzu/strudel/pulls/1110) +- 2024-05-23T15:06:01+02:00 Add analog-style ladder filter by @Ghost in: [#1103](https://codeberg.org/uzu/strudel/pulls/1103) +- 2024-05-22T12:53:20+02:00 fix sampler on windows by @Ghost in: [#1109](https://codeberg.org/uzu/strudel/pulls/1109) +- 2024-05-20T23:26:20+02:00 web package fixes by @froos in: [#1044](https://codeberg.org/uzu/strudel/pulls/1044) +- 2024-05-20T22:41:28+02:00 Fix sampler windows by @froos in: [#1108](https://codeberg.org/uzu/strudel/pulls/1108) +- 2024-05-19T12:07:26+02:00 hs2js package / tidal parser by @froos in: [#870](https://codeberg.org/uzu/strudel/pulls/870) +- 2024-05-18T10:49:08+02:00 fix little dub tune example by @Ghost in: [#1104](https://codeberg.org/uzu/strudel/pulls/1104) +- 2024-05-17T14:43:58+02:00 Samples tab improvements by @Ghost in: [#1102](https://codeberg.org/uzu/strudel/pulls/1102) +- 2024-05-13T10:10:34+02:00 osc: couple of fixes by @Ghost in: [#1093](https://codeberg.org/uzu/strudel/pulls/1093) +- 2024-05-13T06:31:20+02:00 Use sessionStorage for viewingPatternData and activePattern by @Ghost in: [#1091](https://codeberg.org/uzu/strudel/pulls/1091) +- 2024-05-12T18:23:09+02:00 repl: set document.title from @title by @Ghost in: [#1090](https://codeberg.org/uzu/strudel/pulls/1090) +- 2024-05-07T14:29:22+02:00 fix: missing events due to premature worklet cleanup by @froos in: [#1089](https://codeberg.org/uzu/strudel/pulls/1089) +- 2024-05-03T11:52:57+02:00 Benchmarks by @yaxu in: [#1079](https://codeberg.org/uzu/strudel/pulls/1079) +- 2024-05-03T08:46:52+02:00 fix: csound + dough timing by @froos in: [#1086](https://codeberg.org/uzu/strudel/pulls/1086) +- 2024-05-03T00:37:32+02:00 Improve performance of ! (replicate) by @yaxu in: [#1084](https://codeberg.org/uzu/strudel/pulls/1084) +- 2024-05-02T23:40:22+02:00 fix: url parsing with extra params by @froos in: [#1083](https://codeberg.org/uzu/strudel/pulls/1083) ## april 2024 -- 2024-04-29T12:36:11+02:00 Tactus calculation toggle and breaking change to tactus calculation in fast/slow/hurry by yaxu in: [#1081](https://codeberg.org/uzu/strudel/pulls/1081) -- 2024-04-28T20:56:21+02:00 fix docs on alignment.mdx by Ghost in: [#1076](https://codeberg.org/uzu/strudel/pulls/1076) -- 2024-04-28T20:49:18+02:00 add signals to recap in first-effects.mdx by Ghost in: [#1073](https://codeberg.org/uzu/strudel/pulls/1073) -- 2024-04-28T20:44:57+02:00 fix translation issue in first-effects.mdx by Ghost in: [#1072](https://codeberg.org/uzu/strudel/pulls/1072) -- 2024-04-28T20:44:29+02:00 fix failing format test by Ghost in: [#1077](https://codeberg.org/uzu/strudel/pulls/1077) -- 2024-04-27T22:50:17+02:00 add nesting to `off` example variation in pattern-effects.mdx by Ghost in: [#1075](https://codeberg.org/uzu/strudel/pulls/1075) -- 2024-04-27T22:48:58+02:00 add `<...>` to first-sounds.mdx recap by Ghost in: [#1070](https://codeberg.org/uzu/strudel/pulls/1070) -- 2024-04-27T22:48:07+02:00 fix first sounds typo by Ghost in: [#1069](https://codeberg.org/uzu/strudel/pulls/1069) -- 2024-04-27T22:47:44+02:00 fix cr typo on first-sounds.mdx by Ghost in: [#1068](https://codeberg.org/uzu/strudel/pulls/1068) -- 2024-04-26T15:12:30+02:00 More tactus tidying by yaxu in: [#1071](https://codeberg.org/uzu/strudel/pulls/1071) -- 2024-04-23T23:37:21+02:00 Fix stepjoin by yaxu in: [#1067](https://codeberg.org/uzu/strudel/pulls/1067) -- 2024-04-23T23:32:00+02:00 clarify license by yaxu in: [#1064](https://codeberg.org/uzu/strudel/pulls/1064) -- 2024-04-23T15:14:30+02:00 Tactus tweaks - fixes for maintaining tactus and highlight locations by yaxu in: [#1065](https://codeberg.org/uzu/strudel/pulls/1065) -- 2024-04-21T23:57:57+02:00 fix OSC timing for recent scheduler updates by Ghost in: [#1062](https://codeberg.org/uzu/strudel/pulls/1062) -- 2024-04-21T22:17:07+02:00 Stepwise functions from Tidal by yaxu in: [#1060](https://codeberg.org/uzu/strudel/pulls/1060) -- 2024-04-21T11:03:55+02:00 Fix wchooseCycles not picking the whole pattern by Ghost in: [#1061](https://codeberg.org/uzu/strudel/pulls/1061) -- 2024-04-19T00:05:52+02:00 add swing + swingBy by froos in: [#1038](https://codeberg.org/uzu/strudel/pulls/1038) -- 2024-04-19T00:05:08+02:00 anonymous patterns + muting by froos in: [#1059](https://codeberg.org/uzu/strudel/pulls/1059) -- 2024-04-12T12:34:27+02:00 improve tutorial + custom samples doc by froos in: [#1053](https://codeberg.org/uzu/strudel/pulls/1053) -- 2024-04-12T12:31:49+02:00 fix: do not reset cc input values on each eval by froos in: [#1054](https://codeberg.org/uzu/strudel/pulls/1054) -- 2024-04-08T17:25:27+02:00 transpose: support all combinations of numbers and strings for notes and intervals by froos in: [#1048](https://codeberg.org/uzu/strudel/pulls/1048) -- 2024-04-08T10:46:18+02:00 Wax, wane, taper and taperlist by yaxu in: [#1042](https://codeberg.org/uzu/strudel/pulls/1042) -- 2024-04-06T23:44:57+02:00 Midi Time hotfix for scheduler updates by Ghost in: [#1047](https://codeberg.org/uzu/strudel/pulls/1047) -- 2024-04-05T12:48:03+02:00 pitchwheel visual by froos in: [#1041](https://codeberg.org/uzu/strudel/pulls/1041) -- 2024-04-05T12:47:19+02:00 fix cyclist fizzling out by froos in: [#1046](https://codeberg.org/uzu/strudel/pulls/1046) +- 2024-04-29T12:36:11+02:00 Tactus calculation toggle and breaking change to tactus calculation in fast/slow/hurry by @yaxu in: [#1081](https://codeberg.org/uzu/strudel/pulls/1081) +- 2024-04-28T20:56:21+02:00 fix docs on alignment.mdx by @Ghost in: [#1076](https://codeberg.org/uzu/strudel/pulls/1076) +- 2024-04-28T20:49:18+02:00 add signals to recap in first-effects.mdx by @Ghost in: [#1073](https://codeberg.org/uzu/strudel/pulls/1073) +- 2024-04-28T20:44:57+02:00 fix translation issue in first-effects.mdx by @Ghost in: [#1072](https://codeberg.org/uzu/strudel/pulls/1072) +- 2024-04-28T20:44:29+02:00 fix failing format test by @Ghost in: [#1077](https://codeberg.org/uzu/strudel/pulls/1077) +- 2024-04-27T22:50:17+02:00 add nesting to `off` example variation in pattern-effects.mdx by @Ghost in: [#1075](https://codeberg.org/uzu/strudel/pulls/1075) +- 2024-04-27T22:48:58+02:00 add `<...>` to first-sounds.mdx recap by @Ghost in: [#1070](https://codeberg.org/uzu/strudel/pulls/1070) +- 2024-04-27T22:48:07+02:00 fix first sounds typo by @Ghost in: [#1069](https://codeberg.org/uzu/strudel/pulls/1069) +- 2024-04-27T22:47:44+02:00 fix cr typo on first-sounds.mdx by @Ghost in: [#1068](https://codeberg.org/uzu/strudel/pulls/1068) +- 2024-04-26T15:12:30+02:00 More tactus tidying by @yaxu in: [#1071](https://codeberg.org/uzu/strudel/pulls/1071) +- 2024-04-23T23:37:21+02:00 Fix stepjoin by @yaxu in: [#1067](https://codeberg.org/uzu/strudel/pulls/1067) +- 2024-04-23T23:32:00+02:00 clarify license by @yaxu in: [#1064](https://codeberg.org/uzu/strudel/pulls/1064) +- 2024-04-23T15:14:30+02:00 Tactus tweaks - fixes for maintaining tactus and highlight locations by @yaxu in: [#1065](https://codeberg.org/uzu/strudel/pulls/1065) +- 2024-04-21T23:57:57+02:00 fix OSC timing for recent scheduler updates by @Ghost in: [#1062](https://codeberg.org/uzu/strudel/pulls/1062) +- 2024-04-21T22:17:07+02:00 Stepwise functions from Tidal by @yaxu in: [#1060](https://codeberg.org/uzu/strudel/pulls/1060) +- 2024-04-21T11:03:55+02:00 Fix wchooseCycles not picking the whole pattern by @Ghost in: [#1061](https://codeberg.org/uzu/strudel/pulls/1061) +- 2024-04-19T00:05:52+02:00 add swing + swingBy by @froos in: [#1038](https://codeberg.org/uzu/strudel/pulls/1038) +- 2024-04-19T00:05:08+02:00 anonymous patterns + muting by @froos in: [#1059](https://codeberg.org/uzu/strudel/pulls/1059) +- 2024-04-12T12:34:27+02:00 improve tutorial + custom samples doc by @froos in: [#1053](https://codeberg.org/uzu/strudel/pulls/1053) +- 2024-04-12T12:31:49+02:00 fix: do not reset cc input values on each eval by @froos in: [#1054](https://codeberg.org/uzu/strudel/pulls/1054) +- 2024-04-08T17:25:27+02:00 transpose: support all combinations of numbers and strings for notes and intervals by @froos in: [#1048](https://codeberg.org/uzu/strudel/pulls/1048) +- 2024-04-08T10:46:18+02:00 Wax, wane, taper and taperlist by @yaxu in: [#1042](https://codeberg.org/uzu/strudel/pulls/1042) +- 2024-04-06T23:44:57+02:00 Midi Time hotfix for scheduler updates by @Ghost in: [#1047](https://codeberg.org/uzu/strudel/pulls/1047) +- 2024-04-05T12:48:03+02:00 pitchwheel visual by @froos in: [#1041](https://codeberg.org/uzu/strudel/pulls/1041) +- 2024-04-05T12:47:19+02:00 fix cyclist fizzling out by @froos in: [#1046](https://codeberg.org/uzu/strudel/pulls/1046) ## march 2024 -- 2024-03-30T16:05:59+01:00 remove dangerous arithmetic feature by froos in: [#1030](https://codeberg.org/uzu/strudel/pulls/1030) -- 2024-03-30T16:05:27+01:00 Fix sampler paths by froos in: [#1034](https://codeberg.org/uzu/strudel/pulls/1034) -- 2024-03-30T14:43:08+01:00 local sample server cli by froos in: [#1033](https://codeberg.org/uzu/strudel/pulls/1033) -- 2024-03-29T17:14:28+01:00 add font file types to offline cache by froos in: [#1032](https://codeberg.org/uzu/strudel/pulls/1032) -- 2024-03-29T17:10:16+01:00 add closeBrackets setting by froos in: [#1031](https://codeberg.org/uzu/strudel/pulls/1031) -- 2024-03-29T14:55:06+01:00 Tactus tidy by yaxu in: [#1027](https://codeberg.org/uzu/strudel/pulls/1027) -- 2024-03-28T17:06:44+01:00 add setting for sync flag by froos in: [#1025](https://codeberg.org/uzu/strudel/pulls/1025) -- 2024-03-28T11:37:57+01:00 better theme integration for visuals + various fixes by froos in: [#1024](https://codeberg.org/uzu/strudel/pulls/1024) -- 2024-03-28T11:33:25+01:00 More fonts by froos in: [#1023](https://codeberg.org/uzu/strudel/pulls/1023) -- 2024-03-27T13:06:05+01:00 Feature: tactus marking by yaxu in: [#1021](https://codeberg.org/uzu/strudel/pulls/1021) -- 2024-03-25T06:02:02+01:00 hotfix for 1017 by Ghost in: [#1020](https://codeberg.org/uzu/strudel/pulls/1020) -- 2024-03-24T15:06:33+01:00 Document signals by Ghost in: [#1015](https://codeberg.org/uzu/strudel/pulls/1015) -- 2024-03-24T10:16:11+01:00 Repl sync fixes by Ghost in: [#1014](https://codeberg.org/uzu/strudel/pulls/1014) -- 2024-03-23T20:21:51+01:00 eliminate chromium clock jitter by froos in: [#1004](https://codeberg.org/uzu/strudel/pulls/1004) -- 2024-03-23T15:51:07+01:00 update undocumented script by froos in: [#1013](https://codeberg.org/uzu/strudel/pulls/1013) -- 2024-03-23T15:26:52+01:00 fix: await injectPatternMethods by froos in: [#1012](https://codeberg.org/uzu/strudel/pulls/1012) -- 2024-03-23T15:18:12+01:00 rename trig -> reset, trigzero -> restart by froos in: [#1010](https://codeberg.org/uzu/strudel/pulls/1010) -- 2024-03-23T12:30:03+01:00 Inline punchcard + spiral by froos in: [#1008](https://codeberg.org/uzu/strudel/pulls/1008) -- 2024-03-23T12:27:22+01:00 Color in hap value by froos in: [#1007](https://codeberg.org/uzu/strudel/pulls/1007) -- 2024-03-22T23:53:51+01:00 using strudel in your project guide + cleanup examples by froos in: [#1006](https://codeberg.org/uzu/strudel/pulls/1006) -- 2024-03-22T23:41:50+01:00 accidentals in scale degrees by eefano in: [#1000](https://codeberg.org/uzu/strudel/pulls/1000) -- 2024-03-21T22:53:55+01:00 supersaw oscillator by Ghost in: [#978](https://codeberg.org/uzu/strudel/pulls/978) -- 2024-03-21T13:00:04+01:00 remove canvas, externalize samples, delete junk by froos in: [#1003](https://codeberg.org/uzu/strudel/pulls/1003) -- 2024-03-18T11:37:55+01:00 Fix pure mini highlight by yaxu in: [#994](https://codeberg.org/uzu/strudel/pulls/994) -- 2024-03-18T07:12:14+01:00 inline viz / widgets package by froos in: [#989](https://codeberg.org/uzu/strudel/pulls/989) -- 2024-03-17T04:07:00+01:00 Labeled statements by froos in: [#991](https://codeberg.org/uzu/strudel/pulls/991) -- 2024-03-16T18:24:38+01:00 Beat-oriented functionality by yaxu in: [#976](https://codeberg.org/uzu/strudel/pulls/976) -- 2024-03-15T01:47:35+01:00 REPL sync between windows by Ghost in: [#900](https://codeberg.org/uzu/strudel/pulls/900) -- 2024-03-14T00:20:07+01:00 Update synths.mdx by Ghost in: [#984](https://codeberg.org/uzu/strudel/pulls/984) -- 2024-03-13T00:36:22+01:00 fix: share now shares what's visible instead of active by froos in: [#985](https://codeberg.org/uzu/strudel/pulls/985) -- 2024-03-10T01:18:57+01:00 use ireal as default voicing dict by froos in: [#967](https://codeberg.org/uzu/strudel/pulls/967) -- 2024-03-10T00:50:23+01:00 little fix for withVal by eefano in: [#980](https://codeberg.org/uzu/strudel/pulls/980) -- 2024-03-10T00:46:51+01:00 Velocity in value by froos in: [#974](https://codeberg.org/uzu/strudel/pulls/974) -- 2024-03-10T00:42:50+01:00 fix: clear hydra on reset by froos in: [#983](https://codeberg.org/uzu/strudel/pulls/983) -- 2024-03-10T00:38:43+01:00 move canvas related helpers from core to new draw package by froos in: [#971](https://codeberg.org/uzu/strudel/pulls/971) -- 2024-03-08T05:38:07+01:00 replace shape with distort in learn doc by Ghost in: [#982](https://codeberg.org/uzu/strudel/pulls/982) -- 2024-03-06T12:14:49+01:00 alias - for ~ by yaxu in: [#981](https://codeberg.org/uzu/strudel/pulls/981) -- 2024-03-04T16:04:23+01:00 Worklet Improvents / fixes by Ghost in: [#963](https://codeberg.org/uzu/strudel/pulls/963) -- 2024-03-01T17:30:19+01:00 Nested controls by froos in: [#973](https://codeberg.org/uzu/strudel/pulls/973) +- 2024-03-30T16:05:59+01:00 remove dangerous arithmetic feature by @froos in: [#1030](https://codeberg.org/uzu/strudel/pulls/1030) +- 2024-03-30T16:05:27+01:00 Fix sampler paths by @froos in: [#1034](https://codeberg.org/uzu/strudel/pulls/1034) +- 2024-03-30T14:43:08+01:00 local sample server cli by @froos in: [#1033](https://codeberg.org/uzu/strudel/pulls/1033) +- 2024-03-29T17:14:28+01:00 add font file types to offline cache by @froos in: [#1032](https://codeberg.org/uzu/strudel/pulls/1032) +- 2024-03-29T17:10:16+01:00 add closeBrackets setting by @froos in: [#1031](https://codeberg.org/uzu/strudel/pulls/1031) +- 2024-03-29T14:55:06+01:00 Tactus tidy by @yaxu in: [#1027](https://codeberg.org/uzu/strudel/pulls/1027) +- 2024-03-28T17:06:44+01:00 add setting for sync flag by @froos in: [#1025](https://codeberg.org/uzu/strudel/pulls/1025) +- 2024-03-28T11:37:57+01:00 better theme integration for visuals + various fixes by @froos in: [#1024](https://codeberg.org/uzu/strudel/pulls/1024) +- 2024-03-28T11:33:25+01:00 More fonts by @froos in: [#1023](https://codeberg.org/uzu/strudel/pulls/1023) +- 2024-03-27T13:06:05+01:00 Feature: tactus marking by @yaxu in: [#1021](https://codeberg.org/uzu/strudel/pulls/1021) +- 2024-03-25T06:02:02+01:00 hotfix for 1017 by @Ghost in: [#1020](https://codeberg.org/uzu/strudel/pulls/1020) +- 2024-03-24T15:06:33+01:00 Document signals by @Ghost in: [#1015](https://codeberg.org/uzu/strudel/pulls/1015) +- 2024-03-24T10:16:11+01:00 Repl sync fixes by @Ghost in: [#1014](https://codeberg.org/uzu/strudel/pulls/1014) +- 2024-03-23T20:21:51+01:00 eliminate chromium clock jitter by @froos in: [#1004](https://codeberg.org/uzu/strudel/pulls/1004) +- 2024-03-23T15:51:07+01:00 update undocumented script by @froos in: [#1013](https://codeberg.org/uzu/strudel/pulls/1013) +- 2024-03-23T15:26:52+01:00 fix: await injectPatternMethods by @froos in: [#1012](https://codeberg.org/uzu/strudel/pulls/1012) +- 2024-03-23T15:18:12+01:00 rename trig -> reset, trigzero -> restart by @froos in: [#1010](https://codeberg.org/uzu/strudel/pulls/1010) +- 2024-03-23T12:30:03+01:00 Inline punchcard + spiral by @froos in: [#1008](https://codeberg.org/uzu/strudel/pulls/1008) +- 2024-03-23T12:27:22+01:00 Color in hap value by @froos in: [#1007](https://codeberg.org/uzu/strudel/pulls/1007) +- 2024-03-22T23:53:51+01:00 using strudel in your project guide + cleanup examples by @froos in: [#1006](https://codeberg.org/uzu/strudel/pulls/1006) +- 2024-03-22T23:41:50+01:00 accidentals in scale degrees by @eefano in: [#1000](https://codeberg.org/uzu/strudel/pulls/1000) +- 2024-03-21T22:53:55+01:00 supersaw oscillator by @Ghost in: [#978](https://codeberg.org/uzu/strudel/pulls/978) +- 2024-03-21T13:00:04+01:00 remove canvas, externalize samples, delete junk by @froos in: [#1003](https://codeberg.org/uzu/strudel/pulls/1003) +- 2024-03-18T11:37:55+01:00 Fix pure mini highlight by @yaxu in: [#994](https://codeberg.org/uzu/strudel/pulls/994) +- 2024-03-18T07:12:14+01:00 inline viz / widgets package by @froos in: [#989](https://codeberg.org/uzu/strudel/pulls/989) +- 2024-03-17T04:07:00+01:00 Labeled statements by @froos in: [#991](https://codeberg.org/uzu/strudel/pulls/991) +- 2024-03-16T18:24:38+01:00 Beat-oriented functionality by @yaxu in: [#976](https://codeberg.org/uzu/strudel/pulls/976) +- 2024-03-15T01:47:35+01:00 REPL sync between windows by @Ghost in: [#900](https://codeberg.org/uzu/strudel/pulls/900) +- 2024-03-14T00:20:07+01:00 Update synths.mdx by @Ghost in: [#984](https://codeberg.org/uzu/strudel/pulls/984) +- 2024-03-13T00:36:22+01:00 fix: share now shares what's visible instead of active by @froos in: [#985](https://codeberg.org/uzu/strudel/pulls/985) +- 2024-03-10T01:18:57+01:00 use ireal as default voicing dict by @froos in: [#967](https://codeberg.org/uzu/strudel/pulls/967) +- 2024-03-10T00:50:23+01:00 little fix for withVal by @eefano in: [#980](https://codeberg.org/uzu/strudel/pulls/980) +- 2024-03-10T00:46:51+01:00 Velocity in value by @froos in: [#974](https://codeberg.org/uzu/strudel/pulls/974) +- 2024-03-10T00:42:50+01:00 fix: clear hydra on reset by @froos in: [#983](https://codeberg.org/uzu/strudel/pulls/983) +- 2024-03-10T00:38:43+01:00 move canvas related helpers from core to new draw package by @froos in: [#971](https://codeberg.org/uzu/strudel/pulls/971) +- 2024-03-08T05:38:07+01:00 replace shape with distort in learn doc by @Ghost in: [#982](https://codeberg.org/uzu/strudel/pulls/982) +- 2024-03-06T12:14:49+01:00 alias - for ~ by @yaxu in: [#981](https://codeberg.org/uzu/strudel/pulls/981) +- 2024-03-04T16:04:23+01:00 Worklet Improvents / fixes by @Ghost in: [#963](https://codeberg.org/uzu/strudel/pulls/963) +- 2024-03-01T17:30:19+01:00 Nested controls by @froos in: [#973](https://codeberg.org/uzu/strudel/pulls/973) ## february 2024 -- 2024-02-29T15:33:12+01:00 feat: can now invert euclid pulses with negative numbers by froos in: [#959](https://codeberg.org/uzu/strudel/pulls/959) -- 2024-02-29T15:31:23+01:00 pickOut(), pickRestart(), pickReset() by eefano in: [#950](https://codeberg.org/uzu/strudel/pulls/950) -- 2024-02-29T15:12:45+01:00 remove legacy legato + duration implementations by froos in: [#965](https://codeberg.org/uzu/strudel/pulls/965) -- 2024-02-29T08:47:53+01:00 fix for transpose(): preserve hap value object structure by eefano in: [#966](https://codeberg.org/uzu/strudel/pulls/966) -- 2024-02-29T08:32:00+01:00 add debounce to logger by froos in: [#968](https://codeberg.org/uzu/strudel/pulls/968) -- 2024-02-28T18:43:52+01:00 controls refactoring: simplify exports by froos in: [#962](https://codeberg.org/uzu/strudel/pulls/962) -- 2024-02-25T19:17:00+01:00 fix: reset global fx on pattern change by froos in: [#960](https://codeberg.org/uzu/strudel/pulls/960) -- 2024-02-25T14:15:30+01:00 fix midi issue on firefox and added quote error by Ghost in: [#936](https://codeberg.org/uzu/strudel/pulls/936) -- 2024-02-25T13:19:47+01:00 'Enable Bracket Matching' option in Codemirror by eefano in: [#956](https://codeberg.org/uzu/strudel/pulls/956) -- 2024-02-23T14:37:46+01:00 fix script importable packages (web + repl) by froos in: [#957](https://codeberg.org/uzu/strudel/pulls/957) -- 2024-02-21T16:17:37+01:00 account for cps in midi time duration by Ghost in: [#954](https://codeberg.org/uzu/strudel/pulls/954) -- 2024-02-21T10:27:12+01:00 Auto await samples by froos in: [#955](https://codeberg.org/uzu/strudel/pulls/955) -- 2024-02-08T13:16:15+01:00 remove cjs builds by froos in: [#945](https://codeberg.org/uzu/strudel/pulls/945) -- 2024-02-04T23:15:37+01:00 Minor documentation error: Update first-sounds.mdx by Ghost in: [#941](https://codeberg.org/uzu/strudel/pulls/941) +- 2024-02-29T15:33:12+01:00 feat: can now invert euclid pulses with negative numbers by @froos in: [#959](https://codeberg.org/uzu/strudel/pulls/959) +- 2024-02-29T15:31:23+01:00 pickOut(), pickRestart(), pickReset() by @eefano in: [#950](https://codeberg.org/uzu/strudel/pulls/950) +- 2024-02-29T15:12:45+01:00 remove legacy legato + duration implementations by @froos in: [#965](https://codeberg.org/uzu/strudel/pulls/965) +- 2024-02-29T08:47:53+01:00 fix for transpose(): preserve hap value object structure by @eefano in: [#966](https://codeberg.org/uzu/strudel/pulls/966) +- 2024-02-29T08:32:00+01:00 add debounce to logger by @froos in: [#968](https://codeberg.org/uzu/strudel/pulls/968) +- 2024-02-28T18:43:52+01:00 controls refactoring: simplify exports by @froos in: [#962](https://codeberg.org/uzu/strudel/pulls/962) +- 2024-02-25T19:17:00+01:00 fix: reset global fx on pattern change by @froos in: [#960](https://codeberg.org/uzu/strudel/pulls/960) +- 2024-02-25T14:15:30+01:00 fix midi issue on firefox and added quote error by @Ghost in: [#936](https://codeberg.org/uzu/strudel/pulls/936) +- 2024-02-25T13:19:47+01:00 'Enable Bracket Matching' option in Codemirror by @eefano in: [#956](https://codeberg.org/uzu/strudel/pulls/956) +- 2024-02-23T14:37:46+01:00 fix script importable packages (web + repl) by @froos in: [#957](https://codeberg.org/uzu/strudel/pulls/957) +- 2024-02-21T16:17:37+01:00 account for cps in midi time duration by @Ghost in: [#954](https://codeberg.org/uzu/strudel/pulls/954) +- 2024-02-21T10:27:12+01:00 Auto await samples by @froos in: [#955](https://codeberg.org/uzu/strudel/pulls/955) +- 2024-02-08T13:16:15+01:00 remove cjs builds by @froos in: [#945](https://codeberg.org/uzu/strudel/pulls/945) +- 2024-02-04T23:15:37+01:00 Minor documentation error: Update first-sounds.mdx by @Ghost in: [#941](https://codeberg.org/uzu/strudel/pulls/941) ## january 2024 -- 2024-01-24T16:48:57+01:00 fix: pianoroll sorting by froos in: [#938](https://codeberg.org/uzu/strudel/pulls/938) -- 2024-01-23T00:04:03+01:00 V1 release notes by froos in: [#935](https://codeberg.org/uzu/strudel/pulls/935) -- 2024-01-22T20:20:53+01:00 2 years blog post by froos in: [#929](https://codeberg.org/uzu/strudel/pulls/929) -- 2024-01-22T20:02:34+01:00 make 0.5hz cps the default by yaxu in: [#931](https://codeberg.org/uzu/strudel/pulls/931) -- 2024-01-22T00:52:01+01:00 Fix pattern tab not showing patterns without created date by Ghost in: [#934](https://codeberg.org/uzu/strudel/pulls/934) -- 2024-01-21T20:46:28+01:00 Add useful pattern selection behavior for performing. by Ghost in: [#897](https://codeberg.org/uzu/strudel/pulls/897) -- 2024-01-21T01:30:28+01:00 Refactor cps functions by froos in: [#933](https://codeberg.org/uzu/strudel/pulls/933) -- 2024-01-20T23:47:31+01:00 Make splice cps-aware by yaxu in: [#932](https://codeberg.org/uzu/strudel/pulls/932) -- 2024-01-19T18:50:57+01:00 community bakery by froos in: [#923](https://codeberg.org/uzu/strudel/pulls/923) -- 2024-01-19T18:49:54+01:00 add pickF and pickmodF by Ghost in: [#924](https://codeberg.org/uzu/strudel/pulls/924) -- 2024-01-19T15:10:48+01:00 Mini-notation additions towards tidal compatibility by yaxu in: [#926](https://codeberg.org/uzu/strudel/pulls/926) -- 2024-01-18T23:34:11+01:00 Blog improvements by froos in: [#919](https://codeberg.org/uzu/strudel/pulls/919) -- 2024-01-18T18:08:29+01:00 pick, pickmod, inhabit, inhabitmod by yaxu in: [#921](https://codeberg.org/uzu/strudel/pulls/921) -- 2024-01-18T18:04:27+01:00 Revert "`pick` now accepts lookup tables, with alternate cycle squeezing behaviour as new `inhabit` function" by yaxu in: [#920](https://codeberg.org/uzu/strudel/pulls/920) -- 2024-01-18T17:45:39+01:00 `pick` now accepts lookup tables, with alternate cycle squeezing behaviour as new `inhabit` function by yaxu in: [#918](https://codeberg.org/uzu/strudel/pulls/918) -- 2024-01-18T10:30:08+01:00 rename @strudel.cycles/* packages to @strudel/* by froos in: [#917](https://codeberg.org/uzu/strudel/pulls/917) -- 2024-01-18T06:54:33+01:00 pitch envelopes by froos in: [#913](https://codeberg.org/uzu/strudel/pulls/913) -- 2024-01-18T05:09:38+01:00 Fix: swatch/[name].png.js static path by Ghost in: [#916](https://codeberg.org/uzu/strudel/pulls/916) -- 2024-01-16T08:22:28+01:00 Add more vowel qualities for the vowels function by Ghost in: [#907](https://codeberg.org/uzu/strudel/pulls/907) -- 2024-01-14T23:56:36+01:00 adds a blog by froos in: [#911](https://codeberg.org/uzu/strudel/pulls/911) -- 2024-01-14T23:52:41+01:00 Remove hideHeader for better mobile UI and consistency by Ghost in: [#894](https://codeberg.org/uzu/strudel/pulls/894) -- 2024-01-14T22:43:06+01:00 Further Envelope improvements by Ghost in: [#868](https://codeberg.org/uzu/strudel/pulls/868) -- 2024-01-14T00:48:04+01:00 public sharing by froos in: [#910](https://codeberg.org/uzu/strudel/pulls/910) -- 2024-01-12T18:31:41+01:00 fix some build warnings by froos in: [#902](https://codeberg.org/uzu/strudel/pulls/902) -- 2024-01-12T18:01:55+01:00 prevent vite from complaining about additional exports in jsx files by Ghost in: [#891](https://codeberg.org/uzu/strudel/pulls/891) -- 2024-01-12T17:05:47+01:00 Update Vite version so hot reload works properly with newest pnpm version by Ghost in: [#892](https://codeberg.org/uzu/strudel/pulls/892) -- 2024-01-12T15:09:41+01:00 support , in < > by froos in: [#886](https://codeberg.org/uzu/strudel/pulls/886) -- 2024-01-10T13:55:39+01:00 fix: autocomplete / tooltip code example bug by froos in: [#898](https://codeberg.org/uzu/strudel/pulls/898) -- 2024-01-06T15:23:55+01:00 fix: invisible selection on vim + emacs mode by froos in: [#889](https://codeberg.org/uzu/strudel/pulls/889) -- 2024-01-05T22:29:20+01:00 scales can now be anchored by froos in: [#888](https://codeberg.org/uzu/strudel/pulls/888) -- 2024-01-04T11:21:42+01:00 add root mode for voicings by froos in: [#887](https://codeberg.org/uzu/strudel/pulls/887) -- 2024-01-01T18:32:17+01:00 Showcase by froos in: [#885](https://codeberg.org/uzu/strudel/pulls/885) -- 2024-01-01T14:22:11+01:00 bugfix: suspend and close exisiting audio context when changing interface by Ghost in: [#882](https://codeberg.org/uzu/strudel/pulls/882) -- 2024-01-01T14:21:52+01:00 add mastodon link by froos in: [#884](https://codeberg.org/uzu/strudel/pulls/884) +- 2024-01-24T16:48:57+01:00 fix: pianoroll sorting by @froos in: [#938](https://codeberg.org/uzu/strudel/pulls/938) +- 2024-01-23T00:04:03+01:00 V1 release notes by @froos in: [#935](https://codeberg.org/uzu/strudel/pulls/935) +- 2024-01-22T20:20:53+01:00 2 years blog post by @froos in: [#929](https://codeberg.org/uzu/strudel/pulls/929) +- 2024-01-22T20:02:34+01:00 make 0.5hz cps the default by @yaxu in: [#931](https://codeberg.org/uzu/strudel/pulls/931) +- 2024-01-22T00:52:01+01:00 Fix pattern tab not showing patterns without created date by @Ghost in: [#934](https://codeberg.org/uzu/strudel/pulls/934) +- 2024-01-21T20:46:28+01:00 Add useful pattern selection behavior for performing. by @Ghost in: [#897](https://codeberg.org/uzu/strudel/pulls/897) +- 2024-01-21T01:30:28+01:00 Refactor cps functions by @froos in: [#933](https://codeberg.org/uzu/strudel/pulls/933) +- 2024-01-20T23:47:31+01:00 Make splice cps-aware by @yaxu in: [#932](https://codeberg.org/uzu/strudel/pulls/932) +- 2024-01-19T18:50:57+01:00 community bakery by @froos in: [#923](https://codeberg.org/uzu/strudel/pulls/923) +- 2024-01-19T18:49:54+01:00 add pickF and pickmodF by @Ghost in: [#924](https://codeberg.org/uzu/strudel/pulls/924) +- 2024-01-19T15:10:48+01:00 Mini-notation additions towards tidal compatibility by @yaxu in: [#926](https://codeberg.org/uzu/strudel/pulls/926) +- 2024-01-18T23:34:11+01:00 Blog improvements by @froos in: [#919](https://codeberg.org/uzu/strudel/pulls/919) +- 2024-01-18T18:08:29+01:00 pick, pickmod, inhabit, inhabitmod by @yaxu in: [#921](https://codeberg.org/uzu/strudel/pulls/921) +- 2024-01-18T18:04:27+01:00 Revert "`pick` now accepts lookup tables, with alternate cycle squeezing behaviour as new `inhabit` function" by @yaxu in: [#920](https://codeberg.org/uzu/strudel/pulls/920) +- 2024-01-18T17:45:39+01:00 `pick` now accepts lookup tables, with alternate cycle squeezing behaviour as new `inhabit` function by @yaxu in: [#918](https://codeberg.org/uzu/strudel/pulls/918) +- 2024-01-18T10:30:08+01:00 rename @strudel.cycles/* packages to @strudel/* by @froos in: [#917](https://codeberg.org/uzu/strudel/pulls/917) +- 2024-01-18T06:54:33+01:00 pitch envelopes by @froos in: [#913](https://codeberg.org/uzu/strudel/pulls/913) +- 2024-01-18T05:09:38+01:00 Fix: swatch/[name].png.js static path by @Ghost in: [#916](https://codeberg.org/uzu/strudel/pulls/916) +- 2024-01-16T08:22:28+01:00 Add more vowel qualities for the vowels function by @Ghost in: [#907](https://codeberg.org/uzu/strudel/pulls/907) +- 2024-01-14T23:56:36+01:00 adds a blog by @froos in: [#911](https://codeberg.org/uzu/strudel/pulls/911) +- 2024-01-14T23:52:41+01:00 Remove hideHeader for better mobile UI and consistency by @Ghost in: [#894](https://codeberg.org/uzu/strudel/pulls/894) +- 2024-01-14T22:43:06+01:00 Further Envelope improvements by @Ghost in: [#868](https://codeberg.org/uzu/strudel/pulls/868) +- 2024-01-14T00:48:04+01:00 public sharing by @froos in: [#910](https://codeberg.org/uzu/strudel/pulls/910) +- 2024-01-12T18:31:41+01:00 fix some build warnings by @froos in: [#902](https://codeberg.org/uzu/strudel/pulls/902) +- 2024-01-12T18:01:55+01:00 prevent vite from complaining about additional exports in jsx files by @Ghost in: [#891](https://codeberg.org/uzu/strudel/pulls/891) +- 2024-01-12T17:05:47+01:00 Update Vite version so hot reload works properly with newest pnpm version by @Ghost in: [#892](https://codeberg.org/uzu/strudel/pulls/892) +- 2024-01-12T15:09:41+01:00 support , in < > by @froos in: [#886](https://codeberg.org/uzu/strudel/pulls/886) +- 2024-01-10T13:55:39+01:00 fix: autocomplete / tooltip code example bug by @froos in: [#898](https://codeberg.org/uzu/strudel/pulls/898) +- 2024-01-06T15:23:55+01:00 fix: invisible selection on vim + emacs mode by @froos in: [#889](https://codeberg.org/uzu/strudel/pulls/889) +- 2024-01-05T22:29:20+01:00 scales can now be anchored by @froos in: [#888](https://codeberg.org/uzu/strudel/pulls/888) +- 2024-01-04T11:21:42+01:00 add root mode for voicings by @froos in: [#887](https://codeberg.org/uzu/strudel/pulls/887) +- 2024-01-01T18:32:17+01:00 Showcase by @froos in: [#885](https://codeberg.org/uzu/strudel/pulls/885) +- 2024-01-01T14:22:11+01:00 bugfix: suspend and close exisiting audio context when changing interface by @Ghost in: [#882](https://codeberg.org/uzu/strudel/pulls/882) +- 2024-01-01T14:21:52+01:00 add mastodon link by @froos in: [#884](https://codeberg.org/uzu/strudel/pulls/884) ## december 2023 -- 2023-12-31T17:02:57+01:00 fix: make sure n is never undefined before nanFallback by froos in: [#881](https://codeberg.org/uzu/strudel/pulls/881) -- 2023-12-31T16:47:22+01:00 Error tolerance by froos in: [#880](https://codeberg.org/uzu/strudel/pulls/880) -- 2023-12-31T10:06:24+01:00 bugfix: sound select indexes out of bounds by Ghost in: [#871](https://codeberg.org/uzu/strudel/pulls/871) -- 2023-12-31T10:03:01+01:00 Audio device selection by Ghost in: [#854](https://codeberg.org/uzu/strudel/pulls/854) -- 2023-12-31T00:59:49+01:00 Dependency update by froos in: [#879](https://codeberg.org/uzu/strudel/pulls/879) -- 2023-12-29T16:53:41+01:00 move all examples to separate examples folder by froos in: [#878](https://codeberg.org/uzu/strudel/pulls/878) -- 2023-12-29T15:29:17+01:00 final vanillification by froos in: [#876](https://codeberg.org/uzu/strudel/pulls/876) -- 2023-12-27T18:38:09+01:00 Bug Fix #119: Clock drift by Ghost in: [#874](https://codeberg.org/uzu/strudel/pulls/874) -- 2023-12-27T13:17:03+01:00 main repl vanillification by froos in: [#873](https://codeberg.org/uzu/strudel/pulls/873) -- 2023-12-25T17:47:25+01:00 more work on vanilla repl: repl web component + package + MicroRepl by froos in: [#866](https://codeberg.org/uzu/strudel/pulls/866) -- 2023-12-15T00:11:35+01:00 Vanilla repl 3 by froos in: [#865](https://codeberg.org/uzu/strudel/pulls/865) -- 2023-12-14T21:28:49+01:00 Vanilla repl 2 by froos in: [#863](https://codeberg.org/uzu/strudel/pulls/863) -- 2023-12-12T21:49:23+01:00 fix: finally repair envelopes by froos in: [#861](https://codeberg.org/uzu/strudel/pulls/861) -- 2023-12-12T21:20:00+01:00 add missing trailing slashes by froos in: [#860](https://codeberg.org/uzu/strudel/pulls/860) -- 2023-12-12T19:08:31+01:00 Sound Import from local file system by Ghost in: [#839](https://codeberg.org/uzu/strudel/pulls/839) -- 2023-12-11T22:48:35+01:00 Pattern organization by froos in: [#858](https://codeberg.org/uzu/strudel/pulls/858) -- 2023-12-09T17:25:11+01:00 Export patterns + ui tweaks by froos in: [#855](https://codeberg.org/uzu/strudel/pulls/855) -- 2023-12-08T09:41:10+01:00 patterns tab: import patterns + style by froos in: [#852](https://codeberg.org/uzu/strudel/pulls/852) -- 2023-12-07T20:42:00+01:00 Patterns tab + Refactor Panel by froos in: [#769](https://codeberg.org/uzu/strudel/pulls/769) -- 2023-12-07T10:25:16+01:00 CHANGES: pnpm 8.1.3 to 8.11.0 by Ghost in: [#850](https://codeberg.org/uzu/strudel/pulls/850) -- 2023-12-07T09:35:14+01:00 Fix edge case with rehype-urls and trailing slashes in image file paths by Ghost in: [#849](https://codeberg.org/uzu/strudel/pulls/849) -- 2023-12-06T23:02:31+01:00 Fix examples page, piano() and a few workshop imgs by Ghost in: [#848](https://codeberg.org/uzu/strudel/pulls/848) -- 2023-12-06T22:30:59+01:00 fix: swatch png src by froos in: [#846](https://codeberg.org/uzu/strudel/pulls/846) -- 2023-12-06T22:19:30+01:00 fix: missing hash for links starting with / by froos in: [#845](https://codeberg.org/uzu/strudel/pulls/845) -- 2023-12-06T22:05:05+01:00 improve slashing + base href behavior by froos in: [#842](https://codeberg.org/uzu/strudel/pulls/842) -- 2023-12-06T21:26:49+01:00 Add in fixes from my fork to slashocalypse branch by Ghost in: [#843](https://codeberg.org/uzu/strudel/pulls/843) -- 2023-12-05T18:43:58+01:00 Prevent 404 on Algolia crawls by Ghost in: [#838](https://codeberg.org/uzu/strudel/pulls/838) -- 2023-12-05T18:26:47+01:00 Multichannel audio by Ghost in: [#820](https://codeberg.org/uzu/strudel/pulls/820) -- 2023-12-05T12:19:58+01:00 ADDS: JetBrains IDE files and directories to .gitignore by Ghost in: [#840](https://codeberg.org/uzu/strudel/pulls/840) -- 2023-12-05T12:19:27+01:00 CHANGES: github action pnpm version from 7 to 8.3.1 by Ghost in: [#835](https://codeberg.org/uzu/strudel/pulls/835) -- 2023-12-05T12:19:17+01:00 CHANGES: pin pnpm to version 8.3.1 by Ghost in: [#834](https://codeberg.org/uzu/strudel/pulls/834) -- 2023-12-05T12:19:06+01:00 CHANGES: github action checkout v2 -> v4 by Ghost in: [#837](https://codeberg.org/uzu/strudel/pulls/837) -- 2023-12-05T11:15:14+01:00 FIXES: palindrome abc -> abccba by Ghost in: [#831](https://codeberg.org/uzu/strudel/pulls/831) -- 2023-12-02T09:43:50+01:00 Fix a typo by Ghost in: [#830](https://codeberg.org/uzu/strudel/pulls/830) +- 2023-12-31T17:02:57+01:00 fix: make sure n is never undefined before nanFallback by @froos in: [#881](https://codeberg.org/uzu/strudel/pulls/881) +- 2023-12-31T16:47:22+01:00 Error tolerance by @froos in: [#880](https://codeberg.org/uzu/strudel/pulls/880) +- 2023-12-31T10:06:24+01:00 bugfix: sound select indexes out of bounds by @Ghost in: [#871](https://codeberg.org/uzu/strudel/pulls/871) +- 2023-12-31T10:03:01+01:00 Audio device selection by @Ghost in: [#854](https://codeberg.org/uzu/strudel/pulls/854) +- 2023-12-31T00:59:49+01:00 Dependency update by @froos in: [#879](https://codeberg.org/uzu/strudel/pulls/879) +- 2023-12-29T16:53:41+01:00 move all examples to separate examples folder by @froos in: [#878](https://codeberg.org/uzu/strudel/pulls/878) +- 2023-12-29T15:29:17+01:00 final vanillification by @froos in: [#876](https://codeberg.org/uzu/strudel/pulls/876) +- 2023-12-27T18:38:09+01:00 Bug Fix #119: Clock drift by @Ghost in: [#874](https://codeberg.org/uzu/strudel/pulls/874) +- 2023-12-27T13:17:03+01:00 main repl vanillification by @froos in: [#873](https://codeberg.org/uzu/strudel/pulls/873) +- 2023-12-25T17:47:25+01:00 more work on vanilla repl: repl web component + package + MicroRepl by @froos in: [#866](https://codeberg.org/uzu/strudel/pulls/866) +- 2023-12-15T00:11:35+01:00 Vanilla repl 3 by @froos in: [#865](https://codeberg.org/uzu/strudel/pulls/865) +- 2023-12-14T21:28:49+01:00 Vanilla repl 2 by @froos in: [#863](https://codeberg.org/uzu/strudel/pulls/863) +- 2023-12-12T21:49:23+01:00 fix: finally repair envelopes by @froos in: [#861](https://codeberg.org/uzu/strudel/pulls/861) +- 2023-12-12T21:20:00+01:00 add missing trailing slashes by @froos in: [#860](https://codeberg.org/uzu/strudel/pulls/860) +- 2023-12-12T19:08:31+01:00 Sound Import from local file system by @Ghost in: [#839](https://codeberg.org/uzu/strudel/pulls/839) +- 2023-12-11T22:48:35+01:00 Pattern organization by @froos in: [#858](https://codeberg.org/uzu/strudel/pulls/858) +- 2023-12-09T17:25:11+01:00 Export patterns + ui tweaks by @froos in: [#855](https://codeberg.org/uzu/strudel/pulls/855) +- 2023-12-08T09:41:10+01:00 patterns tab: import patterns + style by @froos in: [#852](https://codeberg.org/uzu/strudel/pulls/852) +- 2023-12-07T20:42:00+01:00 Patterns tab + Refactor Panel by @froos in: [#769](https://codeberg.org/uzu/strudel/pulls/769) +- 2023-12-07T10:25:16+01:00 CHANGES: pnpm 8.1.3 to 8.11.0 by @Ghost in: [#850](https://codeberg.org/uzu/strudel/pulls/850) +- 2023-12-07T09:35:14+01:00 Fix edge case with rehype-urls and trailing slashes in image file paths by @Ghost in: [#849](https://codeberg.org/uzu/strudel/pulls/849) +- 2023-12-06T23:02:31+01:00 Fix examples page, piano() and a few workshop imgs by @Ghost in: [#848](https://codeberg.org/uzu/strudel/pulls/848) +- 2023-12-06T22:30:59+01:00 fix: swatch png src by @froos in: [#846](https://codeberg.org/uzu/strudel/pulls/846) +- 2023-12-06T22:19:30+01:00 fix: missing hash for links starting with / by @froos in: [#845](https://codeberg.org/uzu/strudel/pulls/845) +- 2023-12-06T22:05:05+01:00 improve slashing + base href behavior by @froos in: [#842](https://codeberg.org/uzu/strudel/pulls/842) +- 2023-12-06T21:26:49+01:00 Add in fixes from my fork to slashocalypse branch by @Ghost in: [#843](https://codeberg.org/uzu/strudel/pulls/843) +- 2023-12-05T18:43:58+01:00 Prevent 404 on Algolia crawls by @Ghost in: [#838](https://codeberg.org/uzu/strudel/pulls/838) +- 2023-12-05T18:26:47+01:00 Multichannel audio by @Ghost in: [#820](https://codeberg.org/uzu/strudel/pulls/820) +- 2023-12-05T12:19:58+01:00 ADDS: JetBrains IDE files and directories to .gitignore by @Ghost in: [#840](https://codeberg.org/uzu/strudel/pulls/840) +- 2023-12-05T12:19:27+01:00 CHANGES: github action pnpm version from 7 to 8.3.1 by @Ghost in: [#835](https://codeberg.org/uzu/strudel/pulls/835) +- 2023-12-05T12:19:17+01:00 CHANGES: pin pnpm to version 8.3.1 by @Ghost in: [#834](https://codeberg.org/uzu/strudel/pulls/834) +- 2023-12-05T12:19:06+01:00 CHANGES: github action checkout v2 -> v4 by @Ghost in: [#837](https://codeberg.org/uzu/strudel/pulls/837) +- 2023-12-05T11:15:14+01:00 FIXES: palindrome abc -> abccba by @Ghost in: [#831](https://codeberg.org/uzu/strudel/pulls/831) +- 2023-12-02T09:43:50+01:00 Fix a typo by @Ghost in: [#830](https://codeberg.org/uzu/strudel/pulls/830) ## november 2023 -- 2023-11-30T10:42:41+01:00 Add and style algolia search by Ghost in: [#827](https://codeberg.org/uzu/strudel/pulls/827) -- 2023-11-25T15:50:44+01:00 Hydra fixes and improvements by Ghost in: [#818](https://codeberg.org/uzu/strudel/pulls/818) -- 2023-11-24T10:07:17+01:00 add options param to initHydra by Ghost in: [#808](https://codeberg.org/uzu/strudel/pulls/808) -- 2023-11-24T09:57:02+01:00 Improve documentation for synonym functions by Ghost in: [#800](https://codeberg.org/uzu/strudel/pulls/800) -- 2023-11-24T09:00:54+01:00 New noise type: "crackle" by Ghost in: [#806](https://codeberg.org/uzu/strudel/pulls/806) -- 2023-11-18T21:18:16+01:00 Color hsl by froos in: [#815](https://codeberg.org/uzu/strudel/pulls/815) -- 2023-11-17T23:18:23+01:00 fix: multiple repls by froos in: [#813](https://codeberg.org/uzu/strudel/pulls/813) -- 2023-11-17T20:52:25+01:00 upstream changes by yaxu in: [#809](https://codeberg.org/uzu/strudel/pulls/809) -- 2023-11-17T16:04:10+01:00 Add doc for euclidLegatoRot, wordfall and slider by Ghost in: [#801](https://codeberg.org/uzu/strudel/pulls/801) -- 2023-11-17T14:42:58+01:00 add option to disable active line highlighting in Code Settings by Ghost in: [#804](https://codeberg.org/uzu/strudel/pulls/804) -- 2023-11-17T14:37:52+01:00 tidal style d1 ... d9 functions + more by froos in: [#805](https://codeberg.org/uzu/strudel/pulls/805) -- 2023-11-15T20:12:23+01:00 remove unwanted cm6 outline for strudelTheme by Ghost in: [#802](https://codeberg.org/uzu/strudel/pulls/802) -- 2023-11-13T23:30:15+01:00 Create phaser effect by Ghost in: [#798](https://codeberg.org/uzu/strudel/pulls/798) -- 2023-11-10T12:17:35+01:00 support multiple named serial connections, change default baudrate by yaxu in: [#551](https://codeberg.org/uzu/strudel/pulls/551) -- 2023-11-09T09:27:57+01:00 Fix for #1. Enables named instruments for csoundm. by gogins in: [#662](https://codeberg.org/uzu/strudel/pulls/662) -- 2023-11-09T09:26:45+01:00 Adding vibrato to Superdough sampler by Ghost in: [#706](https://codeberg.org/uzu/strudel/pulls/706) -- 2023-11-09T08:46:03+01:00 Document pianoroll by Ghost in: [#784](https://codeberg.org/uzu/strudel/pulls/784) -- 2023-11-09T08:45:29+01:00 Update first-effects.mdx by Ghost in: [#795](https://codeberg.org/uzu/strudel/pulls/795) -- 2023-11-09T08:44:43+01:00 Update pattern-effects.mdx by Ghost in: [#796](https://codeberg.org/uzu/strudel/pulls/796) -- 2023-11-09T08:43:50+01:00 Update recap.mdx by Ghost in: [#797](https://codeberg.org/uzu/strudel/pulls/797) -- 2023-11-07T11:53:49+01:00 Update first-sounds.mdx by Ghost in: [#794](https://codeberg.org/uzu/strudel/pulls/794) -- 2023-11-06T23:17:32+01:00 don't use anchor links for reference by froos in: [#791](https://codeberg.org/uzu/strudel/pulls/791) -- 2023-11-06T22:40:44+01:00 samples loading shortcuts: by froos in: [#788](https://codeberg.org/uzu/strudel/pulls/788) -- 2023-11-05T22:47:48+01:00 Fix scope pos + document by froos in: [#786](https://codeberg.org/uzu/strudel/pulls/786) -- 2023-11-05T22:10:25+01:00 Implement optional hover tooltip with function documentation by Ghost in: [#783](https://codeberg.org/uzu/strudel/pulls/783) -- 2023-11-05T16:46:06+01:00 Add function params in reference tab by Ghost in: [#785](https://codeberg.org/uzu/strudel/pulls/785) -- 2023-11-05T12:42:00+01:00 fix: style issues by froos in: [#781](https://codeberg.org/uzu/strudel/pulls/781) -- 2023-11-05T12:21:28+01:00 add xfade by froos in: [#780](https://codeberg.org/uzu/strudel/pulls/780) -- 2023-11-02T09:30:26+01:00 Update to Astro 3 by froos in: [#775](https://codeberg.org/uzu/strudel/pulls/775) -- 2023-11-02T08:57:14+01:00 add vscode bindings by Ghost in: [#773](https://codeberg.org/uzu/strudel/pulls/773) -- 2023-11-02T08:31:30+01:00 fix: share copy to clipboard + alert by froos in: [#774](https://codeberg.org/uzu/strudel/pulls/774) -- 2023-11-01T22:22:34+01:00 Fix chunk, add fastChunk and repeatCycles by yaxu in: [#712](https://codeberg.org/uzu/strudel/pulls/712) -- 2023-11-01T22:12:49+01:00 Document adsr function by Ghost in: [#767](https://codeberg.org/uzu/strudel/pulls/767) -- 2023-11-01T22:11:49+01:00 Add pick and squeeze functions by Ghost in: [#771](https://codeberg.org/uzu/strudel/pulls/771) -- 2023-11-01T22:04:00+01:00 Update vite pwa by froos in: [#772](https://codeberg.org/uzu/strudel/pulls/772) +- 2023-11-30T10:42:41+01:00 Add and style algolia search by @Ghost in: [#827](https://codeberg.org/uzu/strudel/pulls/827) +- 2023-11-25T15:50:44+01:00 Hydra fixes and improvements by @Ghost in: [#818](https://codeberg.org/uzu/strudel/pulls/818) +- 2023-11-24T10:07:17+01:00 add options param to initHydra by @Ghost in: [#808](https://codeberg.org/uzu/strudel/pulls/808) +- 2023-11-24T09:57:02+01:00 Improve documentation for synonym functions by @Ghost in: [#800](https://codeberg.org/uzu/strudel/pulls/800) +- 2023-11-24T09:00:54+01:00 New noise type: "crackle" by @Ghost in: [#806](https://codeberg.org/uzu/strudel/pulls/806) +- 2023-11-18T21:18:16+01:00 Color hsl by @froos in: [#815](https://codeberg.org/uzu/strudel/pulls/815) +- 2023-11-17T23:18:23+01:00 fix: multiple repls by @froos in: [#813](https://codeberg.org/uzu/strudel/pulls/813) +- 2023-11-17T20:52:25+01:00 upstream changes by @yaxu in: [#809](https://codeberg.org/uzu/strudel/pulls/809) +- 2023-11-17T16:04:10+01:00 Add doc for euclidLegatoRot, wordfall and slider by @Ghost in: [#801](https://codeberg.org/uzu/strudel/pulls/801) +- 2023-11-17T14:42:58+01:00 add option to disable active line highlighting in Code Settings by @Ghost in: [#804](https://codeberg.org/uzu/strudel/pulls/804) +- 2023-11-17T14:37:52+01:00 tidal style d1 ... d9 functions + more by @froos in: [#805](https://codeberg.org/uzu/strudel/pulls/805) +- 2023-11-15T20:12:23+01:00 remove unwanted cm6 outline for strudelTheme by @Ghost in: [#802](https://codeberg.org/uzu/strudel/pulls/802) +- 2023-11-13T23:30:15+01:00 Create phaser effect by @Ghost in: [#798](https://codeberg.org/uzu/strudel/pulls/798) +- 2023-11-10T12:17:35+01:00 support multiple named serial connections, change default baudrate by @yaxu in: [#551](https://codeberg.org/uzu/strudel/pulls/551) +- 2023-11-09T09:27:57+01:00 Fix for #1. Enables named instruments for csoundm. by @gogins in: [#662](https://codeberg.org/uzu/strudel/pulls/662) +- 2023-11-09T09:26:45+01:00 Adding vibrato to Superdough sampler by @Ghost in: [#706](https://codeberg.org/uzu/strudel/pulls/706) +- 2023-11-09T08:46:03+01:00 Document pianoroll by @Ghost in: [#784](https://codeberg.org/uzu/strudel/pulls/784) +- 2023-11-09T08:45:29+01:00 Update first-effects.mdx by @Ghost in: [#795](https://codeberg.org/uzu/strudel/pulls/795) +- 2023-11-09T08:44:43+01:00 Update pattern-effects.mdx by @Ghost in: [#796](https://codeberg.org/uzu/strudel/pulls/796) +- 2023-11-09T08:43:50+01:00 Update recap.mdx by @Ghost in: [#797](https://codeberg.org/uzu/strudel/pulls/797) +- 2023-11-07T11:53:49+01:00 Update first-sounds.mdx by @Ghost in: [#794](https://codeberg.org/uzu/strudel/pulls/794) +- 2023-11-06T23:17:32+01:00 don't use anchor links for reference by @froos in: [#791](https://codeberg.org/uzu/strudel/pulls/791) +- 2023-11-06T22:40:44+01:00 samples loading shortcuts: by @froos in: [#788](https://codeberg.org/uzu/strudel/pulls/788) +- 2023-11-05T22:47:48+01:00 Fix scope pos + document by @froos in: [#786](https://codeberg.org/uzu/strudel/pulls/786) +- 2023-11-05T22:10:25+01:00 Implement optional hover tooltip with function documentation by @Ghost in: [#783](https://codeberg.org/uzu/strudel/pulls/783) +- 2023-11-05T16:46:06+01:00 Add function params in reference tab by @Ghost in: [#785](https://codeberg.org/uzu/strudel/pulls/785) +- 2023-11-05T12:42:00+01:00 fix: style issues by @froos in: [#781](https://codeberg.org/uzu/strudel/pulls/781) +- 2023-11-05T12:21:28+01:00 add xfade by @froos in: [#780](https://codeberg.org/uzu/strudel/pulls/780) +- 2023-11-02T09:30:26+01:00 Update to Astro 3 by @froos in: [#775](https://codeberg.org/uzu/strudel/pulls/775) +- 2023-11-02T08:57:14+01:00 add vscode bindings by @Ghost in: [#773](https://codeberg.org/uzu/strudel/pulls/773) +- 2023-11-02T08:31:30+01:00 fix: share copy to clipboard + alert by @froos in: [#774](https://codeberg.org/uzu/strudel/pulls/774) +- 2023-11-01T22:22:34+01:00 Fix chunk, add fastChunk and repeatCycles by @yaxu in: [#712](https://codeberg.org/uzu/strudel/pulls/712) +- 2023-11-01T22:12:49+01:00 Document adsr function by @Ghost in: [#767](https://codeberg.org/uzu/strudel/pulls/767) +- 2023-11-01T22:11:49+01:00 Add pick and squeeze functions by @Ghost in: [#771](https://codeberg.org/uzu/strudel/pulls/771) +- 2023-11-01T22:04:00+01:00 Update vite pwa by @froos in: [#772](https://codeberg.org/uzu/strudel/pulls/772) ## october 2023 -- 2023-10-28T23:55:07+02:00 replace strudel.tidalcycles.org with strudel.cc by froos in: [#768](https://codeberg.org/uzu/strudel/pulls/768) -- 2023-10-28T12:52:37+02:00 Document striate function by Ghost in: [#766](https://codeberg.org/uzu/strudel/pulls/766) -- 2023-10-27T23:06:20+02:00 fix zen mode logo overlap by froos in: [#760](https://codeberg.org/uzu/strudel/pulls/760) -- 2023-10-27T23:01:18+02:00 fix: scale offset by froos in: [#764](https://codeberg.org/uzu/strudel/pulls/764) -- 2023-10-27T21:59:35+02:00 Fix addivite synthesis phases by froos in: [#762](https://codeberg.org/uzu/strudel/pulls/762) -- 2023-10-26T16:28:16+02:00 Hydra integration by froos in: [#759](https://codeberg.org/uzu/strudel/pulls/759) -- 2023-10-26T14:14:27+02:00 add play function by froos in: [#758](https://codeberg.org/uzu/strudel/pulls/758) -- 2023-10-26T13:11:00+02:00 Add shabda shortcut by Ghost in: [#740](https://codeberg.org/uzu/strudel/pulls/740) -- 2023-10-26T13:07:23+02:00 mini notation: international alphabets support by Ghost in: [#751](https://codeberg.org/uzu/strudel/pulls/751) -- 2023-10-22T23:00:52+02:00 hopefully fix trainling slashes bug by froos in: [#753](https://codeberg.org/uzu/strudel/pulls/753) -- 2023-10-21T23:38:50+02:00 Fix krill build command in README by Ghost in: [#748](https://codeberg.org/uzu/strudel/pulls/748) -- 2023-10-21T00:20:50+02:00 completely revert config mess by froos in: [#745](https://codeberg.org/uzu/strudel/pulls/745) -- 2023-10-20T22:41:39+02:00 fix: try different trailing slash behavior by froos in: [#744](https://codeberg.org/uzu/strudel/pulls/744) -- 2023-10-20T22:34:03+02:00 fix: trailing slash confusion by froos in: [#743](https://codeberg.org/uzu/strudel/pulls/743) -- 2023-10-20T12:07:04+02:00 [Bug Fix] chooseWith: prevent pattern from stopping audio when selection is >= 1 or < 0 by Ghost in: [#741](https://codeberg.org/uzu/strudel/pulls/741) -- 2023-10-20T11:34:26+02:00 Recipes by froos in: [#742](https://codeberg.org/uzu/strudel/pulls/742) -- 2023-10-13T12:57:24+02:00 vite-vanilla-repl readme fix by froos in: [#737](https://codeberg.org/uzu/strudel/pulls/737) -- 2023-10-10T00:17:59+02:00 Add support for using samples as impulse response buffers for the reverb by Ghost in: [#717](https://codeberg.org/uzu/strudel/pulls/717) -- 2023-10-09T21:43:42+02:00 fix: reverb sampleRate by froos in: [#732](https://codeberg.org/uzu/strudel/pulls/732) -- 2023-10-09T21:34:33+02:00 fix: reverb roomsize not required by froos in: [#731](https://codeberg.org/uzu/strudel/pulls/731) -- 2023-10-08T13:54:52+02:00 Compressor by froos in: [#729](https://codeberg.org/uzu/strudel/pulls/729) -- 2023-10-08T13:25:57+02:00 fix: hashes in urls by froos in: [#728](https://codeberg.org/uzu/strudel/pulls/728) -- 2023-10-07T15:48:06+02:00 consume n with scale by froos in: [#727](https://codeberg.org/uzu/strudel/pulls/727) -- 2023-10-07T00:27:21+02:00 fix: reverb regenerate loophole by froos in: [#726](https://codeberg.org/uzu/strudel/pulls/726) -- 2023-10-05T00:04:17+02:00 Better convolution reverb by generating impulse responses by Ghost in: [#718](https://codeberg.org/uzu/strudel/pulls/718) -- 2023-10-04T10:31:53+02:00 Slider afterthoughts by froos in: [#723](https://codeberg.org/uzu/strudel/pulls/723) -- 2023-10-03T16:39:06+02:00 Add 'white', 'pink' and 'brown' oscillators + refactor synth by Ghost in: [#713](https://codeberg.org/uzu/strudel/pulls/713) -- 2023-10-01T14:20:50+02:00 support mininotation '..' range operator, fixes #715 by yaxu in: [#716](https://codeberg.org/uzu/strudel/pulls/716) -- 2023-10-01T14:15:09+02:00 widgets by froos in: [#714](https://codeberg.org/uzu/strudel/pulls/714) +- 2023-10-28T23:55:07+02:00 replace strudel.tidalcycles.org with strudel.cc by @froos in: [#768](https://codeberg.org/uzu/strudel/pulls/768) +- 2023-10-28T12:52:37+02:00 Document striate function by @Ghost in: [#766](https://codeberg.org/uzu/strudel/pulls/766) +- 2023-10-27T23:06:20+02:00 fix zen mode logo overlap by @froos in: [#760](https://codeberg.org/uzu/strudel/pulls/760) +- 2023-10-27T23:01:18+02:00 fix: scale offset by @froos in: [#764](https://codeberg.org/uzu/strudel/pulls/764) +- 2023-10-27T21:59:35+02:00 Fix addivite synthesis phases by @froos in: [#762](https://codeberg.org/uzu/strudel/pulls/762) +- 2023-10-26T16:28:16+02:00 Hydra integration by @froos in: [#759](https://codeberg.org/uzu/strudel/pulls/759) +- 2023-10-26T14:14:27+02:00 add play function by @froos in: [#758](https://codeberg.org/uzu/strudel/pulls/758) +- 2023-10-26T13:11:00+02:00 Add shabda shortcut by @Ghost in: [#740](https://codeberg.org/uzu/strudel/pulls/740) +- 2023-10-26T13:07:23+02:00 mini notation: international alphabets support by @Ghost in: [#751](https://codeberg.org/uzu/strudel/pulls/751) +- 2023-10-22T23:00:52+02:00 hopefully fix trainling slashes bug by @froos in: [#753](https://codeberg.org/uzu/strudel/pulls/753) +- 2023-10-21T23:38:50+02:00 Fix krill build command in README by @Ghost in: [#748](https://codeberg.org/uzu/strudel/pulls/748) +- 2023-10-21T00:20:50+02:00 completely revert config mess by @froos in: [#745](https://codeberg.org/uzu/strudel/pulls/745) +- 2023-10-20T22:41:39+02:00 fix: try different trailing slash behavior by @froos in: [#744](https://codeberg.org/uzu/strudel/pulls/744) +- 2023-10-20T22:34:03+02:00 fix: trailing slash confusion by @froos in: [#743](https://codeberg.org/uzu/strudel/pulls/743) +- 2023-10-20T12:07:04+02:00 [Bug Fix] chooseWith: prevent pattern from stopping audio when selection is >= 1 or < 0 by @Ghost in: [#741](https://codeberg.org/uzu/strudel/pulls/741) +- 2023-10-20T11:34:26+02:00 Recipes by @froos in: [#742](https://codeberg.org/uzu/strudel/pulls/742) +- 2023-10-13T12:57:24+02:00 vite-vanilla-repl readme fix by @froos in: [#737](https://codeberg.org/uzu/strudel/pulls/737) +- 2023-10-10T00:17:59+02:00 Add support for using samples as impulse response buffers for the reverb by @Ghost in: [#717](https://codeberg.org/uzu/strudel/pulls/717) +- 2023-10-09T21:43:42+02:00 fix: reverb sampleRate by @froos in: [#732](https://codeberg.org/uzu/strudel/pulls/732) +- 2023-10-09T21:34:33+02:00 fix: reverb roomsize not required by @froos in: [#731](https://codeberg.org/uzu/strudel/pulls/731) +- 2023-10-08T13:54:52+02:00 Compressor by @froos in: [#729](https://codeberg.org/uzu/strudel/pulls/729) +- 2023-10-08T13:25:57+02:00 fix: hashes in urls by @froos in: [#728](https://codeberg.org/uzu/strudel/pulls/728) +- 2023-10-07T15:48:06+02:00 consume n with scale by @froos in: [#727](https://codeberg.org/uzu/strudel/pulls/727) +- 2023-10-07T00:27:21+02:00 fix: reverb regenerate loophole by @froos in: [#726](https://codeberg.org/uzu/strudel/pulls/726) +- 2023-10-05T00:04:17+02:00 Better convolution reverb by generating impulse responses by @Ghost in: [#718](https://codeberg.org/uzu/strudel/pulls/718) +- 2023-10-04T10:31:53+02:00 Slider afterthoughts by @froos in: [#723](https://codeberg.org/uzu/strudel/pulls/723) +- 2023-10-03T16:39:06+02:00 Add 'white', 'pink' and 'brown' oscillators + refactor synth by @Ghost in: [#713](https://codeberg.org/uzu/strudel/pulls/713) +- 2023-10-01T14:20:50+02:00 support mininotation '..' range operator, fixes #715 by @yaxu in: [#716](https://codeberg.org/uzu/strudel/pulls/716) +- 2023-10-01T14:15:09+02:00 widgets by @froos in: [#714](https://codeberg.org/uzu/strudel/pulls/714) ## september 2023 -- 2023-09-28T11:03:09+02:00 Midi in by froos in: [#699](https://codeberg.org/uzu/strudel/pulls/699) -- 2023-09-27T22:53:49+02:00 add midi clock support by froos in: [#710](https://codeberg.org/uzu/strudel/pulls/710) -- 2023-09-25T23:36:10+02:00 Update bournemouth.mdx by Ghost in: [#708](https://codeberg.org/uzu/strudel/pulls/708) -- 2023-09-25T23:06:30+02:00 add dough function for raw dsp by froos in: [#707](https://codeberg.org/uzu/strudel/pulls/707) -- 2023-09-17T15:53:27+02:00 Update tauri.yml workflow file by Ghost in: [#705](https://codeberg.org/uzu/strudel/pulls/705) -- 2023-09-17T11:05:06+02:00 Adding vibrato to base oscillators by Ghost in: [#693](https://codeberg.org/uzu/strudel/pulls/693) -- 2023-09-17T08:27:36+02:00 Adding loop points and thus wavetable synthesis by Ghost in: [#698](https://codeberg.org/uzu/strudel/pulls/698) -- 2023-09-16T02:03:49+02:00 Adding filter envelopes and filter order selection by Ghost in: [#692](https://codeberg.org/uzu/strudel/pulls/692) -- 2023-09-09T11:19:58+02:00 Add logging from tauri by Ghost in: [#697](https://codeberg.org/uzu/strudel/pulls/697) -- 2023-09-05T00:33:54+02:00 Direct OSC Support in Tauri by Ghost in: [#694](https://codeberg.org/uzu/strudel/pulls/694) -- 2023-09-03T22:19:22+02:00 fix MIDI CC messages by Ghost in: [#690](https://codeberg.org/uzu/strudel/pulls/690) -- 2023-09-03T10:22:15+02:00 add sleep timer + improve message iterating by Ghost in: [#688](https://codeberg.org/uzu/strudel/pulls/688) +- 2023-09-28T11:03:09+02:00 Midi in by @froos in: [#699](https://codeberg.org/uzu/strudel/pulls/699) +- 2023-09-27T22:53:49+02:00 add midi clock support by @froos in: [#710](https://codeberg.org/uzu/strudel/pulls/710) +- 2023-09-25T23:36:10+02:00 Update bournemouth.mdx by @Ghost in: [#708](https://codeberg.org/uzu/strudel/pulls/708) +- 2023-09-25T23:06:30+02:00 add dough function for raw dsp by @froos in: [#707](https://codeberg.org/uzu/strudel/pulls/707) +- 2023-09-17T15:53:27+02:00 Update tauri.yml workflow file by @Ghost in: [#705](https://codeberg.org/uzu/strudel/pulls/705) +- 2023-09-17T11:05:06+02:00 Adding vibrato to base oscillators by @Ghost in: [#693](https://codeberg.org/uzu/strudel/pulls/693) +- 2023-09-17T08:27:36+02:00 Adding loop points and thus wavetable synthesis by @Ghost in: [#698](https://codeberg.org/uzu/strudel/pulls/698) +- 2023-09-16T02:03:49+02:00 Adding filter envelopes and filter order selection by @Ghost in: [#692](https://codeberg.org/uzu/strudel/pulls/692) +- 2023-09-09T11:19:58+02:00 Add logging from tauri by @Ghost in: [#697](https://codeberg.org/uzu/strudel/pulls/697) +- 2023-09-05T00:33:54+02:00 Direct OSC Support in Tauri by @Ghost in: [#694](https://codeberg.org/uzu/strudel/pulls/694) +- 2023-09-03T22:19:22+02:00 fix MIDI CC messages by @Ghost in: [#690](https://codeberg.org/uzu/strudel/pulls/690) +- 2023-09-03T10:22:15+02:00 add sleep timer + improve message iterating by @Ghost in: [#688](https://codeberg.org/uzu/strudel/pulls/688) ## august 2023 -- 2023-08-31T13:00:31+02:00 ZZFX Synth support by Ghost in: [#684](https://codeberg.org/uzu/strudel/pulls/684) -- 2023-08-31T12:36:37+02:00 Wave Selection and Global Envelope on the FM Synth Modulator by Ghost in: [#683](https://codeberg.org/uzu/strudel/pulls/683) -- 2023-08-31T05:52:17+02:00 Create Midi Integration for Tauri Desktop app by Ghost in: [#685](https://codeberg.org/uzu/strudel/pulls/685) -- 2023-08-31T05:32:16+02:00 teletext theme + fonts by froos in: [#681](https://codeberg.org/uzu/strudel/pulls/681) -- 2023-08-31T05:20:55+02:00 control osc partial count with n by froos in: [#674](https://codeberg.org/uzu/strudel/pulls/674) -- 2023-08-27T22:18:06+02:00 add emoji support by froos in: [#680](https://codeberg.org/uzu/strudel/pulls/680) -- 2023-08-27T16:12:32+02:00 Pianoroll improvements by froos in: [#679](https://codeberg.org/uzu/strudel/pulls/679) -- 2023-08-26T21:22:17+02:00 Scope by froos in: [#677](https://codeberg.org/uzu/strudel/pulls/677) -- 2023-08-23T21:50:50+02:00 Midi time fixes by Ghost in: [#668](https://codeberg.org/uzu/strudel/pulls/668) -- 2023-08-20T23:19:08+02:00 basic fm by froos in: [#669](https://codeberg.org/uzu/strudel/pulls/669) -- 2023-08-18T23:56:20+02:00 togglable panel position by froos in: [#667](https://codeberg.org/uzu/strudel/pulls/667) -- 2023-08-18T15:59:20+02:00 fix osc bundle timestamp glitches caused by drifting clock by Ghost in: [#666](https://codeberg.org/uzu/strudel/pulls/666) -- 2023-08-17T11:36:07+02:00 superdough: encapsulates web audio output by froos in: [#664](https://codeberg.org/uzu/strudel/pulls/664) -- 2023-08-11T00:06:21+02:00 fix: always run previous trigger by froos in: [#660](https://codeberg.org/uzu/strudel/pulls/660) -- 2023-08-10T23:54:58+02:00 fix: welcome message for latestCode by froos in: [#659](https://codeberg.org/uzu/strudel/pulls/659) -- 2023-08-07T07:45:26+02:00 [Bug Fix] Midi: Don't treat note 0 as false by Ghost in: [#657](https://codeberg.org/uzu/strudel/pulls/657) +- 2023-08-31T13:00:31+02:00 ZZFX Synth support by @Ghost in: [#684](https://codeberg.org/uzu/strudel/pulls/684) +- 2023-08-31T12:36:37+02:00 Wave Selection and Global Envelope on the FM Synth Modulator by @Ghost in: [#683](https://codeberg.org/uzu/strudel/pulls/683) +- 2023-08-31T05:52:17+02:00 Create Midi Integration for Tauri Desktop app by @Ghost in: [#685](https://codeberg.org/uzu/strudel/pulls/685) +- 2023-08-31T05:32:16+02:00 teletext theme + fonts by @froos in: [#681](https://codeberg.org/uzu/strudel/pulls/681) +- 2023-08-31T05:20:55+02:00 control osc partial count with n by @froos in: [#674](https://codeberg.org/uzu/strudel/pulls/674) +- 2023-08-27T22:18:06+02:00 add emoji support by @froos in: [#680](https://codeberg.org/uzu/strudel/pulls/680) +- 2023-08-27T16:12:32+02:00 Pianoroll improvements by @froos in: [#679](https://codeberg.org/uzu/strudel/pulls/679) +- 2023-08-26T21:22:17+02:00 Scope by @froos in: [#677](https://codeberg.org/uzu/strudel/pulls/677) +- 2023-08-23T21:50:50+02:00 Midi time fixes by @Ghost in: [#668](https://codeberg.org/uzu/strudel/pulls/668) +- 2023-08-20T23:19:08+02:00 basic fm by @froos in: [#669](https://codeberg.org/uzu/strudel/pulls/669) +- 2023-08-18T23:56:20+02:00 togglable panel position by @froos in: [#667](https://codeberg.org/uzu/strudel/pulls/667) +- 2023-08-18T15:59:20+02:00 fix osc bundle timestamp glitches caused by drifting clock by @Ghost in: [#666](https://codeberg.org/uzu/strudel/pulls/666) +- 2023-08-17T11:36:07+02:00 superdough: encapsulates web audio output by @froos in: [#664](https://codeberg.org/uzu/strudel/pulls/664) +- 2023-08-11T00:06:21+02:00 fix: always run previous trigger by @froos in: [#660](https://codeberg.org/uzu/strudel/pulls/660) +- 2023-08-10T23:54:58+02:00 fix: welcome message for latestCode by @froos in: [#659](https://codeberg.org/uzu/strudel/pulls/659) +- 2023-08-07T07:45:26+02:00 [Bug Fix] Midi: Don't treat note 0 as false by @Ghost in: [#657](https://codeberg.org/uzu/strudel/pulls/657) ## july 2023 -- 2023-07-29T09:06:18+02:00 [Bug Fix] Account for numeral notation when converting to midi by Ghost in: [#656](https://codeberg.org/uzu/strudel/pulls/656) -- 2023-07-23T22:18:49+02:00 ireal voicings by froos in: [#653](https://codeberg.org/uzu/strudel/pulls/653) -- 2023-07-22T09:30:21+02:00 Understand pitch by froos in: [#652](https://codeberg.org/uzu/strudel/pulls/652) -- 2023-07-17T23:42:59+02:00 update vitest by froos in: [#651](https://codeberg.org/uzu/strudel/pulls/651) -- 2023-07-17T23:34:33+02:00 stateless voicings + tonleiter lib by froos in: [#647](https://codeberg.org/uzu/strudel/pulls/647) -- 2023-07-17T17:57:17+02:00 FIXES: TODO in rotateChroma by Ghost in: [#650](https://codeberg.org/uzu/strudel/pulls/650) -- 2023-07-10T19:07:45+02:00 slice: list mode by froos in: [#645](https://codeberg.org/uzu/strudel/pulls/645) -- 2023-07-04T23:50:30+02:00 Delete old packages by froos in: [#639](https://codeberg.org/uzu/strudel/pulls/639) -- 2023-07-04T23:38:42+02:00 Adaptive Highlighting by froos in: [#634](https://codeberg.org/uzu/strudel/pulls/634) -- 2023-07-04T21:55:57+02:00 More work on highlight IDs by Ghost in: [#636](https://codeberg.org/uzu/strudel/pulls/636) -- 2023-07-04T18:44:48+02:00 snapshot tests: sort haps by part by froos in: [#637](https://codeberg.org/uzu/strudel/pulls/637) +- 2023-07-29T09:06:18+02:00 [Bug Fix] Account for numeral notation when converting to midi by @Ghost in: [#656](https://codeberg.org/uzu/strudel/pulls/656) +- 2023-07-23T22:18:49+02:00 ireal voicings by @froos in: [#653](https://codeberg.org/uzu/strudel/pulls/653) +- 2023-07-22T09:30:21+02:00 Understand pitch by @froos in: [#652](https://codeberg.org/uzu/strudel/pulls/652) +- 2023-07-17T23:42:59+02:00 update vitest by @froos in: [#651](https://codeberg.org/uzu/strudel/pulls/651) +- 2023-07-17T23:34:33+02:00 stateless voicings + tonleiter lib by @froos in: [#647](https://codeberg.org/uzu/strudel/pulls/647) +- 2023-07-17T17:57:17+02:00 FIXES: TODO in rotateChroma by @Ghost in: [#650](https://codeberg.org/uzu/strudel/pulls/650) +- 2023-07-10T19:07:45+02:00 slice: list mode by @froos in: [#645](https://codeberg.org/uzu/strudel/pulls/645) +- 2023-07-04T23:50:30+02:00 Delete old packages by @froos in: [#639](https://codeberg.org/uzu/strudel/pulls/639) +- 2023-07-04T23:38:42+02:00 Adaptive Highlighting by @froos in: [#634](https://codeberg.org/uzu/strudel/pulls/634) +- 2023-07-04T21:55:57+02:00 More work on highlight IDs by @Ghost in: [#636](https://codeberg.org/uzu/strudel/pulls/636) +- 2023-07-04T18:44:48+02:00 snapshot tests: sort haps by part by @froos in: [#637](https://codeberg.org/uzu/strudel/pulls/637) ## juny 2023 -- 2023-06-30T22:47:40+02:00 fix: update canvas size on window resize by froos in: [#631](https://codeberg.org/uzu/strudel/pulls/631) -- 2023-06-30T22:45:41+02:00 fix: out of range error by froos in: [#630](https://codeberg.org/uzu/strudel/pulls/630) -- 2023-06-30T08:14:40+02:00 fix: midi clock drift by froos in: [#627](https://codeberg.org/uzu/strudel/pulls/627) -- 2023-06-29T22:02:28+02:00 desktop: play samples from disk by froos in: [#621](https://codeberg.org/uzu/strudel/pulls/621) -- 2023-06-29T21:59:43+02:00 cps dependent functions by froos in: [#620](https://codeberg.org/uzu/strudel/pulls/620) -- 2023-06-26T22:45:04+02:00 Fix typo on packages.mdx by Ghost in: [#520](https://codeberg.org/uzu/strudel/pulls/520) -- 2023-06-26T22:35:02+02:00 patterning ui settings by froos in: [#606](https://codeberg.org/uzu/strudel/pulls/606) -- 2023-06-26T22:32:46+02:00 add spiral viz by froos in: [#614](https://codeberg.org/uzu/strudel/pulls/614) -- 2023-06-26T22:17:46+02:00 tauri desktop app by Ghost in: [#613](https://codeberg.org/uzu/strudel/pulls/613) -- 2023-06-23T09:59:43+02:00 fix: doc links by froos in: [#612](https://codeberg.org/uzu/strudel/pulls/612) -- 2023-06-23T09:55:18+02:00 clip now works like legato in tidal by froos in: [#598](https://codeberg.org/uzu/strudel/pulls/598) -- 2023-06-18T10:36:19+02:00 fix: flatten scale lists by froos in: [#605](https://codeberg.org/uzu/strudel/pulls/605) -- 2023-06-18T10:36:17+02:00 tonal fixes by froos in: [#607](https://codeberg.org/uzu/strudel/pulls/607) -- 2023-06-15T12:51:40+02:00 editor: enable line wrapping by Ghost in: [#581](https://codeberg.org/uzu/strudel/pulls/581) -- 2023-06-15T10:22:46+02:00 add ratio function by froos in: [#602](https://codeberg.org/uzu/strudel/pulls/602) -- 2023-06-12T23:24:29+02:00 enable auto-completion by Ghost in: [#588](https://codeberg.org/uzu/strudel/pulls/588) -- 2023-06-11T22:04:17+02:00 improve cursor by froos in: [#597](https://codeberg.org/uzu/strudel/pulls/597) -- 2023-06-11T20:00:45+02:00 Solmization added by Ghost in: [#570](https://codeberg.org/uzu/strudel/pulls/570) -- 2023-06-11T19:45:00+02:00 fix: division by zero by froos in: [#591](https://codeberg.org/uzu/strudel/pulls/591) -- 2023-06-11T19:44:43+02:00 fix: allow f for flat notes like tidal by froos in: [#593](https://codeberg.org/uzu/strudel/pulls/593) -- 2023-06-11T19:44:41+02:00 Fix option dot by froos in: [#596](https://codeberg.org/uzu/strudel/pulls/596) -- 2023-06-09T21:02:12+02:00 New Workshop by froos in: [#587](https://codeberg.org/uzu/strudel/pulls/587) -- 2023-06-09T17:31:58+02:00 Music metadata by Ghost in: [#580](https://codeberg.org/uzu/strudel/pulls/580) -- 2023-06-08T09:33:34+02:00 learn/tonal: fix typo in "scaleTran[s]pose" by Ghost in: [#585](https://codeberg.org/uzu/strudel/pulls/585) -- 2023-06-07T20:19:11+02:00 repl: add option to display line numbers by Ghost in: [#582](https://codeberg.org/uzu/strudel/pulls/582) -- 2023-06-05T21:35:49+02:00 Vanilla JS Refactoring by froos in: [#563](https://codeberg.org/uzu/strudel/pulls/563) +- 2023-06-30T22:47:40+02:00 fix: update canvas size on window resize by @froos in: [#631](https://codeberg.org/uzu/strudel/pulls/631) +- 2023-06-30T22:45:41+02:00 fix: out of range error by @froos in: [#630](https://codeberg.org/uzu/strudel/pulls/630) +- 2023-06-30T08:14:40+02:00 fix: midi clock drift by @froos in: [#627](https://codeberg.org/uzu/strudel/pulls/627) +- 2023-06-29T22:02:28+02:00 desktop: play samples from disk by @froos in: [#621](https://codeberg.org/uzu/strudel/pulls/621) +- 2023-06-29T21:59:43+02:00 cps dependent functions by @froos in: [#620](https://codeberg.org/uzu/strudel/pulls/620) +- 2023-06-26T22:45:04+02:00 Fix typo on packages.mdx by @Ghost in: [#520](https://codeberg.org/uzu/strudel/pulls/520) +- 2023-06-26T22:35:02+02:00 patterning ui settings by @froos in: [#606](https://codeberg.org/uzu/strudel/pulls/606) +- 2023-06-26T22:32:46+02:00 add spiral viz by @froos in: [#614](https://codeberg.org/uzu/strudel/pulls/614) +- 2023-06-26T22:17:46+02:00 tauri desktop app by @Ghost in: [#613](https://codeberg.org/uzu/strudel/pulls/613) +- 2023-06-23T09:59:43+02:00 fix: doc links by @froos in: [#612](https://codeberg.org/uzu/strudel/pulls/612) +- 2023-06-23T09:55:18+02:00 clip now works like legato in tidal by @froos in: [#598](https://codeberg.org/uzu/strudel/pulls/598) +- 2023-06-18T10:36:19+02:00 fix: flatten scale lists by @froos in: [#605](https://codeberg.org/uzu/strudel/pulls/605) +- 2023-06-18T10:36:17+02:00 tonal fixes by @froos in: [#607](https://codeberg.org/uzu/strudel/pulls/607) +- 2023-06-15T12:51:40+02:00 editor: enable line wrapping by @Ghost in: [#581](https://codeberg.org/uzu/strudel/pulls/581) +- 2023-06-15T10:22:46+02:00 add ratio function by @froos in: [#602](https://codeberg.org/uzu/strudel/pulls/602) +- 2023-06-12T23:24:29+02:00 enable auto-completion by @Ghost in: [#588](https://codeberg.org/uzu/strudel/pulls/588) +- 2023-06-11T22:04:17+02:00 improve cursor by @froos in: [#597](https://codeberg.org/uzu/strudel/pulls/597) +- 2023-06-11T20:00:45+02:00 Solmization added by @Ghost in: [#570](https://codeberg.org/uzu/strudel/pulls/570) +- 2023-06-11T19:45:00+02:00 fix: division by zero by @froos in: [#591](https://codeberg.org/uzu/strudel/pulls/591) +- 2023-06-11T19:44:43+02:00 fix: allow f for flat notes like tidal by @froos in: [#593](https://codeberg.org/uzu/strudel/pulls/593) +- 2023-06-11T19:44:41+02:00 Fix option dot by @froos in: [#596](https://codeberg.org/uzu/strudel/pulls/596) +- 2023-06-09T21:02:12+02:00 New Workshop by @froos in: [#587](https://codeberg.org/uzu/strudel/pulls/587) +- 2023-06-09T17:31:58+02:00 Music metadata by @Ghost in: [#580](https://codeberg.org/uzu/strudel/pulls/580) +- 2023-06-08T09:33:34+02:00 learn/tonal: fix typo in "scaleTran[s]pose" by @Ghost in: [#585](https://codeberg.org/uzu/strudel/pulls/585) +- 2023-06-07T20:19:11+02:00 repl: add option to display line numbers by @Ghost in: [#582](https://codeberg.org/uzu/strudel/pulls/582) +- 2023-06-05T21:35:49+02:00 Vanilla JS Refactoring by @froos in: [#563](https://codeberg.org/uzu/strudel/pulls/563) ## may 2023 -- 2023-05-05T08:34:37+02:00 Patchday by froos in: [#559](https://codeberg.org/uzu/strudel/pulls/559) -- 2023-05-02T21:48:33+02:00 add basic triads and guidetone voicings by froos in: [#557](https://codeberg.org/uzu/strudel/pulls/557) +- 2023-05-05T08:34:37+02:00 Patchday by @froos in: [#559](https://codeberg.org/uzu/strudel/pulls/559) +- 2023-05-02T21:48:33+02:00 add basic triads and guidetone voicings by @froos in: [#557](https://codeberg.org/uzu/strudel/pulls/557) ## april 2023 -- 2023-04-28T12:46:56+02:00 fix: make soundfonts import dynamic by froos in: [#556](https://codeberg.org/uzu/strudel/pulls/556) -- 2023-04-22T15:43:22+02:00 fix: colorable highlighting by froos in: [#553](https://codeberg.org/uzu/strudel/pulls/553) -- 2023-04-06T00:11:02+02:00 fix: load soundfonts in prebake by froos in: [#550](https://codeberg.org/uzu/strudel/pulls/550) +- 2023-04-28T12:46:56+02:00 fix: make soundfonts import dynamic by @froos in: [#556](https://codeberg.org/uzu/strudel/pulls/556) +- 2023-04-22T15:43:22+02:00 fix: colorable highlighting by @froos in: [#553](https://codeberg.org/uzu/strudel/pulls/553) +- 2023-04-06T00:11:02+02:00 fix: load soundfonts in prebake by @froos in: [#550](https://codeberg.org/uzu/strudel/pulls/550) ## march 2023 -- 2023-03-29T22:23:07+02:00 fix: reset time on stop by froos in: [#548](https://codeberg.org/uzu/strudel/pulls/548) -- 2023-03-29T22:16:38+02:00 fix: allow whitespace at the end of a mini pattern by froos in: [#547](https://codeberg.org/uzu/strudel/pulls/547) -- 2023-03-24T22:10:56+01:00 add firacode font by froos in: [#544](https://codeberg.org/uzu/strudel/pulls/544) -- 2023-03-24T12:54:20+01:00 feat: add loader bar to animate loading state by froos in: [#542](https://codeberg.org/uzu/strudel/pulls/542) -- 2023-03-23T22:39:27+01:00 do not reset cps before eval #517 by froos in: [#539](https://codeberg.org/uzu/strudel/pulls/539) -- 2023-03-23T22:27:31+01:00 improve initial loading + wait before eval by froos in: [#538](https://codeberg.org/uzu/strudel/pulls/538) -- 2023-03-23T21:40:20+01:00 fix period key for dvorak + remove duplicated code by froos in: [#537](https://codeberg.org/uzu/strudel/pulls/537) -- 2023-03-23T11:44:56+01:00 Update lerna by froos in: [#535](https://codeberg.org/uzu/strudel/pulls/535) -- 2023-03-23T10:21:55+01:00 feat: add freq support to gm soundfonts by froos in: [#534](https://codeberg.org/uzu/strudel/pulls/534) -- 2023-03-23T10:18:58+01:00 Maintain random seed state in parser, not globally by Ghost in: [#531](https://codeberg.org/uzu/strudel/pulls/531) -- 2023-03-21T22:35:18+01:00 FIXES: alias pm for polymeter by Ghost in: [#527](https://codeberg.org/uzu/strudel/pulls/527) -- 2023-03-21T22:29:07+01:00 fix(footer): fix link to tidalcycles by julienbouquillon in: [#529](https://codeberg.org/uzu/strudel/pulls/529) -- 2023-03-18T20:25:21+01:00 Update intro.mdx by Ghost in: [#525](https://codeberg.org/uzu/strudel/pulls/525) -- 2023-03-18T20:24:38+01:00 Update samples.mdx by Ghost in: [#524](https://codeberg.org/uzu/strudel/pulls/524) -- 2023-03-17T09:03:11+01:00 fix: envelopes in chrome by froos in: [#521](https://codeberg.org/uzu/strudel/pulls/521) -- 2023-03-16T16:13:30+01:00 registerSound API + improved sounds tab + regroup soundfonts by froos in: [#516](https://codeberg.org/uzu/strudel/pulls/516) -- 2023-03-14T21:54:53+01:00 add 2 illegible fonts by froos in: [#518](https://codeberg.org/uzu/strudel/pulls/518) -- 2023-03-06T22:55:00+01:00 Update README.md by Ghost in: [#474](https://codeberg.org/uzu/strudel/pulls/474) -- 2023-03-05T14:52:30+01:00 add arrange function by froos in: [#508](https://codeberg.org/uzu/strudel/pulls/508) -- 2023-03-05T14:29:08+01:00 update react to 18 by froos in: [#514](https://codeberg.org/uzu/strudel/pulls/514) -- 2023-03-04T19:06:18+01:00 Support list syntax in mininotation by yaxu in: [#512](https://codeberg.org/uzu/strudel/pulls/512) -- 2023-03-03T12:40:23+01:00 can now use : as a replacement for space in scales by froos in: [#502](https://codeberg.org/uzu/strudel/pulls/502) -- 2023-03-02T15:44:41+01:00 Reinstate slice and splice by yaxu in: [#500](https://codeberg.org/uzu/strudel/pulls/500) -- 2023-03-02T14:49:30+01:00 fix: nano-repl highlighting by froos in: [#501](https://codeberg.org/uzu/strudel/pulls/501) -- 2023-03-02T14:17:13+01:00 Add control aliases by yaxu in: [#497](https://codeberg.org/uzu/strudel/pulls/497) -- 2023-03-01T09:27:27+01:00 implement cps in scheduler by froos in: [#493](https://codeberg.org/uzu/strudel/pulls/493) +- 2023-03-29T22:23:07+02:00 fix: reset time on stop by @froos in: [#548](https://codeberg.org/uzu/strudel/pulls/548) +- 2023-03-29T22:16:38+02:00 fix: allow whitespace at the end of a mini pattern by @froos in: [#547](https://codeberg.org/uzu/strudel/pulls/547) +- 2023-03-24T22:10:56+01:00 add firacode font by @froos in: [#544](https://codeberg.org/uzu/strudel/pulls/544) +- 2023-03-24T12:54:20+01:00 feat: add loader bar to animate loading state by @froos in: [#542](https://codeberg.org/uzu/strudel/pulls/542) +- 2023-03-23T22:39:27+01:00 do not reset cps before eval #517 by @froos in: [#539](https://codeberg.org/uzu/strudel/pulls/539) +- 2023-03-23T22:27:31+01:00 improve initial loading + wait before eval by @froos in: [#538](https://codeberg.org/uzu/strudel/pulls/538) +- 2023-03-23T21:40:20+01:00 fix period key for dvorak + remove duplicated code by @froos in: [#537](https://codeberg.org/uzu/strudel/pulls/537) +- 2023-03-23T11:44:56+01:00 Update lerna by @froos in: [#535](https://codeberg.org/uzu/strudel/pulls/535) +- 2023-03-23T10:21:55+01:00 feat: add freq support to gm soundfonts by @froos in: [#534](https://codeberg.org/uzu/strudel/pulls/534) +- 2023-03-23T10:18:58+01:00 Maintain random seed state in parser, not globally by @Ghost in: [#531](https://codeberg.org/uzu/strudel/pulls/531) +- 2023-03-21T22:35:18+01:00 FIXES: alias pm for polymeter by @Ghost in: [#527](https://codeberg.org/uzu/strudel/pulls/527) +- 2023-03-21T22:29:07+01:00 fix(footer): fix link to tidalcycles by @julienbouquillon in: [#529](https://codeberg.org/uzu/strudel/pulls/529) +- 2023-03-18T20:25:21+01:00 Update intro.mdx by @Ghost in: [#525](https://codeberg.org/uzu/strudel/pulls/525) +- 2023-03-18T20:24:38+01:00 Update samples.mdx by @Ghost in: [#524](https://codeberg.org/uzu/strudel/pulls/524) +- 2023-03-17T09:03:11+01:00 fix: envelopes in chrome by @froos in: [#521](https://codeberg.org/uzu/strudel/pulls/521) +- 2023-03-16T16:13:30+01:00 registerSound API + improved sounds tab + regroup soundfonts by @froos in: [#516](https://codeberg.org/uzu/strudel/pulls/516) +- 2023-03-14T21:54:53+01:00 add 2 illegible fonts by @froos in: [#518](https://codeberg.org/uzu/strudel/pulls/518) +- 2023-03-06T22:55:00+01:00 Update README.md by @Ghost in: [#474](https://codeberg.org/uzu/strudel/pulls/474) +- 2023-03-05T14:52:30+01:00 add arrange function by @froos in: [#508](https://codeberg.org/uzu/strudel/pulls/508) +- 2023-03-05T14:29:08+01:00 update react to 18 by @froos in: [#514](https://codeberg.org/uzu/strudel/pulls/514) +- 2023-03-04T19:06:18+01:00 Support list syntax in mininotation by @yaxu in: [#512](https://codeberg.org/uzu/strudel/pulls/512) +- 2023-03-03T12:40:23+01:00 can now use : as a replacement for space in scales by @froos in: [#502](https://codeberg.org/uzu/strudel/pulls/502) +- 2023-03-02T15:44:41+01:00 Reinstate slice and splice by @yaxu in: [#500](https://codeberg.org/uzu/strudel/pulls/500) +- 2023-03-02T14:49:30+01:00 fix: nano-repl highlighting by @froos in: [#501](https://codeberg.org/uzu/strudel/pulls/501) +- 2023-03-02T14:17:13+01:00 Add control aliases by @yaxu in: [#497](https://codeberg.org/uzu/strudel/pulls/497) +- 2023-03-01T09:27:27+01:00 implement cps in scheduler by @froos in: [#493](https://codeberg.org/uzu/strudel/pulls/493) ## february 2023 -- 2023-02-28T23:06:29+01:00 react style fixes by froos in: [#491](https://codeberg.org/uzu/strudel/pulls/491) -- 2023-02-28T22:57:20+01:00 refactor react package by froos in: [#490](https://codeberg.org/uzu/strudel/pulls/490) -- 2023-02-27T23:47:34+01:00 add algolia creds + optimize sidebar for crawling by froos in: [#488](https://codeberg.org/uzu/strudel/pulls/488) -- 2023-02-27T19:28:10+01:00 fix app height by froos in: [#485](https://codeberg.org/uzu/strudel/pulls/485) -- 2023-02-27T16:04:21+01:00 Revert "Another attempt at composable functions - WIP (#390)" by froos in: [#484](https://codeberg.org/uzu/strudel/pulls/484) -- 2023-02-27T15:45:20+01:00 Update mini-notation.mdx by yaxu in: [#365](https://codeberg.org/uzu/strudel/pulls/365) -- 2023-02-27T12:52:09+01:00 docs: packages + offline by froos in: [#482](https://codeberg.org/uzu/strudel/pulls/482) -- 2023-02-25T14:26:49+01:00 Fix array args by froos in: [#480](https://codeberg.org/uzu/strudel/pulls/480) -- 2023-02-25T12:33:22+01:00 midi cc support by froos in: [#478](https://codeberg.org/uzu/strudel/pulls/478) -- 2023-02-23T00:11:05+01:00 fix: hash links by froos in: [#473](https://codeberg.org/uzu/strudel/pulls/473) -- 2023-02-22T22:54:39+01:00 settings tab with vim / emacs modes + additional themes and fonts by froos in: [#467](https://codeberg.org/uzu/strudel/pulls/467) -- 2023-02-22T22:53:22+01:00 Update input-output.mdx by Ghost in: [#471](https://codeberg.org/uzu/strudel/pulls/471) -- 2023-02-22T22:52:50+01:00 FIXES: freqs instead of pitches by Ghost in: [#464](https://codeberg.org/uzu/strudel/pulls/464) -- 2023-02-22T20:01:00+01:00 fix: osc should not return a promise by froos in: [#472](https://codeberg.org/uzu/strudel/pulls/472) -- 2023-02-22T12:51:31+01:00 slice and splice by yaxu in: [#466](https://codeberg.org/uzu/strudel/pulls/466) -- 2023-02-18T01:00:19+01:00 weave and weaveWith by yaxu in: [#465](https://codeberg.org/uzu/strudel/pulls/465) -- 2023-02-17T00:15:21+01:00 Composable functions by yaxu in: [#390](https://codeberg.org/uzu/strudel/pulls/390) -- 2023-02-16T20:38:45+01:00 FIXES: Warning about jsxBracketSameLine deprecation by Ghost in: [#461](https://codeberg.org/uzu/strudel/pulls/461) -- 2023-02-14T22:11:02+01:00 Update synths.mdx by Ghost in: [#438](https://codeberg.org/uzu/strudel/pulls/438) -- 2023-02-14T19:54:42+01:00 Update mini-notation.mdx by Ghost in: [#437](https://codeberg.org/uzu/strudel/pulls/437) -- 2023-02-14T19:54:25+01:00 Update code.mdx by Ghost in: [#436](https://codeberg.org/uzu/strudel/pulls/436) -- 2023-02-13T00:43:29+01:00 Fix anchors by froos in: [#433](https://codeberg.org/uzu/strudel/pulls/433) -- 2023-02-11T21:02:11+01:00 autocomplete preparations by froos in: [#427](https://codeberg.org/uzu/strudel/pulls/427) -- 2023-02-10T23:14:48+01:00 Themes by froos in: [#431](https://codeberg.org/uzu/strudel/pulls/431) -- 2023-02-09T19:22:56+01:00 minirepl: add keyboard shortcuts by froos in: [#429](https://codeberg.org/uzu/strudel/pulls/429) -- 2023-02-09T08:56:42+01:00 add cdn.freesound to cache list by froos in: [#425](https://codeberg.org/uzu/strudel/pulls/425) -- 2023-02-08T20:36:00+01:00 add more offline caching by froos in: [#421](https://codeberg.org/uzu/strudel/pulls/421) -- 2023-02-07T22:08:01+01:00 add caching strategy for missing file types + cache all samples loaded from github by froos in: [#419](https://codeberg.org/uzu/strudel/pulls/419) -- 2023-02-06T23:29:57+01:00 PWA with offline support by froos in: [#417](https://codeberg.org/uzu/strudel/pulls/417) -- 2023-02-05T17:27:32+01:00 improve samples doc by froos in: [#411](https://codeberg.org/uzu/strudel/pulls/411) -- 2023-02-05T17:27:10+01:00 google gtfo by froos in: [#413](https://codeberg.org/uzu/strudel/pulls/413) -- 2023-02-05T16:27:59+01:00 improve effects doc by froos in: [#409](https://codeberg.org/uzu/strudel/pulls/409) -- 2023-02-05T14:56:03+01:00 Update effects.mdx by Ghost in: [#410](https://codeberg.org/uzu/strudel/pulls/410) -- 2023-02-03T19:54:47+01:00 add shabda doc by froos in: [#407](https://codeberg.org/uzu/strudel/pulls/407) -- 2023-02-02T21:45:56+01:00 fix: share url on subpath by froos in: [#405](https://codeberg.org/uzu/strudel/pulls/405) -- 2023-02-02T21:35:45+01:00 update csound + fix sound output by froos in: [#404](https://codeberg.org/uzu/strudel/pulls/404) -- 2023-02-02T19:53:36+01:00 pin @csound/browser to 6.18.3 + bump by froos in: [#403](https://codeberg.org/uzu/strudel/pulls/403) -- 2023-02-01T22:47:27+01:00 release webaudio by froos in: [#400](https://codeberg.org/uzu/strudel/pulls/400) -- 2023-02-01T22:45:16+01:00 can now await initAudio + initAudioOnFirstClick by froos in: [#399](https://codeberg.org/uzu/strudel/pulls/399) -- 2023-02-01T22:32:18+01:00 fix: minirepl styles by froos in: [#398](https://codeberg.org/uzu/strudel/pulls/398) -- 2023-02-01T22:17:19+01:00 proper builds + use pnpm workspaces by froos in: [#396](https://codeberg.org/uzu/strudel/pulls/396) -- 2023-02-01T16:49:55+01:00 add pattern methods hurry, press and pressBy by yaxu in: [#397](https://codeberg.org/uzu/strudel/pulls/397) +- 2023-02-28T23:06:29+01:00 react style fixes by @froos in: [#491](https://codeberg.org/uzu/strudel/pulls/491) +- 2023-02-28T22:57:20+01:00 refactor react package by @froos in: [#490](https://codeberg.org/uzu/strudel/pulls/490) +- 2023-02-27T23:47:34+01:00 add algolia creds + optimize sidebar for crawling by @froos in: [#488](https://codeberg.org/uzu/strudel/pulls/488) +- 2023-02-27T19:28:10+01:00 fix app height by @froos in: [#485](https://codeberg.org/uzu/strudel/pulls/485) +- 2023-02-27T16:04:21+01:00 Revert "Another attempt at composable functions - WIP (#390)" by @froos in: [#484](https://codeberg.org/uzu/strudel/pulls/484) +- 2023-02-27T15:45:20+01:00 Update mini-notation.mdx by @yaxu in: [#365](https://codeberg.org/uzu/strudel/pulls/365) +- 2023-02-27T12:52:09+01:00 docs: packages + offline by @froos in: [#482](https://codeberg.org/uzu/strudel/pulls/482) +- 2023-02-25T14:26:49+01:00 Fix array args by @froos in: [#480](https://codeberg.org/uzu/strudel/pulls/480) +- 2023-02-25T12:33:22+01:00 midi cc support by @froos in: [#478](https://codeberg.org/uzu/strudel/pulls/478) +- 2023-02-23T00:11:05+01:00 fix: hash links by @froos in: [#473](https://codeberg.org/uzu/strudel/pulls/473) +- 2023-02-22T22:54:39+01:00 settings tab with vim / emacs modes + additional themes and fonts by @froos in: [#467](https://codeberg.org/uzu/strudel/pulls/467) +- 2023-02-22T22:53:22+01:00 Update input-output.mdx by @Ghost in: [#471](https://codeberg.org/uzu/strudel/pulls/471) +- 2023-02-22T22:52:50+01:00 FIXES: freqs instead of pitches by @Ghost in: [#464](https://codeberg.org/uzu/strudel/pulls/464) +- 2023-02-22T20:01:00+01:00 fix: osc should not return a promise by @froos in: [#472](https://codeberg.org/uzu/strudel/pulls/472) +- 2023-02-22T12:51:31+01:00 slice and splice by @yaxu in: [#466](https://codeberg.org/uzu/strudel/pulls/466) +- 2023-02-18T01:00:19+01:00 weave and weaveWith by @yaxu in: [#465](https://codeberg.org/uzu/strudel/pulls/465) +- 2023-02-17T00:15:21+01:00 Composable functions by @yaxu in: [#390](https://codeberg.org/uzu/strudel/pulls/390) +- 2023-02-16T20:38:45+01:00 FIXES: Warning about jsxBracketSameLine deprecation by @Ghost in: [#461](https://codeberg.org/uzu/strudel/pulls/461) +- 2023-02-14T22:11:02+01:00 Update synths.mdx by @Ghost in: [#438](https://codeberg.org/uzu/strudel/pulls/438) +- 2023-02-14T19:54:42+01:00 Update mini-notation.mdx by @Ghost in: [#437](https://codeberg.org/uzu/strudel/pulls/437) +- 2023-02-14T19:54:25+01:00 Update code.mdx by @Ghost in: [#436](https://codeberg.org/uzu/strudel/pulls/436) +- 2023-02-13T00:43:29+01:00 Fix anchors by @froos in: [#433](https://codeberg.org/uzu/strudel/pulls/433) +- 2023-02-11T21:02:11+01:00 autocomplete preparations by @froos in: [#427](https://codeberg.org/uzu/strudel/pulls/427) +- 2023-02-10T23:14:48+01:00 Themes by @froos in: [#431](https://codeberg.org/uzu/strudel/pulls/431) +- 2023-02-09T19:22:56+01:00 minirepl: add keyboard shortcuts by @froos in: [#429](https://codeberg.org/uzu/strudel/pulls/429) +- 2023-02-09T08:56:42+01:00 add cdn.freesound to cache list by @froos in: [#425](https://codeberg.org/uzu/strudel/pulls/425) +- 2023-02-08T20:36:00+01:00 add more offline caching by @froos in: [#421](https://codeberg.org/uzu/strudel/pulls/421) +- 2023-02-07T22:08:01+01:00 add caching strategy for missing file types + cache all samples loaded from github by @froos in: [#419](https://codeberg.org/uzu/strudel/pulls/419) +- 2023-02-06T23:29:57+01:00 PWA with offline support by @froos in: [#417](https://codeberg.org/uzu/strudel/pulls/417) +- 2023-02-05T17:27:32+01:00 improve samples doc by @froos in: [#411](https://codeberg.org/uzu/strudel/pulls/411) +- 2023-02-05T17:27:10+01:00 google gtfo by @froos in: [#413](https://codeberg.org/uzu/strudel/pulls/413) +- 2023-02-05T16:27:59+01:00 improve effects doc by @froos in: [#409](https://codeberg.org/uzu/strudel/pulls/409) +- 2023-02-05T14:56:03+01:00 Update effects.mdx by @Ghost in: [#410](https://codeberg.org/uzu/strudel/pulls/410) +- 2023-02-03T19:54:47+01:00 add shabda doc by @froos in: [#407](https://codeberg.org/uzu/strudel/pulls/407) +- 2023-02-02T21:45:56+01:00 fix: share url on subpath by @froos in: [#405](https://codeberg.org/uzu/strudel/pulls/405) +- 2023-02-02T21:35:45+01:00 update csound + fix sound output by @froos in: [#404](https://codeberg.org/uzu/strudel/pulls/404) +- 2023-02-02T19:53:36+01:00 pin @csound/browser to 6.18.3 + bump by @froos in: [#403](https://codeberg.org/uzu/strudel/pulls/403) +- 2023-02-01T22:47:27+01:00 release webaudio by @froos in: [#400](https://codeberg.org/uzu/strudel/pulls/400) +- 2023-02-01T22:45:16+01:00 can now await initAudio + initAudioOnFirstClick by @froos in: [#399](https://codeberg.org/uzu/strudel/pulls/399) +- 2023-02-01T22:32:18+01:00 fix: minirepl styles by @froos in: [#398](https://codeberg.org/uzu/strudel/pulls/398) +- 2023-02-01T22:17:19+01:00 proper builds + use pnpm workspaces by @froos in: [#396](https://codeberg.org/uzu/strudel/pulls/396) +- 2023-02-01T16:49:55+01:00 add pattern methods hurry, press and pressBy by @yaxu in: [#397](https://codeberg.org/uzu/strudel/pulls/397) ## january 2023 -- 2023-01-27T14:40:40+01:00 Add tidal-drum-patterns to examples by Ghost in: [#379](https://codeberg.org/uzu/strudel/pulls/379) -- 2023-01-27T14:39:56+01:00 add ribbon + test + docs by froos in: [#388](https://codeberg.org/uzu/strudel/pulls/388) -- 2023-01-27T12:10:37+01:00 Notes are not essential :) by yaxu in: [#393](https://codeberg.org/uzu/strudel/pulls/393) -- 2023-01-21T17:18:13+01:00 document csound by froos in: [#391](https://codeberg.org/uzu/strudel/pulls/391) -- 2023-01-19T12:04:51+01:00 Rename a to angle by froos in: [#387](https://codeberg.org/uzu/strudel/pulls/387) -- 2023-01-19T11:49:39+01:00 add run + test + docs by froos in: [#386](https://codeberg.org/uzu/strudel/pulls/386) -- 2023-01-19T11:32:56+01:00 docs: use note instead of n to mitigate confusion by froos in: [#385](https://codeberg.org/uzu/strudel/pulls/385) -- 2023-01-18T17:10:09+01:00 update my-patterns instructions by froos in: [#384](https://codeberg.org/uzu/strudel/pulls/384) -- 2023-01-15T23:19:43+01:00 Draw fixes by froos in: [#377](https://codeberg.org/uzu/strudel/pulls/377) -- 2023-01-14T11:07:08+01:00 improve new draw logic by froos in: [#372](https://codeberg.org/uzu/strudel/pulls/372) -- 2023-01-12T14:40:59+01:00 document more functions + change arp join by froos in: [#369](https://codeberg.org/uzu/strudel/pulls/369) -- 2023-01-10T00:03:47+01:00 add https to url by Ghost in: [#364](https://codeberg.org/uzu/strudel/pulls/364) -- 2023-01-09T23:37:34+01:00 doc structuring by froos in: [#360](https://codeberg.org/uzu/strudel/pulls/360) -- 2023-01-09T23:23:28+01:00 Support for multiple mininotation operators by yaxu in: [#350](https://codeberg.org/uzu/strudel/pulls/350) -- 2023-01-09T00:40:15+01:00 Fix .out(), renaming webaudio's out() to webaudio() by yaxu in: [#361](https://codeberg.org/uzu/strudel/pulls/361) -- 2023-01-06T22:02:31+01:00 docs: tidal comparison + add global fx + add missing sampler fx by froos in: [#356](https://codeberg.org/uzu/strudel/pulls/356) -- 2023-01-06T12:31:32+01:00 Fix Bjorklund by yaxu in: [#343](https://codeberg.org/uzu/strudel/pulls/343) -- 2023-01-04T20:29:35+01:00 Fix prebake base path by froos in: [#345](https://codeberg.org/uzu/strudel/pulls/345) -- 2023-01-04T19:55:49+01:00 fixes #346 by froos in: [#347](https://codeberg.org/uzu/strudel/pulls/347) -- 2023-01-02T21:28:07+01:00 Patternify euclid, fast, slow and polymeter step parameters in mininotation by yaxu in: [#341](https://codeberg.org/uzu/strudel/pulls/341) -- 2023-01-02T00:30:16+01:00 more animate functions + mini repl fix by froos in: [#340](https://codeberg.org/uzu/strudel/pulls/340) -- 2023-01-01T12:34:11+01:00 move /my-patterns to /swatch by yaxu in: [#338](https://codeberg.org/uzu/strudel/pulls/338) -- 2023-01-01T12:22:55+01:00 animation options by froos in: [#337](https://codeberg.org/uzu/strudel/pulls/337) +- 2023-01-27T14:40:40+01:00 Add tidal-drum-patterns to examples by @Ghost in: [#379](https://codeberg.org/uzu/strudel/pulls/379) +- 2023-01-27T14:39:56+01:00 add ribbon + test + docs by @froos in: [#388](https://codeberg.org/uzu/strudel/pulls/388) +- 2023-01-27T12:10:37+01:00 Notes are not essential :) by @yaxu in: [#393](https://codeberg.org/uzu/strudel/pulls/393) +- 2023-01-21T17:18:13+01:00 document csound by @froos in: [#391](https://codeberg.org/uzu/strudel/pulls/391) +- 2023-01-19T12:04:51+01:00 Rename a to angle by @froos in: [#387](https://codeberg.org/uzu/strudel/pulls/387) +- 2023-01-19T11:49:39+01:00 add run + test + docs by @froos in: [#386](https://codeberg.org/uzu/strudel/pulls/386) +- 2023-01-19T11:32:56+01:00 docs: use note instead of n to mitigate confusion by @froos in: [#385](https://codeberg.org/uzu/strudel/pulls/385) +- 2023-01-18T17:10:09+01:00 update my-patterns instructions by @froos in: [#384](https://codeberg.org/uzu/strudel/pulls/384) +- 2023-01-15T23:19:43+01:00 Draw fixes by @froos in: [#377](https://codeberg.org/uzu/strudel/pulls/377) +- 2023-01-14T11:07:08+01:00 improve new draw logic by @froos in: [#372](https://codeberg.org/uzu/strudel/pulls/372) +- 2023-01-12T14:40:59+01:00 document more functions + change arp join by @froos in: [#369](https://codeberg.org/uzu/strudel/pulls/369) +- 2023-01-10T00:03:47+01:00 add https to url by @Ghost in: [#364](https://codeberg.org/uzu/strudel/pulls/364) +- 2023-01-09T23:37:34+01:00 doc structuring by @froos in: [#360](https://codeberg.org/uzu/strudel/pulls/360) +- 2023-01-09T23:23:28+01:00 Support for multiple mininotation operators by @yaxu in: [#350](https://codeberg.org/uzu/strudel/pulls/350) +- 2023-01-09T00:40:15+01:00 Fix .out(), renaming webaudio's out() to webaudio() by @yaxu in: [#361](https://codeberg.org/uzu/strudel/pulls/361) +- 2023-01-06T22:02:31+01:00 docs: tidal comparison + add global fx + add missing sampler fx by @froos in: [#356](https://codeberg.org/uzu/strudel/pulls/356) +- 2023-01-06T12:31:32+01:00 Fix Bjorklund by @yaxu in: [#343](https://codeberg.org/uzu/strudel/pulls/343) +- 2023-01-04T20:29:35+01:00 Fix prebake base path by @froos in: [#345](https://codeberg.org/uzu/strudel/pulls/345) +- 2023-01-04T19:55:49+01:00 fixes #346 by @froos in: [#347](https://codeberg.org/uzu/strudel/pulls/347) +- 2023-01-02T21:28:07+01:00 Patternify euclid, fast, slow and polymeter step parameters in mininotation by @yaxu in: [#341](https://codeberg.org/uzu/strudel/pulls/341) +- 2023-01-02T00:30:16+01:00 more animate functions + mini repl fix by @froos in: [#340](https://codeberg.org/uzu/strudel/pulls/340) +- 2023-01-01T12:34:11+01:00 move /my-patterns to /swatch by @yaxu in: [#338](https://codeberg.org/uzu/strudel/pulls/338) +- 2023-01-01T12:22:55+01:00 animation options by @froos in: [#337](https://codeberg.org/uzu/strudel/pulls/337) ## december 2022 -- 2022-12-31T22:42:49+01:00 Tidy parser, implement polymeters by yaxu in: [#336](https://codeberg.org/uzu/strudel/pulls/336) -- 2022-12-31T16:51:53+01:00 animate mvp by froos in: [#335](https://codeberg.org/uzu/strudel/pulls/335) -- 2022-12-30T20:16:10+01:00 testing + docs docs by froos in: [#334](https://codeberg.org/uzu/strudel/pulls/334) -- 2022-12-29T21:11:16+01:00 Embed mode improvements by froos in: [#333](https://codeberg.org/uzu/strudel/pulls/333) -- 2022-12-29T14:06:20+01:00 fix: can now multiply floats in mini notation by froos in: [#332](https://codeberg.org/uzu/strudel/pulls/332) -- 2022-12-29T13:50:07+01:00 improve displaying 's' in pianoroll by froos in: [#331](https://codeberg.org/uzu/strudel/pulls/331) -- 2022-12-28T17:35:35+01:00 my-patterns: fix paths + update readme by froos in: [#330](https://codeberg.org/uzu/strudel/pulls/330) -- 2022-12-28T17:11:40+01:00 my-patterns build + deploy by froos in: [#329](https://codeberg.org/uzu/strudel/pulls/329) -- 2022-12-28T15:43:31+01:00 add my-patterns by froos in: [#328](https://codeberg.org/uzu/strudel/pulls/328) -- 2022-12-28T14:47:14+01:00 add examples route by froos in: [#327](https://codeberg.org/uzu/strudel/pulls/327) -- 2022-12-28T14:44:31+01:00 fix: workaround Object.assign globalThis by froos in: [#326](https://codeberg.org/uzu/strudel/pulls/326) -- 2022-12-26T23:29:47+01:00 mini repl improvements by froos in: [#324](https://codeberg.org/uzu/strudel/pulls/324) -- 2022-12-26T23:00:34+01:00 support notes without octave by froos in: [#323](https://codeberg.org/uzu/strudel/pulls/323) -- 2022-12-26T12:37:36+01:00 tutorial updates by Ghost in: [#320](https://codeberg.org/uzu/strudel/pulls/320) -- 2022-12-23T18:26:30+01:00 Reference tab sort by froos in: [#318](https://codeberg.org/uzu/strudel/pulls/318) -- 2022-12-23T00:49:56+01:00 Astro build by froos in: [#315](https://codeberg.org/uzu/strudel/pulls/315) -- 2022-12-19T21:02:37+01:00 object support for .scale by froos in: [#307](https://codeberg.org/uzu/strudel/pulls/307) -- 2022-12-19T21:02:22+01:00 Jsdoc component by froos in: [#312](https://codeberg.org/uzu/strudel/pulls/312) -- 2022-12-19T20:31:46+01:00 fix: copy share link to clipboard was broken for some browers by froos in: [#311](https://codeberg.org/uzu/strudel/pulls/311) -- 2022-12-19T12:11:24+01:00 ICLC2023 paper WIP by yaxu in: [#306](https://codeberg.org/uzu/strudel/pulls/306) -- 2022-12-15T21:23:22+01:00 support freq in pianoroll by froos in: [#308](https://codeberg.org/uzu/strudel/pulls/308) -- 2022-12-13T22:07:27+01:00 Updated csoundm to use the register facility . by gogins in: [#303](https://codeberg.org/uzu/strudel/pulls/303) -- 2022-12-13T21:57:03+01:00 add lint + prettier check before test by froos in: [#305](https://codeberg.org/uzu/strudel/pulls/305) -- 2022-12-13T21:21:44+01:00 add freq support to sampler by froos in: [#301](https://codeberg.org/uzu/strudel/pulls/301) -- 2022-12-12T00:48:41+01:00 fix whitespace trimming by froos in: [#300](https://codeberg.org/uzu/strudel/pulls/300) -- 2022-12-12T00:21:53+01:00 .defragmentHaps() for merging touching haps that share a whole and value by yaxu in: [#299](https://codeberg.org/uzu/strudel/pulls/299) -- 2022-12-11T23:17:49+01:00 remove whitespace from highlighted region by froos in: [#298](https://codeberg.org/uzu/strudel/pulls/298) -- 2022-12-11T22:03:26+01:00 update vitest by froos in: [#297](https://codeberg.org/uzu/strudel/pulls/297) -- 2022-12-11T21:44:16+01:00 can now add bare numbers to numeral object props by froos in: [#287](https://codeberg.org/uzu/strudel/pulls/287) -- 2022-12-11T21:27:58+01:00 Move stuff to new register function by froos in: [#295](https://codeberg.org/uzu/strudel/pulls/295) -- 2022-12-11T20:07:12+01:00 add prettier task by froos in: [#296](https://codeberg.org/uzu/strudel/pulls/296) -- 2022-12-10T15:39:04+01:00 Reorganise pattern.mjs with a 'toplevel first' regime by yaxu in: [#286](https://codeberg.org/uzu/strudel/pulls/286) -- 2022-12-10T11:56:16+01:00 Fancy hap show, include part in snapshots by yaxu in: [#291](https://codeberg.org/uzu/strudel/pulls/291) -- 2022-12-07T20:07:55+01:00 Switch 'operators' from .whatHow to .what.how by yaxu in: [#285](https://codeberg.org/uzu/strudel/pulls/285) -- 2022-12-04T12:51:01+01:00 implement collect + arp function by froos in: [#281](https://codeberg.org/uzu/strudel/pulls/281) -- 2022-12-02T13:38:27+01:00 do not recompile orc by froos in: [#278](https://codeberg.org/uzu/strudel/pulls/278) -- 2022-12-02T12:49:26+01:00 add basic csound output by froos in: [#275](https://codeberg.org/uzu/strudel/pulls/275) -- 2022-12-02T12:18:41+01:00 add licenses / credits to all tunes + remove some by froos in: [#277](https://codeberg.org/uzu/strudel/pulls/277) -- 2022-12-02T11:45:02+01:00 Support sending CRC16 bytes with serial messages by yaxu in: [#276](https://codeberg.org/uzu/strudel/pulls/276) +- 2022-12-31T22:42:49+01:00 Tidy parser, implement polymeters by @yaxu in: [#336](https://codeberg.org/uzu/strudel/pulls/336) +- 2022-12-31T16:51:53+01:00 animate mvp by @froos in: [#335](https://codeberg.org/uzu/strudel/pulls/335) +- 2022-12-30T20:16:10+01:00 testing + docs docs by @froos in: [#334](https://codeberg.org/uzu/strudel/pulls/334) +- 2022-12-29T21:11:16+01:00 Embed mode improvements by @froos in: [#333](https://codeberg.org/uzu/strudel/pulls/333) +- 2022-12-29T14:06:20+01:00 fix: can now multiply floats in mini notation by @froos in: [#332](https://codeberg.org/uzu/strudel/pulls/332) +- 2022-12-29T13:50:07+01:00 improve displaying 's' in pianoroll by @froos in: [#331](https://codeberg.org/uzu/strudel/pulls/331) +- 2022-12-28T17:35:35+01:00 my-patterns: fix paths + update readme by @froos in: [#330](https://codeberg.org/uzu/strudel/pulls/330) +- 2022-12-28T17:11:40+01:00 my-patterns build + deploy by @froos in: [#329](https://codeberg.org/uzu/strudel/pulls/329) +- 2022-12-28T15:43:31+01:00 add my-patterns by @froos in: [#328](https://codeberg.org/uzu/strudel/pulls/328) +- 2022-12-28T14:47:14+01:00 add examples route by @froos in: [#327](https://codeberg.org/uzu/strudel/pulls/327) +- 2022-12-28T14:44:31+01:00 fix: workaround Object.assign globalThis by @froos in: [#326](https://codeberg.org/uzu/strudel/pulls/326) +- 2022-12-26T23:29:47+01:00 mini repl improvements by @froos in: [#324](https://codeberg.org/uzu/strudel/pulls/324) +- 2022-12-26T23:00:34+01:00 support notes without octave by @froos in: [#323](https://codeberg.org/uzu/strudel/pulls/323) +- 2022-12-26T12:37:36+01:00 tutorial updates by @Ghost in: [#320](https://codeberg.org/uzu/strudel/pulls/320) +- 2022-12-23T18:26:30+01:00 Reference tab sort by @froos in: [#318](https://codeberg.org/uzu/strudel/pulls/318) +- 2022-12-23T00:49:56+01:00 Astro build by @froos in: [#315](https://codeberg.org/uzu/strudel/pulls/315) +- 2022-12-19T21:02:37+01:00 object support for .scale by @froos in: [#307](https://codeberg.org/uzu/strudel/pulls/307) +- 2022-12-19T21:02:22+01:00 Jsdoc component by @froos in: [#312](https://codeberg.org/uzu/strudel/pulls/312) +- 2022-12-19T20:31:46+01:00 fix: copy share link to clipboard was broken for some browers by @froos in: [#311](https://codeberg.org/uzu/strudel/pulls/311) +- 2022-12-19T12:11:24+01:00 ICLC2023 paper WIP by @yaxu in: [#306](https://codeberg.org/uzu/strudel/pulls/306) +- 2022-12-15T21:23:22+01:00 support freq in pianoroll by @froos in: [#308](https://codeberg.org/uzu/strudel/pulls/308) +- 2022-12-13T22:07:27+01:00 Updated csoundm to use the register facility . by @gogins in: [#303](https://codeberg.org/uzu/strudel/pulls/303) +- 2022-12-13T21:57:03+01:00 add lint + prettier check before test by @froos in: [#305](https://codeberg.org/uzu/strudel/pulls/305) +- 2022-12-13T21:21:44+01:00 add freq support to sampler by @froos in: [#301](https://codeberg.org/uzu/strudel/pulls/301) +- 2022-12-12T00:48:41+01:00 fix whitespace trimming by @froos in: [#300](https://codeberg.org/uzu/strudel/pulls/300) +- 2022-12-12T00:21:53+01:00 .defragmentHaps() for merging touching haps that share a whole and value by @yaxu in: [#299](https://codeberg.org/uzu/strudel/pulls/299) +- 2022-12-11T23:17:49+01:00 remove whitespace from highlighted region by @froos in: [#298](https://codeberg.org/uzu/strudel/pulls/298) +- 2022-12-11T22:03:26+01:00 update vitest by @froos in: [#297](https://codeberg.org/uzu/strudel/pulls/297) +- 2022-12-11T21:44:16+01:00 can now add bare numbers to numeral object props by @froos in: [#287](https://codeberg.org/uzu/strudel/pulls/287) +- 2022-12-11T21:27:58+01:00 Move stuff to new register function by @froos in: [#295](https://codeberg.org/uzu/strudel/pulls/295) +- 2022-12-11T20:07:12+01:00 add prettier task by @froos in: [#296](https://codeberg.org/uzu/strudel/pulls/296) +- 2022-12-10T15:39:04+01:00 Reorganise pattern.mjs with a 'toplevel first' regime by @yaxu in: [#286](https://codeberg.org/uzu/strudel/pulls/286) +- 2022-12-10T11:56:16+01:00 Fancy hap show, include part in snapshots by @yaxu in: [#291](https://codeberg.org/uzu/strudel/pulls/291) +- 2022-12-07T20:07:55+01:00 Switch 'operators' from .whatHow to .what.how by @yaxu in: [#285](https://codeberg.org/uzu/strudel/pulls/285) +- 2022-12-04T12:51:01+01:00 implement collect + arp function by @froos in: [#281](https://codeberg.org/uzu/strudel/pulls/281) +- 2022-12-02T13:38:27+01:00 do not recompile orc by @froos in: [#278](https://codeberg.org/uzu/strudel/pulls/278) +- 2022-12-02T12:49:26+01:00 add basic csound output by @froos in: [#275](https://codeberg.org/uzu/strudel/pulls/275) +- 2022-12-02T12:18:41+01:00 add licenses / credits to all tunes + remove some by @froos in: [#277](https://codeberg.org/uzu/strudel/pulls/277) +- 2022-12-02T11:45:02+01:00 Support sending CRC16 bytes with serial messages by @yaxu in: [#276](https://codeberg.org/uzu/strudel/pulls/276) ## november 2022 -- 2022-11-29T23:41:39+01:00 release version bumps by froos in: [#273](https://codeberg.org/uzu/strudel/pulls/273) -- 2022-11-29T23:34:50+01:00 add eslint by froos in: [#271](https://codeberg.org/uzu/strudel/pulls/271) -- 2022-11-29T23:34:26+01:00 tonal update with fixed memory leak by froos in: [#272](https://codeberg.org/uzu/strudel/pulls/272) -- 2022-11-22T09:51:26+01:00 Tidying up core by yaxu in: [#256](https://codeberg.org/uzu/strudel/pulls/256) -- 2022-11-21T22:15:48+01:00 fix performance bottleneck by froos in: [#266](https://codeberg.org/uzu/strudel/pulls/266) -- 2022-11-17T11:08:38+01:00 fix tutorial bugs by froos in: [#263](https://codeberg.org/uzu/strudel/pulls/263) -- 2022-11-16T13:15:28+01:00 Binaries by froos in: [#254](https://codeberg.org/uzu/strudel/pulls/254) -- 2022-11-13T20:20:32+01:00 Repl refactoring by froos in: [#255](https://codeberg.org/uzu/strudel/pulls/255) -- 2022-11-08T23:35:11+01:00 Webaudio build by froos in: [#250](https://codeberg.org/uzu/strudel/pulls/250) -- 2022-11-08T22:43:32+01:00 new transpiler based on acorn by froos in: [#249](https://codeberg.org/uzu/strudel/pulls/249) -- 2022-11-06T19:03:19+01:00 General purpose scheduler by froos in: [#248](https://codeberg.org/uzu/strudel/pulls/248) -- 2022-11-06T11:05:23+01:00 snapshot tests on shared snippets by froos in: [#243](https://codeberg.org/uzu/strudel/pulls/243) -- 2022-11-06T11:03:42+01:00 Some tunes by froos in: [#247](https://codeberg.org/uzu/strudel/pulls/247) -- 2022-11-06T00:37:11+01:00 patchday by froos in: [#246](https://codeberg.org/uzu/strudel/pulls/246) -- 2022-11-04T20:28:23+01:00 Readme + TLC by froos in: [#244](https://codeberg.org/uzu/strudel/pulls/244) -- 2022-11-03T15:16:04+01:00 in source example tests by froos in: [#242](https://codeberg.org/uzu/strudel/pulls/242) -- 2022-11-02T23:16:43+01:00 feat: support github: links by froos in: [#240](https://codeberg.org/uzu/strudel/pulls/240) -- 2022-11-02T21:26:44+01:00 Load samples from url by froos in: [#239](https://codeberg.org/uzu/strudel/pulls/239) -- 2022-11-02T11:42:32+01:00 Object arithmetic by froos in: [#238](https://codeberg.org/uzu/strudel/pulls/238) -- 2022-11-01T00:36:14+01:00 Tidal drum machines by froos in: [#237](https://codeberg.org/uzu/strudel/pulls/237) -- 2022-10-31T21:39:23+01:00 fx on stereo speakers by froos in: [#236](https://codeberg.org/uzu/strudel/pulls/236) +- 2022-11-29T23:41:39+01:00 release version bumps by @froos in: [#273](https://codeberg.org/uzu/strudel/pulls/273) +- 2022-11-29T23:34:50+01:00 add eslint by @froos in: [#271](https://codeberg.org/uzu/strudel/pulls/271) +- 2022-11-29T23:34:26+01:00 tonal update with fixed memory leak by @froos in: [#272](https://codeberg.org/uzu/strudel/pulls/272) +- 2022-11-22T09:51:26+01:00 Tidying up core by @yaxu in: [#256](https://codeberg.org/uzu/strudel/pulls/256) +- 2022-11-21T22:15:48+01:00 fix performance bottleneck by @froos in: [#266](https://codeberg.org/uzu/strudel/pulls/266) +- 2022-11-17T11:08:38+01:00 fix tutorial bugs by @froos in: [#263](https://codeberg.org/uzu/strudel/pulls/263) +- 2022-11-16T13:15:28+01:00 Binaries by @froos in: [#254](https://codeberg.org/uzu/strudel/pulls/254) +- 2022-11-13T20:20:32+01:00 Repl refactoring by @froos in: [#255](https://codeberg.org/uzu/strudel/pulls/255) +- 2022-11-08T23:35:11+01:00 Webaudio build by @froos in: [#250](https://codeberg.org/uzu/strudel/pulls/250) +- 2022-11-08T22:43:32+01:00 new transpiler based on acorn by @froos in: [#249](https://codeberg.org/uzu/strudel/pulls/249) +- 2022-11-06T19:03:19+01:00 General purpose scheduler by @froos in: [#248](https://codeberg.org/uzu/strudel/pulls/248) +- 2022-11-06T11:05:23+01:00 snapshot tests on shared snippets by @froos in: [#243](https://codeberg.org/uzu/strudel/pulls/243) +- 2022-11-06T11:03:42+01:00 Some tunes by @froos in: [#247](https://codeberg.org/uzu/strudel/pulls/247) +- 2022-11-06T00:37:11+01:00 patchday by @froos in: [#246](https://codeberg.org/uzu/strudel/pulls/246) +- 2022-11-04T20:28:23+01:00 Readme + TLC by @froos in: [#244](https://codeberg.org/uzu/strudel/pulls/244) +- 2022-11-03T15:16:04+01:00 in source example tests by @froos in: [#242](https://codeberg.org/uzu/strudel/pulls/242) +- 2022-11-02T23:16:43+01:00 feat: support github: links by @froos in: [#240](https://codeberg.org/uzu/strudel/pulls/240) +- 2022-11-02T21:26:44+01:00 Load samples from url by @froos in: [#239](https://codeberg.org/uzu/strudel/pulls/239) +- 2022-11-02T11:42:32+01:00 Object arithmetic by @froos in: [#238](https://codeberg.org/uzu/strudel/pulls/238) +- 2022-11-01T00:36:14+01:00 Tidal drum machines by @froos in: [#237](https://codeberg.org/uzu/strudel/pulls/237) +- 2022-10-31T21:39:23+01:00 fx on stereo speakers by @froos in: [#236](https://codeberg.org/uzu/strudel/pulls/236) ## october 2022 -- 2022-10-30T19:10:33+01:00 add vcsl sample library by froos in: [#235](https://codeberg.org/uzu/strudel/pulls/235) -- 2022-10-30T00:23:10+02:00 Fix zero length queries WIP by yaxu in: [#234](https://codeberg.org/uzu/strudel/pulls/234) -- 2022-10-29T17:54:05+02:00 Out by default by froos in: [#232](https://codeberg.org/uzu/strudel/pulls/232) -- 2022-10-26T23:53:49+02:00 Patternify range by yaxu in: [#231](https://codeberg.org/uzu/strudel/pulls/231) -- 2022-10-26T21:42:12+02:00 Just another docs branch by froos in: [#228](https://codeberg.org/uzu/strudel/pulls/228) -- 2022-10-26T21:41:12+02:00 Refactor tunes away from tone by froos in: [#230](https://codeberg.org/uzu/strudel/pulls/230) -- 2022-10-20T09:26:28+02:00 Core util tests by Ghost in: [#226](https://codeberg.org/uzu/strudel/pulls/226) -- 2022-10-06T22:35:45+02:00 fix fastgap for events that go across cycle boundaries by yaxu in: [#225](https://codeberg.org/uzu/strudel/pulls/225) +- 2022-10-30T19:10:33+01:00 add vcsl sample library by @froos in: [#235](https://codeberg.org/uzu/strudel/pulls/235) +- 2022-10-30T00:23:10+02:00 Fix zero length queries WIP by @yaxu in: [#234](https://codeberg.org/uzu/strudel/pulls/234) +- 2022-10-29T17:54:05+02:00 Out by default by @froos in: [#232](https://codeberg.org/uzu/strudel/pulls/232) +- 2022-10-26T23:53:49+02:00 Patternify range by @yaxu in: [#231](https://codeberg.org/uzu/strudel/pulls/231) +- 2022-10-26T21:42:12+02:00 Just another docs branch by @froos in: [#228](https://codeberg.org/uzu/strudel/pulls/228) +- 2022-10-26T21:41:12+02:00 Refactor tunes away from tone by @froos in: [#230](https://codeberg.org/uzu/strudel/pulls/230) +- 2022-10-20T09:26:28+02:00 Core util tests by @Ghost in: [#226](https://codeberg.org/uzu/strudel/pulls/226) +- 2022-10-06T22:35:45+02:00 fix fastgap for events that go across cycle boundaries by @yaxu in: [#225](https://codeberg.org/uzu/strudel/pulls/225) ## september 2022 -- 2022-09-25T21:15:36+02:00 Reverb by froos in: [#224](https://codeberg.org/uzu/strudel/pulls/224) -- 2022-09-25T00:27:26+02:00 focus tweak for squeezeJoin - another go at fixing #216 by yaxu in: [#221](https://codeberg.org/uzu/strudel/pulls/221) -- 2022-09-24T23:17:21+02:00 support negative speeds by froos in: [#222](https://codeberg.org/uzu/strudel/pulls/222) -- 2022-09-24T21:55:15+02:00 Feedback Delay by froos in: [#213](https://codeberg.org/uzu/strudel/pulls/213) -- 2022-09-23T12:06:17+02:00 Fix squeeze join by yaxu in: [#220](https://codeberg.org/uzu/strudel/pulls/220) -- 2022-09-22T21:23:11+02:00 encapsulate webaudio output by froos in: [#219](https://codeberg.org/uzu/strudel/pulls/219) -- 2022-09-22T19:18:18+02:00 samples now have envelopes by froos in: [#218](https://codeberg.org/uzu/strudel/pulls/218) -- 2022-09-22T00:03:30+02:00 sampler features + fixes by froos in: [#217](https://codeberg.org/uzu/strudel/pulls/217) -- 2022-09-19T23:32:55+02:00 Just another docs PR by froos in: [#215](https://codeberg.org/uzu/strudel/pulls/215) -- 2022-09-17T23:47:43+02:00 Even more docs by froos in: [#212](https://codeberg.org/uzu/strudel/pulls/212) -- 2022-09-17T15:36:53+02:00 Webaudio guide by froos in: [#207](https://codeberg.org/uzu/strudel/pulls/207) -- 2022-09-16T00:21:29+02:00 Coarse crush shape by froos in: [#205](https://codeberg.org/uzu/strudel/pulls/205) -- 2022-09-15T21:04:28+02:00 add vowel to .out by froos in: [#201](https://codeberg.org/uzu/strudel/pulls/201) -- 2022-09-15T16:55:52+02:00 add rollup-plugin-visualizer to build by froos in: [#200](https://codeberg.org/uzu/strudel/pulls/200) -- 2022-09-14T23:46:39+02:00 document random functions by froos in: [#199](https://codeberg.org/uzu/strudel/pulls/199) -- 2022-09-09T22:04:40+02:00 Fix numbers in sampler by froos in: [#196](https://codeberg.org/uzu/strudel/pulls/196) +- 2022-09-25T21:15:36+02:00 Reverb by @froos in: [#224](https://codeberg.org/uzu/strudel/pulls/224) +- 2022-09-25T00:27:26+02:00 focus tweak for squeezeJoin - another go at fixing #216 by @yaxu in: [#221](https://codeberg.org/uzu/strudel/pulls/221) +- 2022-09-24T23:17:21+02:00 support negative speeds by @froos in: [#222](https://codeberg.org/uzu/strudel/pulls/222) +- 2022-09-24T21:55:15+02:00 Feedback Delay by @froos in: [#213](https://codeberg.org/uzu/strudel/pulls/213) +- 2022-09-23T12:06:17+02:00 Fix squeeze join by @yaxu in: [#220](https://codeberg.org/uzu/strudel/pulls/220) +- 2022-09-22T21:23:11+02:00 encapsulate webaudio output by @froos in: [#219](https://codeberg.org/uzu/strudel/pulls/219) +- 2022-09-22T19:18:18+02:00 samples now have envelopes by @froos in: [#218](https://codeberg.org/uzu/strudel/pulls/218) +- 2022-09-22T00:03:30+02:00 sampler features + fixes by @froos in: [#217](https://codeberg.org/uzu/strudel/pulls/217) +- 2022-09-19T23:32:55+02:00 Just another docs PR by @froos in: [#215](https://codeberg.org/uzu/strudel/pulls/215) +- 2022-09-17T23:47:43+02:00 Even more docs by @froos in: [#212](https://codeberg.org/uzu/strudel/pulls/212) +- 2022-09-17T15:36:53+02:00 Webaudio guide by @froos in: [#207](https://codeberg.org/uzu/strudel/pulls/207) +- 2022-09-16T00:21:29+02:00 Coarse crush shape by @froos in: [#205](https://codeberg.org/uzu/strudel/pulls/205) +- 2022-09-15T21:04:28+02:00 add vowel to .out by @froos in: [#201](https://codeberg.org/uzu/strudel/pulls/201) +- 2022-09-15T16:55:52+02:00 add rollup-plugin-visualizer to build by @froos in: [#200](https://codeberg.org/uzu/strudel/pulls/200) +- 2022-09-14T23:46:39+02:00 document random functions by @froos in: [#199](https://codeberg.org/uzu/strudel/pulls/199) +- 2022-09-09T22:04:40+02:00 Fix numbers in sampler by @froos in: [#196](https://codeberg.org/uzu/strudel/pulls/196) ## august 2022 -- 2022-08-14T17:40:41+02:00 change "stride"/"offset" of successive degradeBy/chooseIn by Ghost in: [#185](https://codeberg.org/uzu/strudel/pulls/185) -- 2022-08-14T16:04:14+02:00 Soundfont file support by froos in: [#183](https://codeberg.org/uzu/strudel/pulls/183) -- 2022-08-14T15:55:50+02:00 fix regression: old way of setting frequencies was broken by froos in: [#190](https://codeberg.org/uzu/strudel/pulls/190) -- 2022-08-14T15:45:53+02:00 wait for prebake to finish before evaluating by froos in: [#189](https://codeberg.org/uzu/strudel/pulls/189) -- 2022-08-14T11:31:00+02:00 Fix codemirror bug by froos in: [#186](https://codeberg.org/uzu/strudel/pulls/186) -- 2022-08-13T16:29:53+02:00 scheduler improvements by froos in: [#181](https://codeberg.org/uzu/strudel/pulls/181) -- 2022-08-12T23:10:36+02:00 replace mocha with vitest by froos in: [#175](https://codeberg.org/uzu/strudel/pulls/175) -- 2022-08-07T00:59:07+02:00 incorporate elements of randomness to the mini notation by Ghost in: [#165](https://codeberg.org/uzu/strudel/pulls/165) -- 2022-08-06T23:32:16+02:00 fix some annoying bugs by froos in: [#177](https://codeberg.org/uzu/strudel/pulls/177) -- 2022-08-06T00:27:17+02:00 Replace react-codemirror6 with @uiw/react-codemirror by froos in: [#173](https://codeberg.org/uzu/strudel/pulls/173) -- 2022-08-04T22:19:46+02:00 add more shapeshifter flags by froos in: [#99](https://codeberg.org/uzu/strudel/pulls/99) -- 2022-08-04T22:06:38+02:00 Amend shapeshifter to allow use of dynamic import by Ghost in: [#171](https://codeberg.org/uzu/strudel/pulls/171) -- 2022-08-02T23:43:07+02:00 Talk fixes by froos in: [#164](https://codeberg.org/uzu/strudel/pulls/164) -- 2022-08-02T23:04:34+02:00 Pianoroll fixes by froos in: [#163](https://codeberg.org/uzu/strudel/pulls/163) -- 2022-08-02T22:57:43+02:00 fix: jsdoc comments by froos in: [#169](https://codeberg.org/uzu/strudel/pulls/169) +- 2022-08-14T17:40:41+02:00 change "stride"/"offset" of successive degradeBy/chooseIn by @Ghost in: [#185](https://codeberg.org/uzu/strudel/pulls/185) +- 2022-08-14T16:04:14+02:00 Soundfont file support by @froos in: [#183](https://codeberg.org/uzu/strudel/pulls/183) +- 2022-08-14T15:55:50+02:00 fix regression: old way of setting frequencies was broken by @froos in: [#190](https://codeberg.org/uzu/strudel/pulls/190) +- 2022-08-14T15:45:53+02:00 wait for prebake to finish before evaluating by @froos in: [#189](https://codeberg.org/uzu/strudel/pulls/189) +- 2022-08-14T11:31:00+02:00 Fix codemirror bug by @froos in: [#186](https://codeberg.org/uzu/strudel/pulls/186) +- 2022-08-13T16:29:53+02:00 scheduler improvements by @froos in: [#181](https://codeberg.org/uzu/strudel/pulls/181) +- 2022-08-12T23:10:36+02:00 replace mocha with vitest by @froos in: [#175](https://codeberg.org/uzu/strudel/pulls/175) +- 2022-08-07T00:59:07+02:00 incorporate elements of randomness to the mini notation by @Ghost in: [#165](https://codeberg.org/uzu/strudel/pulls/165) +- 2022-08-06T23:32:16+02:00 fix some annoying bugs by @froos in: [#177](https://codeberg.org/uzu/strudel/pulls/177) +- 2022-08-06T00:27:17+02:00 Replace react-codemirror6 with @uiw/react-codemirror by @froos in: [#173](https://codeberg.org/uzu/strudel/pulls/173) +- 2022-08-04T22:19:46+02:00 add more shapeshifter flags by @froos in: [#99](https://codeberg.org/uzu/strudel/pulls/99) +- 2022-08-04T22:06:38+02:00 Amend shapeshifter to allow use of dynamic import by @Ghost in: [#171](https://codeberg.org/uzu/strudel/pulls/171) +- 2022-08-02T23:43:07+02:00 Talk fixes by @froos in: [#164](https://codeberg.org/uzu/strudel/pulls/164) +- 2022-08-02T23:04:34+02:00 Pianoroll fixes by @froos in: [#163](https://codeberg.org/uzu/strudel/pulls/163) +- 2022-08-02T22:57:43+02:00 fix: jsdoc comments by @froos in: [#169](https://codeberg.org/uzu/strudel/pulls/169) ## july 2022 -- 2022-07-30T23:51:11+02:00 add chooseInWith/chooseCycles by yaxu in: [#166](https://codeberg.org/uzu/strudel/pulls/166) -- 2022-07-28T19:26:42+02:00 update to tutorial documentation by Ghost in: [#162](https://codeberg.org/uzu/strudel/pulls/162) -- 2022-07-12T18:58:47+02:00 add webdirt drum samples to prebake for general availability by Ghost in: [#150](https://codeberg.org/uzu/strudel/pulls/150) -- 2022-07-12T08:34:17+02:00 Final update to demo.pdf by yaxu in: [#151](https://codeberg.org/uzu/strudel/pulls/151) +- 2022-07-30T23:51:11+02:00 add chooseInWith/chooseCycles by @yaxu in: [#166](https://codeberg.org/uzu/strudel/pulls/166) +- 2022-07-28T19:26:42+02:00 update to tutorial documentation by @Ghost in: [#162](https://codeberg.org/uzu/strudel/pulls/162) +- 2022-07-12T18:58:47+02:00 add webdirt drum samples to prebake for general availability by @Ghost in: [#150](https://codeberg.org/uzu/strudel/pulls/150) +- 2022-07-12T08:34:17+02:00 Final update to demo.pdf by @yaxu in: [#151](https://codeberg.org/uzu/strudel/pulls/151) ## june 2022 -- 2022-06-28T21:54:35+02:00 Sampler optimizations and more by froos in: [#148](https://codeberg.org/uzu/strudel/pulls/148) -- 2022-06-26T12:58:53+02:00 can now generate short link for sharing by froos in: [#146](https://codeberg.org/uzu/strudel/pulls/146) -- 2022-06-24T22:16:04+02:00 flash effect on ctrl enter by froos in: [#144](https://codeberg.org/uzu/strudel/pulls/144) -- 2022-06-22T20:18:17+02:00 Pianoroll Object Support by froos in: [#142](https://codeberg.org/uzu/strudel/pulls/142) -- 2022-06-21T22:57:28+02:00 Serial twiddles by yaxu in: [#141](https://codeberg.org/uzu/strudel/pulls/141) -- 2022-06-21T22:20:05+02:00 Soundfont Support by froos in: [#139](https://codeberg.org/uzu/strudel/pulls/139) -- 2022-06-21T14:11:50+02:00 Fix createParam() by yaxu in: [#140](https://codeberg.org/uzu/strudel/pulls/140) -- 2022-06-18T23:24:42+02:00 Webaudio rewrite by froos in: [#138](https://codeberg.org/uzu/strudel/pulls/138) -- 2022-06-16T21:58:38+02:00 add onTrigger helper by froos in: [#136](https://codeberg.org/uzu/strudel/pulls/136) -- 2022-06-16T20:48:23+02:00 Scheduler improvements by froos in: [#134](https://codeberg.org/uzu/strudel/pulls/134) -- 2022-06-16T20:48:07+02:00 remove cycle + delta from onTrigger by froos in: [#135](https://codeberg.org/uzu/strudel/pulls/135) -- 2022-06-13T21:28:27+02:00 add createParam + createParams by froos in: [#110](https://codeberg.org/uzu/strudel/pulls/110) -- 2022-06-13T21:27:09+02:00 Pianoroll enhancements by froos in: [#131](https://codeberg.org/uzu/strudel/pulls/131) -- 2022-06-05T13:06:03+02:00 Fix link to contributing to tutorial docs by Ghost in: [#129](https://codeberg.org/uzu/strudel/pulls/129) -- 2022-06-02T00:20:45+02:00 Webdirt by froos in: [#121](https://codeberg.org/uzu/strudel/pulls/121) -- 2022-06-01T23:56:04+02:00 fix: #122 ctrl enter would add newline by froos in: [#124](https://codeberg.org/uzu/strudel/pulls/124) -- 2022-06-01T18:21:56+02:00 fix: #108 by froos in: [#123](https://codeberg.org/uzu/strudel/pulls/123) +- 2022-06-28T21:54:35+02:00 Sampler optimizations and more by @froos in: [#148](https://codeberg.org/uzu/strudel/pulls/148) +- 2022-06-26T12:58:53+02:00 can now generate short link for sharing by @froos in: [#146](https://codeberg.org/uzu/strudel/pulls/146) +- 2022-06-24T22:16:04+02:00 flash effect on ctrl enter by @froos in: [#144](https://codeberg.org/uzu/strudel/pulls/144) +- 2022-06-22T20:18:17+02:00 Pianoroll Object Support by @froos in: [#142](https://codeberg.org/uzu/strudel/pulls/142) +- 2022-06-21T22:57:28+02:00 Serial twiddles by @yaxu in: [#141](https://codeberg.org/uzu/strudel/pulls/141) +- 2022-06-21T22:20:05+02:00 Soundfont Support by @froos in: [#139](https://codeberg.org/uzu/strudel/pulls/139) +- 2022-06-21T14:11:50+02:00 Fix createParam() by @yaxu in: [#140](https://codeberg.org/uzu/strudel/pulls/140) +- 2022-06-18T23:24:42+02:00 Webaudio rewrite by @froos in: [#138](https://codeberg.org/uzu/strudel/pulls/138) +- 2022-06-16T21:58:38+02:00 add onTrigger helper by @froos in: [#136](https://codeberg.org/uzu/strudel/pulls/136) +- 2022-06-16T20:48:23+02:00 Scheduler improvements by @froos in: [#134](https://codeberg.org/uzu/strudel/pulls/134) +- 2022-06-16T20:48:07+02:00 remove cycle + delta from onTrigger by @froos in: [#135](https://codeberg.org/uzu/strudel/pulls/135) +- 2022-06-13T21:28:27+02:00 add createParam + createParams by @froos in: [#110](https://codeberg.org/uzu/strudel/pulls/110) +- 2022-06-13T21:27:09+02:00 Pianoroll enhancements by @froos in: [#131](https://codeberg.org/uzu/strudel/pulls/131) +- 2022-06-05T13:06:03+02:00 Fix link to contributing to tutorial docs by @Ghost in: [#129](https://codeberg.org/uzu/strudel/pulls/129) +- 2022-06-02T00:20:45+02:00 Webdirt by @froos in: [#121](https://codeberg.org/uzu/strudel/pulls/121) +- 2022-06-01T23:56:04+02:00 fix: #122 ctrl enter would add newline by @froos in: [#124](https://codeberg.org/uzu/strudel/pulls/124) +- 2022-06-01T18:21:56+02:00 fix: #108 by @froos in: [#123](https://codeberg.org/uzu/strudel/pulls/123) ## may 2022 -- 2022-05-29T09:54:59+02:00 In source doc by froos in: [#117](https://codeberg.org/uzu/strudel/pulls/117) -- 2022-05-20T11:49:10+02:00 react package + vite build by froos in: [#116](https://codeberg.org/uzu/strudel/pulls/116) -- 2022-05-15T22:29:26+02:00 Osc timing improvements by yaxu in: [#113](https://codeberg.org/uzu/strudel/pulls/113) -- 2022-05-15T22:28:24+02:00 loopAt by yaxu in: [#114](https://codeberg.org/uzu/strudel/pulls/114) -- 2022-05-09T20:09:47+02:00 `.brak()`, `.inside()` and `.outside()` by yaxu in: [#112](https://codeberg.org/uzu/strudel/pulls/112) -- 2022-05-06T15:18:41+02:00 In source doc by yaxu in: [#105](https://codeberg.org/uzu/strudel/pulls/105) -- 2022-05-04T22:59:31+02:00 Embed style by froos in: [#109](https://codeberg.org/uzu/strudel/pulls/109) -- 2022-05-03T01:40:22+02:00 Reset, Restart and other composers by froos in: [#88](https://codeberg.org/uzu/strudel/pulls/88) -- 2022-05-02T22:47:21+02:00 /embed package: web component for repl by froos in: [#106](https://codeberg.org/uzu/strudel/pulls/106) -- 2022-05-02T22:05:34+02:00 Tune tests by froos in: [#104](https://codeberg.org/uzu/strudel/pulls/104) -- 2022-05-01T14:33:11+02:00 Codemirror 6 by froos in: [#97](https://codeberg.org/uzu/strudel/pulls/97) +- 2022-05-29T09:54:59+02:00 In source doc by @froos in: [#117](https://codeberg.org/uzu/strudel/pulls/117) +- 2022-05-20T11:49:10+02:00 react package + vite build by @froos in: [#116](https://codeberg.org/uzu/strudel/pulls/116) +- 2022-05-15T22:29:26+02:00 Osc timing improvements by @yaxu in: [#113](https://codeberg.org/uzu/strudel/pulls/113) +- 2022-05-15T22:28:24+02:00 loopAt by @yaxu in: [#114](https://codeberg.org/uzu/strudel/pulls/114) +- 2022-05-09T20:09:47+02:00 `.brak()`, `.inside()` and `.outside()` by @yaxu in: [#112](https://codeberg.org/uzu/strudel/pulls/112) +- 2022-05-06T15:18:41+02:00 In source doc by @yaxu in: [#105](https://codeberg.org/uzu/strudel/pulls/105) +- 2022-05-04T22:59:31+02:00 Embed style by @froos in: [#109](https://codeberg.org/uzu/strudel/pulls/109) +- 2022-05-03T01:40:22+02:00 Reset, Restart and other composers by @froos in: [#88](https://codeberg.org/uzu/strudel/pulls/88) +- 2022-05-02T22:47:21+02:00 /embed package: web component for repl by @froos in: [#106](https://codeberg.org/uzu/strudel/pulls/106) +- 2022-05-02T22:05:34+02:00 Tune tests by @froos in: [#104](https://codeberg.org/uzu/strudel/pulls/104) +- 2022-05-01T14:33:11+02:00 Codemirror 6 by @froos in: [#97](https://codeberg.org/uzu/strudel/pulls/97) ## april 2022 -- 2022-04-28T23:45:53+02:00 Work on Codemirror 6 highlighting by Ghost in: [#102](https://codeberg.org/uzu/strudel/pulls/102) -- 2022-04-28T15:46:06+02:00 Change to Affero GPL by yaxu in: [#101](https://codeberg.org/uzu/strudel/pulls/101) -- 2022-04-26T00:13:07+02:00 Paper by froos in: [#98](https://codeberg.org/uzu/strudel/pulls/98) -- 2022-04-25T22:10:21+02:00 Fiddles with cat/stack by yaxu in: [#90](https://codeberg.org/uzu/strudel/pulls/90) -- 2022-04-22T13:08:11+02:00 Add pattern composers, implements #82 by yaxu in: [#83](https://codeberg.org/uzu/strudel/pulls/83) -- 2022-04-21T20:57:45+02:00 Tidy up a couple of old files by Ghost in: [#84](https://codeberg.org/uzu/strudel/pulls/84) -- 2022-04-21T15:21:29+02:00 add `striate()` by yaxu in: [#76](https://codeberg.org/uzu/strudel/pulls/76) -- 2022-04-20T20:17:27+02:00 Webaudio in REPL by froos in: [#77](https://codeberg.org/uzu/strudel/pulls/77) -- 2022-04-19T15:31:34+02:00 Basic webserial support by yaxu in: [#80](https://codeberg.org/uzu/strudel/pulls/80) -- 2022-04-17T19:29:49+02:00 Try to fix appLeft / appRight by yaxu in: [#75](https://codeberg.org/uzu/strudel/pulls/75) -- 2022-04-16T13:26:57+02:00 More random functions by yaxu in: [#74](https://codeberg.org/uzu/strudel/pulls/74) -- 2022-04-16T11:13:26+02:00 Port `perlin` noise, `rangex`, and `palindrome` by yaxu in: [#73](https://codeberg.org/uzu/strudel/pulls/73) -- 2022-04-16T10:26:07+02:00 webaudio package by froos in: [#26](https://codeberg.org/uzu/strudel/pulls/26) -- 2022-04-15T20:18:06+02:00 More randomness, fix `rand`, and add `brand`, `irand` and `choose` by yaxu in: [#70](https://codeberg.org/uzu/strudel/pulls/70) -- 2022-04-15T11:29:51+02:00 First effort at rand() by yaxu in: [#69](https://codeberg.org/uzu/strudel/pulls/69) -- 2022-04-14T17:41:18+02:00 use new fixed version of osc-js package by froos in: [#68](https://codeberg.org/uzu/strudel/pulls/68) -- 2022-04-14T00:09:18+02:00 Speech output by froos in: [#67](https://codeberg.org/uzu/strudel/pulls/67) -- 2022-04-13T23:55:33+02:00 Separate out strudel.mjs, make index.mjs aggregate module by yaxu in: [#62](https://codeberg.org/uzu/strudel/pulls/62) -- 2022-04-13T17:26:45+02:00 More functions by yaxu in: [#61](https://codeberg.org/uzu/strudel/pulls/61) -- 2022-04-13T10:04:29+02:00 More functions by yaxu in: [#56](https://codeberg.org/uzu/strudel/pulls/56) -- 2022-04-12T17:04:04+02:00 OSC and SuperDirt support by yaxu in: [#27](https://codeberg.org/uzu/strudel/pulls/27) -- 2022-04-12T13:24:14+02:00 Implement `chop()` by yaxu in: [#50](https://codeberg.org/uzu/strudel/pulls/50) -- 2022-04-12T12:37:32+02:00 First run at squeezeBind, ref #32 by yaxu in: [#48](https://codeberg.org/uzu/strudel/pulls/48) -- 2022-04-12T00:03:37+02:00 Fix polymeter by yaxu in: [#44](https://codeberg.org/uzu/strudel/pulls/44) -- 2022-04-11T22:39:44+02:00 Compose by froos in: [#40](https://codeberg.org/uzu/strudel/pulls/40) -- 2022-04-11T21:49:35+02:00 Update tutorial.mdx by Ghost in: [#38](https://codeberg.org/uzu/strudel/pulls/38) -- 2022-04-11T08:43:44+02:00 Update tutorial.mdx by Ghost in: [#37](https://codeberg.org/uzu/strudel/pulls/37) +- 2022-04-28T23:45:53+02:00 Work on Codemirror 6 highlighting by @Ghost in: [#102](https://codeberg.org/uzu/strudel/pulls/102) +- 2022-04-28T15:46:06+02:00 Change to Affero GPL by @yaxu in: [#101](https://codeberg.org/uzu/strudel/pulls/101) +- 2022-04-26T00:13:07+02:00 Paper by @froos in: [#98](https://codeberg.org/uzu/strudel/pulls/98) +- 2022-04-25T22:10:21+02:00 Fiddles with cat/stack by @yaxu in: [#90](https://codeberg.org/uzu/strudel/pulls/90) +- 2022-04-22T13:08:11+02:00 Add pattern composers, implements #82 by @yaxu in: [#83](https://codeberg.org/uzu/strudel/pulls/83) +- 2022-04-21T20:57:45+02:00 Tidy up a couple of old files by @Ghost in: [#84](https://codeberg.org/uzu/strudel/pulls/84) +- 2022-04-21T15:21:29+02:00 add `striate()` by @yaxu in: [#76](https://codeberg.org/uzu/strudel/pulls/76) +- 2022-04-20T20:17:27+02:00 Webaudio in REPL by @froos in: [#77](https://codeberg.org/uzu/strudel/pulls/77) +- 2022-04-19T15:31:34+02:00 Basic webserial support by @yaxu in: [#80](https://codeberg.org/uzu/strudel/pulls/80) +- 2022-04-17T19:29:49+02:00 Try to fix appLeft / appRight by @yaxu in: [#75](https://codeberg.org/uzu/strudel/pulls/75) +- 2022-04-16T13:26:57+02:00 More random functions by @yaxu in: [#74](https://codeberg.org/uzu/strudel/pulls/74) +- 2022-04-16T11:13:26+02:00 Port `perlin` noise, `rangex`, and `palindrome` by @yaxu in: [#73](https://codeberg.org/uzu/strudel/pulls/73) +- 2022-04-16T10:26:07+02:00 webaudio package by @froos in: [#26](https://codeberg.org/uzu/strudel/pulls/26) +- 2022-04-15T20:18:06+02:00 More randomness, fix `rand`, and add `brand`, `irand` and `choose` by @yaxu in: [#70](https://codeberg.org/uzu/strudel/pulls/70) +- 2022-04-15T11:29:51+02:00 First effort at rand() by @yaxu in: [#69](https://codeberg.org/uzu/strudel/pulls/69) +- 2022-04-14T17:41:18+02:00 use new fixed version of osc-js package by @froos in: [#68](https://codeberg.org/uzu/strudel/pulls/68) +- 2022-04-14T00:09:18+02:00 Speech output by @froos in: [#67](https://codeberg.org/uzu/strudel/pulls/67) +- 2022-04-13T23:55:33+02:00 Separate out strudel.mjs, make index.mjs aggregate module by @yaxu in: [#62](https://codeberg.org/uzu/strudel/pulls/62) +- 2022-04-13T17:26:45+02:00 More functions by @yaxu in: [#61](https://codeberg.org/uzu/strudel/pulls/61) +- 2022-04-13T10:04:29+02:00 More functions by @yaxu in: [#56](https://codeberg.org/uzu/strudel/pulls/56) +- 2022-04-12T17:04:04+02:00 OSC and SuperDirt support by @yaxu in: [#27](https://codeberg.org/uzu/strudel/pulls/27) +- 2022-04-12T13:24:14+02:00 Implement `chop()` by @yaxu in: [#50](https://codeberg.org/uzu/strudel/pulls/50) +- 2022-04-12T12:37:32+02:00 First run at squeezeBind, ref #32 by @yaxu in: [#48](https://codeberg.org/uzu/strudel/pulls/48) +- 2022-04-12T00:03:37+02:00 Fix polymeter by @yaxu in: [#44](https://codeberg.org/uzu/strudel/pulls/44) +- 2022-04-11T22:39:44+02:00 Compose by @froos in: [#40](https://codeberg.org/uzu/strudel/pulls/40) +- 2022-04-11T21:49:35+02:00 Update tutorial.mdx by @Ghost in: [#38](https://codeberg.org/uzu/strudel/pulls/38) +- 2022-04-11T08:43:44+02:00 Update tutorial.mdx by @Ghost in: [#37](https://codeberg.org/uzu/strudel/pulls/37) ## march 2022 -- 2022-03-28T17:27:22+02:00 Add chunk, chunkBack and iterBack by yaxu in: [#25](https://codeberg.org/uzu/strudel/pulls/25) -- 2022-03-28T11:48:22+02:00 packaging by froos in: [#24](https://codeberg.org/uzu/strudel/pulls/24) -- 2022-03-22T12:06:48+01:00 Update package.json by Ghost in: [#23](https://codeberg.org/uzu/strudel/pulls/23) -- 2022-03-06T20:35:20+01:00 added _asNumber + interprete numbers as midi by froos in: [#21](https://codeberg.org/uzu/strudel/pulls/21) +- 2022-03-28T17:27:22+02:00 Add chunk, chunkBack and iterBack by @yaxu in: [#25](https://codeberg.org/uzu/strudel/pulls/25) +- 2022-03-28T11:48:22+02:00 packaging by @froos in: [#24](https://codeberg.org/uzu/strudel/pulls/24) +- 2022-03-22T12:06:48+01:00 Update package.json by @Ghost in: [#23](https://codeberg.org/uzu/strudel/pulls/23) +- 2022-03-06T20:35:20+01:00 added _asNumber + interprete numbers as midi by @froos in: [#21](https://codeberg.org/uzu/strudel/pulls/21) ## february 2022 -- 2022-02-28T00:32:29+01:00 Fix resolveState by yaxu in: [#22](https://codeberg.org/uzu/strudel/pulls/22) -- 2022-02-27T20:52:52+01:00 Stateful queries and events (WIP) by yaxu in: [#14](https://codeberg.org/uzu/strudel/pulls/14) -- 2022-02-27T18:04:07+01:00 fix: 💄 Enhance visualisation of the Tutorial on mobile by Ghost in: [#19](https://codeberg.org/uzu/strudel/pulls/19) -- 2022-02-26T21:16:34+01:00 test: 📦 Add missing dependency and a CI check, to prevent oversights ;p by Ghost in: [#17](https://codeberg.org/uzu/strudel/pulls/17) -- 2022-02-26T13:29:19+01:00 higher latencyHint by froos in: [#16](https://codeberg.org/uzu/strudel/pulls/16) -- 2022-02-23T22:15:11+01:00 add apply and layer, and missing div/mul methods by yaxu in: [#15](https://codeberg.org/uzu/strudel/pulls/15) -- 2022-02-21T01:02:07+01:00 Add continuous signals (sine, cosine, saw, etc) by yaxu in: [#13](https://codeberg.org/uzu/strudel/pulls/13) -- 2022-02-19T21:30:04+01:00 Added mask() and struct() by yaxu in: [#11](https://codeberg.org/uzu/strudel/pulls/11) -- 2022-02-19T16:07:07+01:00 Failing test for `when` WIP by yaxu in: [#10](https://codeberg.org/uzu/strudel/pulls/10) -- 2022-02-10T17:21:31+01:00 Bugfix every, and create more top level functions by yaxu in: [#9](https://codeberg.org/uzu/strudel/pulls/9) -- 2022-02-10T00:51:21+01:00 timeCat by yaxu in: [#8](https://codeberg.org/uzu/strudel/pulls/8) -- 2022-02-07T23:07:58+01:00 fixed editor crash by froos in: [#7](https://codeberg.org/uzu/strudel/pulls/7) -- 2022-02-07T19:08:40+01:00 krill parser + improved repl by froos in: [#6](https://codeberg.org/uzu/strudel/pulls/6) -- 2022-02-06T00:59:16+01:00 Patternify all the things by yaxu in: [#5](https://codeberg.org/uzu/strudel/pulls/5) -- 2022-02-05T23:31:37+01:00 update readme for local dev by Ghost in: [#4](https://codeberg.org/uzu/strudel/pulls/4) -- 2022-02-05T22:36:06+01:00 Fix path by yaxu in: [#3](https://codeberg.org/uzu/strudel/pulls/3) -- 2022-02-05T21:52:51+01:00 repl + reify functions by froos in: [#2](https://codeberg.org/uzu/strudel/pulls/2) +- 2022-02-28T00:32:29+01:00 Fix resolveState by @yaxu in: [#22](https://codeberg.org/uzu/strudel/pulls/22) +- 2022-02-27T20:52:52+01:00 Stateful queries and events (WIP) by @yaxu in: [#14](https://codeberg.org/uzu/strudel/pulls/14) +- 2022-02-27T18:04:07+01:00 fix: 💄 Enhance visualisation of the Tutorial on mobile by @Ghost in: [#19](https://codeberg.org/uzu/strudel/pulls/19) +- 2022-02-26T21:16:34+01:00 test: 📦 Add missing dependency and a CI check, to prevent oversights ;p by @Ghost in: [#17](https://codeberg.org/uzu/strudel/pulls/17) +- 2022-02-26T13:29:19+01:00 higher latencyHint by @froos in: [#16](https://codeberg.org/uzu/strudel/pulls/16) +- 2022-02-23T22:15:11+01:00 add apply and layer, and missing div/mul methods by @yaxu in: [#15](https://codeberg.org/uzu/strudel/pulls/15) +- 2022-02-21T01:02:07+01:00 Add continuous signals (sine, cosine, saw, etc) by @yaxu in: [#13](https://codeberg.org/uzu/strudel/pulls/13) +- 2022-02-19T21:30:04+01:00 Added mask() and struct() by @yaxu in: [#11](https://codeberg.org/uzu/strudel/pulls/11) +- 2022-02-19T16:07:07+01:00 Failing test for `when` WIP by @yaxu in: [#10](https://codeberg.org/uzu/strudel/pulls/10) +- 2022-02-10T17:21:31+01:00 Bugfix every, and create more top level functions by @yaxu in: [#9](https://codeberg.org/uzu/strudel/pulls/9) +- 2022-02-10T00:51:21+01:00 timeCat by @yaxu in: [#8](https://codeberg.org/uzu/strudel/pulls/8) +- 2022-02-07T23:07:58+01:00 fixed editor crash by @froos in: [#7](https://codeberg.org/uzu/strudel/pulls/7) +- 2022-02-07T19:08:40+01:00 krill parser + improved repl by @froos in: [#6](https://codeberg.org/uzu/strudel/pulls/6) +- 2022-02-06T00:59:16+01:00 Patternify all the things by @yaxu in: [#5](https://codeberg.org/uzu/strudel/pulls/5) +- 2022-02-05T23:31:37+01:00 update readme for local dev by @Ghost in: [#4](https://codeberg.org/uzu/strudel/pulls/4) +- 2022-02-05T22:36:06+01:00 Fix path by @yaxu in: [#3](https://codeberg.org/uzu/strudel/pulls/3) +- 2022-02-05T21:52:51+01:00 repl + reify functions by @froos in: [#2](https://codeberg.org/uzu/strudel/pulls/2) [9:37:36] strudel % \ No newline at end of file From 2dc921e3756f6805d15d92ca28108c7b18921fda Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 16 Jan 2026 10:14:17 +0100 Subject: [PATCH 107/124] fix: add fun fact --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 587343e43..911463dec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1076,5 +1076,6 @@ NOTE: you can generate this with `node warm.js`. it might still not be perfectly - 2022-02-06T00:59:16+01:00 Patternify all the things by @yaxu in: [#5](https://codeberg.org/uzu/strudel/pulls/5) - 2022-02-05T23:31:37+01:00 update readme for local dev by @Ghost in: [#4](https://codeberg.org/uzu/strudel/pulls/4) - 2022-02-05T22:36:06+01:00 Fix path by @yaxu in: [#3](https://codeberg.org/uzu/strudel/pulls/3) -- 2022-02-05T21:52:51+01:00 repl + reify functions by @froos in: [#2](https://codeberg.org/uzu/strudel/pulls/2) -[9:37:36] strudel % \ No newline at end of file +- 2022-02-05T21:52:51+01:00 repl + reify functions by @froos in: [#2](https://codeberg.org/uzu/strudel/pulls/2) + +... hello from the bottom of this file! you seem to be interested in the history of strudel, so it's worth noting that the first [commit](https://codeberg.org/uzu/strudel/commit/38b5a0d5cdf28685b2b5e18d460772b70246207b) to the repo was actually on Jan 22, 2022, 09:24 PM GMT+1 by @yaxu which could be seen as strudel's birthday 🎂 From f9d0e154e841997f4260b9ceb81faa58bff5fee2 Mon Sep 17 00:00:00 2001 From: robojumper Date: Sat, 17 Jan 2026 17:47:48 +0100 Subject: [PATCH 108/124] fix: performance issues when reference is open --- website/src/repl/components/panel/Reference.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index c03ac0cf9..4c48b6a35 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react'; +import { memo, useMemo, useState } from 'react'; import jsdocJson from '../../../../../doc.json'; import { Textbox } from '../textbox/Textbox'; @@ -35,7 +35,7 @@ const getInnerText = (html) => { return div.textContent || div.innerText || ''; }; -export function Reference() { +export const Reference = memo(function Reference() { const [search, setSearch] = useState(''); const visibleFunctions = useMemo(() => { @@ -112,4 +112,4 @@ export function Reference() {
); -} +}); From 77076a1c3eeed4a1d9f070336c8805ed9d6349b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sat, 17 Jan 2026 19:12:23 +0100 Subject: [PATCH 109/124] Improve reference search/filtering semantics --- .../src/repl/components/panel/Reference.jsx | 125 +++++++++++++----- 1 file changed, 92 insertions(+), 33 deletions(-) diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index c943db144..24d4a6773 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import jsdocJson from '../../../../../doc.json'; import { Textbox } from '../textbox/Textbox'; @@ -46,6 +46,7 @@ const getInnerText = (html) => { export function Reference() { const [search, setSearch] = useState(''); const [selectedTag, setSelectedTag] = useState(null); + const [selectedFunction, setSelectedFunction] = useState(null); const toggleTag = (tag) => { if (selectedTag === tag) { @@ -55,7 +56,7 @@ export function Reference() { } }; - const visibleFunctions = useMemo(() => { + const searchVisibleFunctions = useMemo(() => { return availableFunctions.filter((entry) => { if (selectedTag) { if (!(entry.tags || ['untagged']).includes(selectedTag)) { @@ -63,6 +64,14 @@ export function Reference() { } } + // if (entry.name === selectedFunction) { + // return true; + // } + + // if (!selectedTag) { + // return false; + // } + if (!search) { return true; } @@ -75,6 +84,19 @@ export function Reference() { }); }, [search, selectedTag]); + const detailVisibleFunctions = useMemo(() => { + return searchVisibleFunctions.filter((x) => { + if (selectedTag === null) { + if (search) { + return true; + } + return x.name === selectedFunction; + } else { + return true; + } + }); + }, [searchVisibleFunctions, selectedFunction, selectedTag]); + const tagCounts = {}; for (const doc of availableFunctions) { (doc.tags || ['untagged']).forEach((t) => { @@ -84,42 +106,60 @@ export function Reference() { }); } + const onSearchTagFilterClick = () => { + const container = document.getElementById('reference-container'); + container.scrollTo(0, 0); + setSelectedTag(null); + setSelectedFunction(null); + }; + + useEffect(() => { + if (selectedFunction) { + const el = document.getElementById(`doc-${selectedFunction}`); + const container = document.getElementById('reference-container'); + container.scrollTo(0, el.offsetTop); + } + }, [selectedFunction]); + return ( - //
- //
- //
-
+
- -
- {Object.entries(tagCounts) - .sort(([a], [b]) => a.localeCompare(b)) - .map(([t, count]) => ( - - toggleTag(t)} - > - {t} ({count}) - {' '} - - ))} -
+ { + setSelectedFunction(null); + setSearch(e); + }} + />
-
- {visibleFunctions.map((entry, i) => ( + {selectedTag && ( +
+ + {selectedTag ? selectedTag + ' x' : 'Filter by tag'} + +
+ )} +
+ {searchVisibleFunctions.map((entry, i) => ( <> { - const el = document.getElementById(`doc-${entry.name}`); - const container = document.getElementById('reference-container'); - container.scrollTo(0, el.offsetTop); + if (entry.name === selectedFunction) { + setSelectedFunction(null); + } else { + setSelectedFunction(entry.name); + } }} > {entry.name} @@ -134,11 +174,28 @@ export function Reference() { >

API Reference

-

+

This is the long list of functions you can use. Remember that you don't need to remember all of those and that you can already make music with a small set of functions!

- {visibleFunctions.map((entry, i) => ( +
+ {detailVisibleFunctions.map((entry, i) => (

@@ -170,7 +227,9 @@ export function Reference() { ))}

- ))} + )) ||

Searcb or select a tag to get started.

} + {detailVisibleFunctions.length > 0 &&
} + {/* {!selectedTag &&

Select a tag to see the functions.

} */}
From 42d0bfadceba9cae8b276eba55aea578ddc28436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sat, 17 Jan 2026 20:08:23 +0100 Subject: [PATCH 110/124] Clean up --- .../src/repl/components/panel/Reference.jsx | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index 8a018ff90..950949b90 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -25,13 +25,13 @@ const availableFunctions = (() => { seen.add(s); // Swap `doc.name` in for `s` in the list of synonyms const synonymsWithDoc = [doc.name, ...synonyms].filter((x) => x && x !== s); - // functions.push({ - // ...doc, - // name: s, // update names for the synonym - // longname: s, - // synonyms: synonymsWithDoc, - // synonyms_text: synonymsWithDoc.join(', '), - // }); + functions.push({ + ...doc, + name: s, // update names for the synonym + longname: s, + synonyms: synonymsWithDoc, + synonyms_text: synonymsWithDoc.join(', '), + }); } } return functions.sort((a, b) => /* a.meta.filename.localeCompare(b.meta.filename) + */ a.name.localeCompare(b.name)); @@ -64,14 +64,6 @@ export function Reference() { } } - // if (entry.name === selectedFunction) { - // return true; - // } - - // if (!selectedTag) { - // return false; - // } - if (!search) { return true; } @@ -228,7 +220,6 @@ export function Reference() {
)) ||

Searcb or select a tag to get started.

} {detailVisibleFunctions.length > 0 &&
} - {/* {!selectedTag &&

Select a tag to see the functions.

} */}
From 99cf256ee253b12057775e689dfd3c73c943b504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sat, 17 Jan 2026 20:11:26 +0100 Subject: [PATCH 111/124] Clean up 2 --- website/src/repl/components/panel/Reference.jsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index 950949b90..e5bb56000 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -99,8 +99,6 @@ export function Reference() { } const onSearchTagFilterClick = () => { - const container = document.getElementById('reference-container'); - container.scrollTo(0, 0); setSelectedTag(null); setSelectedFunction(null); }; @@ -133,7 +131,7 @@ export function Reference() { className="text-foreground border-2 border-gray-500 px-1 py-0.5 my-2 rounded-md cursor-pointer font-sans" onClick={onSearchTagFilterClick} > - {selectedTag ? selectedTag + ' x' : 'Filter by tag'} + {selectedTag} )} From 7bc8576a68ed959dd563776ed54c47b053d73dd7 Mon Sep 17 00:00:00 2001 From: Aria Date: Sat, 17 Jan 2026 11:16:58 -0800 Subject: [PATCH 112/124] Make kabelsalat stereo out --- packages/superdough/superdough.mjs | 2 +- packages/superdough/worklets.mjs | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index ec44e8af7..03a4c3da8 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -613,7 +613,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) // Kabelsalat if (fx.workletSrc !== undefined) { - const workletNode = getWorklet(ac, 'generic-processor', {}); + const workletNode = getWorklet(ac, 'generic-processor', {}, { outputChannelCount: [2] }); chain.connect(workletNode); const workletSrc = fx.workletSrc .replace(/\bpat\[(\d+)\]/g, (_, i) => fx.workletInputs[i]) diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 6969ee2c3..26a0fa668 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -1540,12 +1540,20 @@ class GenericProcessor extends AudioWorkletProcessor { this.gateNode?.setValue(0); this.gateEnded = true; } - const outL = outputs[0][0]; - const outR = outputs[0][1] ?? outputs[0][0]; + const output = outputs[0]; + const outL = output[0]; + const outR = output[1]; for (let n = 0; n < blockSize; n++) { this.genSample(this.playPos, this.nodes, input ? input[n] : 0, this.registers, this.outputs, this.sources); - outL[n] = this.outputs[0]; - outR[n] = this.outputs[1]; + const left = this.outputs[0]; + const right = this.outputs[1]; + // Spread to stereo if possible; else mixdown to mono + if (outR) { + outL[n] = left; + outR[n] = right; + } else { + outL[n] = 0.5 * (left + right); + } this.playPos += 1 / sampleRate; } return true; From d198910a86fcaecd658ef528bfa6d7884e93490b Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 17 Jan 2026 15:26:28 -0800 Subject: [PATCH 113/124] Publish - @strudel/codemirror@1.2.7 - @strudel/core@1.2.6 - @strudel/csound@1.2.7 - @strudel/draw@1.2.6 - @strudel/embed@1.1.2 - @strudel/gamepad@1.2.6 - @strudel/hydra@1.2.6 - @strudel/midi@1.2.7 - @strudel/mini@1.2.6 - mondolang@1.1.2 - @strudel/mondo@1.1.6 - @strudel/motion@1.2.6 - @strudel/mqtt@1.2.6 - @strudel/osc@1.3.1 - @strudel/reference@1.2.2 - @strudel/repl@1.2.8 - @strudel/sampler@0.2.4 - @strudel/serial@1.2.6 - @strudel/soundfonts@1.2.7 - superdough@1.2.7 - supradough@1.2.5 - @strudel/tonal@1.2.6 - @strudel/transpiler@1.2.6 - vite-plugin-bundle-audioworklet@0.1.2 - @strudel/web@1.2.7 - @strudel/webaudio@1.2.7 - @strudel/xen@1.2.6 --- packages/codemirror/package.json | 2 +- packages/core/package.json | 2 +- packages/csound/package.json | 2 +- packages/draw/package.json | 2 +- packages/embed/package.json | 2 +- packages/gamepad/package.json | 2 +- packages/hydra/package.json | 2 +- packages/midi/package.json | 2 +- packages/mini/package.json | 2 +- packages/mondo/package.json | 2 +- packages/mondough/package.json | 2 +- packages/motion/package.json | 2 +- packages/mqtt/package.json | 2 +- packages/osc/package.json | 2 +- packages/reference/package.json | 2 +- packages/repl/package.json | 2 +- packages/sampler/package.json | 2 +- packages/serial/package.json | 2 +- packages/soundfonts/package.json | 2 +- packages/superdough/package.json | 2 +- packages/supradough/package.json | 2 +- packages/tonal/package.json | 2 +- packages/transpiler/package.json | 2 +- packages/vite-plugin-bundle-audioworklet/package.json | 2 +- packages/web/package.json | 2 +- packages/webaudio/package.json | 2 +- packages/xen/package.json | 2 +- 27 files changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/codemirror/package.json b/packages/codemirror/package.json index a3735491f..2f87124a4 100644 --- a/packages/codemirror/package.json +++ b/packages/codemirror/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/codemirror", - "version": "1.2.6", + "version": "1.2.7", "description": "Codemirror Extensions for Strudel", "main": "index.mjs", "publishConfig": { diff --git a/packages/core/package.json b/packages/core/package.json index abb54e9ce..e558c574c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/core", - "version": "1.2.5", + "version": "1.2.6", "description": "Port of Tidal Cycles to JavaScript", "main": "index.mjs", "type": "module", diff --git a/packages/csound/package.json b/packages/csound/package.json index 762131d65..039752758 100644 --- a/packages/csound/package.json +++ b/packages/csound/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/csound", - "version": "1.2.6", + "version": "1.2.7", "description": "csound bindings for strudel", "main": "index.mjs", "type": "module", diff --git a/packages/draw/package.json b/packages/draw/package.json index b0af8418b..4a6f9a5a3 100644 --- a/packages/draw/package.json +++ b/packages/draw/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/draw", - "version": "1.2.5", + "version": "1.2.6", "description": "Helpers for drawing with Strudel", "main": "index.mjs", "type": "module", diff --git a/packages/embed/package.json b/packages/embed/package.json index ffafdc8bc..84a8cd6f5 100644 --- a/packages/embed/package.json +++ b/packages/embed/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/embed", - "version": "1.1.1", + "version": "1.1.2", "description": "Embeddable Web Component to load a Strudel REPL into an iframe", "main": "embed.js", "type": "module", diff --git a/packages/gamepad/package.json b/packages/gamepad/package.json index 6a07df968..21ad8eef3 100644 --- a/packages/gamepad/package.json +++ b/packages/gamepad/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/gamepad", - "version": "1.2.5", + "version": "1.2.6", "description": "Gamepad Inputs for strudel", "main": "index.mjs", "type": "module", diff --git a/packages/hydra/package.json b/packages/hydra/package.json index 16d6f0f4c..67419ce07 100644 --- a/packages/hydra/package.json +++ b/packages/hydra/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/hydra", - "version": "1.2.5", + "version": "1.2.6", "description": "Hydra integration for strudel", "main": "hydra.mjs", "type": "module", diff --git a/packages/midi/package.json b/packages/midi/package.json index 11fe9a3f7..368911276 100644 --- a/packages/midi/package.json +++ b/packages/midi/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/midi", - "version": "1.2.6", + "version": "1.2.7", "description": "Midi API for strudel", "main": "index.mjs", "type": "module", diff --git a/packages/mini/package.json b/packages/mini/package.json index b9a50fb60..432306550 100644 --- a/packages/mini/package.json +++ b/packages/mini/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/mini", - "version": "1.2.5", + "version": "1.2.6", "description": "Mini notation for strudel", "main": "index.mjs", "type": "module", diff --git a/packages/mondo/package.json b/packages/mondo/package.json index ebdbd329a..1dba882e4 100644 --- a/packages/mondo/package.json +++ b/packages/mondo/package.json @@ -1,6 +1,6 @@ { "name": "mondolang", - "version": "1.1.1", + "version": "1.1.2", "description": "a language for functional composition that translates to js", "main": "mondo.mjs", "type": "module", diff --git a/packages/mondough/package.json b/packages/mondough/package.json index 9b170fde5..919c5da2a 100644 --- a/packages/mondough/package.json +++ b/packages/mondough/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/mondo", - "version": "1.1.5", + "version": "1.1.6", "description": "mondo notation for strudel", "main": "mondough.mjs", "type": "module", diff --git a/packages/motion/package.json b/packages/motion/package.json index fd1fae890..dda3c3686 100644 --- a/packages/motion/package.json +++ b/packages/motion/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/motion", - "version": "1.2.5", + "version": "1.2.6", "description": "DeviceMotion API for strudel", "main": "index.mjs", "type": "module", diff --git a/packages/mqtt/package.json b/packages/mqtt/package.json index 492d915d5..9fa9da9b3 100644 --- a/packages/mqtt/package.json +++ b/packages/mqtt/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/mqtt", - "version": "1.2.5", + "version": "1.2.6", "description": "MQTT API for strudel", "main": "mqtt.mjs", "type": "module", diff --git a/packages/osc/package.json b/packages/osc/package.json index 00faffbd0..f1bbbdd55 100644 --- a/packages/osc/package.json +++ b/packages/osc/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/osc", - "version": "1.3.0", + "version": "1.3.1", "description": "OSC messaging for strudel", "main": "osc.mjs", "bin": "./server.js", diff --git a/packages/reference/package.json b/packages/reference/package.json index 6d1fa1807..783c1bbc9 100644 --- a/packages/reference/package.json +++ b/packages/reference/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/reference", - "version": "1.2.1", + "version": "1.2.2", "description": "Headless reference of all strudel functions", "main": "index.mjs", "type": "module", diff --git a/packages/repl/package.json b/packages/repl/package.json index e5216316e..003086a9d 100644 --- a/packages/repl/package.json +++ b/packages/repl/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/repl", - "version": "1.2.7", + "version": "1.2.8", "description": "Strudel REPL as a Web Component", "module": "index.mjs", "publishConfig": { diff --git a/packages/sampler/package.json b/packages/sampler/package.json index b9ea4c2a1..a960fbbe0 100644 --- a/packages/sampler/package.json +++ b/packages/sampler/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/sampler", - "version": "0.2.3", + "version": "0.2.4", "description": "", "keywords": [ "tidalcycles", diff --git a/packages/serial/package.json b/packages/serial/package.json index 9dc6c8999..e98a897e4 100644 --- a/packages/serial/package.json +++ b/packages/serial/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/serial", - "version": "1.2.5", + "version": "1.2.6", "description": "Webserial API for strudel", "main": "serial.mjs", "type": "module", diff --git a/packages/soundfonts/package.json b/packages/soundfonts/package.json index 9bf0c4006..dfdce7828 100644 --- a/packages/soundfonts/package.json +++ b/packages/soundfonts/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/soundfonts", - "version": "1.2.6", + "version": "1.2.7", "description": "Soundsfont support for strudel", "main": "index.mjs", "publishConfig": { diff --git a/packages/superdough/package.json b/packages/superdough/package.json index 78db8338e..c6ed7c64b 100644 --- a/packages/superdough/package.json +++ b/packages/superdough/package.json @@ -1,6 +1,6 @@ { "name": "superdough", - "version": "1.2.6", + "version": "1.2.7", "description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.", "main": "index.mjs", "type": "module", diff --git a/packages/supradough/package.json b/packages/supradough/package.json index ee3869d08..10cf8df57 100644 --- a/packages/supradough/package.json +++ b/packages/supradough/package.json @@ -1,6 +1,6 @@ { "name": "supradough", - "version": "1.2.4", + "version": "1.2.5", "description": "platform agnostic synth and sampler intended for live coding. a reimplementation of superdough.", "main": "index.mjs", "type": "module", diff --git a/packages/tonal/package.json b/packages/tonal/package.json index 14ffabc7b..a6a539c98 100644 --- a/packages/tonal/package.json +++ b/packages/tonal/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/tonal", - "version": "1.2.5", + "version": "1.2.6", "description": "Tonal functions for strudel", "main": "index.mjs", "publishConfig": { diff --git a/packages/transpiler/package.json b/packages/transpiler/package.json index 513550442..7b2a053f9 100644 --- a/packages/transpiler/package.json +++ b/packages/transpiler/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/transpiler", - "version": "1.2.5", + "version": "1.2.6", "description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.", "main": "index.mjs", "type": "module", diff --git a/packages/vite-plugin-bundle-audioworklet/package.json b/packages/vite-plugin-bundle-audioworklet/package.json index 198a4f9cf..524af4706 100644 --- a/packages/vite-plugin-bundle-audioworklet/package.json +++ b/packages/vite-plugin-bundle-audioworklet/package.json @@ -1,7 +1,7 @@ { "name": "vite-plugin-bundle-audioworklet", "main": "./vite-plugin-bundle-audioworklet.js", - "version": "0.1.1", + "version": "0.1.2", "description": "", "keywords": [ "vite", diff --git a/packages/web/package.json b/packages/web/package.json index f02d86139..40ba83c5d 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/web", - "version": "1.2.6", + "version": "1.2.7", "description": "Easy to setup, opiniated bundle of Strudel for the browser.", "module": "web.mjs", "publishConfig": { diff --git a/packages/webaudio/package.json b/packages/webaudio/package.json index f0bc4043f..2815c739c 100644 --- a/packages/webaudio/package.json +++ b/packages/webaudio/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/webaudio", - "version": "1.2.6", + "version": "1.2.7", "description": "Web Audio helpers for Strudel", "main": "index.mjs", "type": "module", diff --git a/packages/xen/package.json b/packages/xen/package.json index c178c5de4..e7d9d2038 100644 --- a/packages/xen/package.json +++ b/packages/xen/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/xen", - "version": "1.2.5", + "version": "1.2.6", "description": "Xenharmonic API for strudel", "main": "index.mjs", "type": "module", From 85e787214b476f0f6f03578e210269711a0175f6 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 17 Jan 2026 15:59:38 -0800 Subject: [PATCH 114/124] rollback_supradough --- packages/superdough/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/package.json b/packages/superdough/package.json index c6ed7c64b..23b64b29c 100644 --- a/packages/superdough/package.json +++ b/packages/superdough/package.json @@ -1,6 +1,6 @@ { "name": "superdough", - "version": "1.2.7", + "version": "1.2.4", "description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.", "main": "index.mjs", "type": "module", From 166985a05576a5cb88f2965748f0ef61d3fadedd Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 17 Jan 2026 16:00:34 -0800 Subject: [PATCH 115/124] spr --- packages/osc/server.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 packages/osc/server.js diff --git a/packages/osc/server.js b/packages/osc/server.js old mode 100644 new mode 100755 From 12b89f9f88b01de01de8a18e13f110cea499bcfa Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 17 Jan 2026 16:12:51 -0800 Subject: [PATCH 116/124] rollback supradough --- packages/supradough/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/supradough/package.json b/packages/supradough/package.json index 10cf8df57..ee3869d08 100644 --- a/packages/supradough/package.json +++ b/packages/supradough/package.json @@ -1,6 +1,6 @@ { "name": "supradough", - "version": "1.2.5", + "version": "1.2.4", "description": "platform agnostic synth and sampler intended for live coding. a reimplementation of superdough.", "main": "index.mjs", "type": "module", From a3ac9646f5cb86958bb6193cddd5f30f78a57368 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 17 Jan 2026 18:54:39 -0800 Subject: [PATCH 117/124] inc webaudio --- packages/webaudio/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webaudio/package.json b/packages/webaudio/package.json index 2815c739c..18bd32909 100644 --- a/packages/webaudio/package.json +++ b/packages/webaudio/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/webaudio", - "version": "1.2.7", + "version": "1.2.8", "description": "Web Audio helpers for Strudel", "main": "index.mjs", "type": "module", From f4f04a7d574684aea78a757ef0d92370225cb27d Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 17 Jan 2026 19:01:10 -0800 Subject: [PATCH 118/124] Publish - @strudel/codemirror@1.2.8 - @strudel/csound@1.2.8 - @strudel/midi@1.2.8 - @strudel/osc@1.3.2 - @strudel/repl@1.2.9 - @strudel/soundfonts@1.2.8 - superdough@1.2.5 - supradough@1.2.4 - @strudel/web@1.2.8 - @strudel/webaudio@1.2.9 --- packages/codemirror/package.json | 2 +- packages/csound/package.json | 2 +- packages/midi/package.json | 2 +- packages/osc/package.json | 2 +- packages/repl/package.json | 2 +- packages/soundfonts/package.json | 2 +- packages/superdough/package.json | 2 +- packages/web/package.json | 2 +- packages/webaudio/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/codemirror/package.json b/packages/codemirror/package.json index 2f87124a4..9ad040976 100644 --- a/packages/codemirror/package.json +++ b/packages/codemirror/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/codemirror", - "version": "1.2.7", + "version": "1.2.8", "description": "Codemirror Extensions for Strudel", "main": "index.mjs", "publishConfig": { diff --git a/packages/csound/package.json b/packages/csound/package.json index 039752758..19ba68199 100644 --- a/packages/csound/package.json +++ b/packages/csound/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/csound", - "version": "1.2.7", + "version": "1.2.8", "description": "csound bindings for strudel", "main": "index.mjs", "type": "module", diff --git a/packages/midi/package.json b/packages/midi/package.json index 368911276..38d0f0ca9 100644 --- a/packages/midi/package.json +++ b/packages/midi/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/midi", - "version": "1.2.7", + "version": "1.2.8", "description": "Midi API for strudel", "main": "index.mjs", "type": "module", diff --git a/packages/osc/package.json b/packages/osc/package.json index f1bbbdd55..569242252 100644 --- a/packages/osc/package.json +++ b/packages/osc/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/osc", - "version": "1.3.1", + "version": "1.3.2", "description": "OSC messaging for strudel", "main": "osc.mjs", "bin": "./server.js", diff --git a/packages/repl/package.json b/packages/repl/package.json index 003086a9d..d28034926 100644 --- a/packages/repl/package.json +++ b/packages/repl/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/repl", - "version": "1.2.8", + "version": "1.2.9", "description": "Strudel REPL as a Web Component", "module": "index.mjs", "publishConfig": { diff --git a/packages/soundfonts/package.json b/packages/soundfonts/package.json index dfdce7828..2403adfcf 100644 --- a/packages/soundfonts/package.json +++ b/packages/soundfonts/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/soundfonts", - "version": "1.2.7", + "version": "1.2.8", "description": "Soundsfont support for strudel", "main": "index.mjs", "publishConfig": { diff --git a/packages/superdough/package.json b/packages/superdough/package.json index 23b64b29c..d1e9b45fc 100644 --- a/packages/superdough/package.json +++ b/packages/superdough/package.json @@ -1,6 +1,6 @@ { "name": "superdough", - "version": "1.2.4", + "version": "1.2.5", "description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.", "main": "index.mjs", "type": "module", diff --git a/packages/web/package.json b/packages/web/package.json index 40ba83c5d..8be1f89dc 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/web", - "version": "1.2.7", + "version": "1.2.8", "description": "Easy to setup, opiniated bundle of Strudel for the browser.", "module": "web.mjs", "publishConfig": { diff --git a/packages/webaudio/package.json b/packages/webaudio/package.json index 18bd32909..6b6980e99 100644 --- a/packages/webaudio/package.json +++ b/packages/webaudio/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/webaudio", - "version": "1.2.8", + "version": "1.2.9", "description": "Web Audio helpers for Strudel", "main": "index.mjs", "type": "module", From f0cb96dcad64e98cb7c692fc3949464317db2611 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 17 Jan 2026 19:02:25 -0800 Subject: [PATCH 119/124] patch --- lerna-debug.log | 211 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 lerna-debug.log diff --git a/lerna-debug.log b/lerna-debug.log new file mode 100644 index 000000000..3c15de507 --- /dev/null +++ b/lerna-debug.log @@ -0,0 +1,211 @@ +0 silly argv { +0 silly argv _: [ 'version' ], +0 silly argv private: false, +0 silly argv lernaVersion: '8.1.9', +0 silly argv '$0': 'node_modules/lerna/dist/cli.js' +0 silly argv } +1 notice cli v8.1.9 +2 verbose packageConfigs Explicit "packages" configuration found in lerna.json. Resolving packages using the configured glob(s): ["packages/*"] +3 verbose rootPath /Users/jaderose/Documents/Github/cstrudel/strudel +4 info versioning independent +5 silly isAnythingCommitted +6 verbose isAnythingCommitted 1 +7 silly getCurrentBranch +8 verbose currentBranch main +9 silly remoteBranchExists +10 silly isBehindUpstream +11 silly isBehindUpstream main is behind origin/main by 0 commit(s) and ahead by 4 +12 silly hasTags +13 verbose hasTags true +14 silly git-describe.sync "@strudel/codemirror@1.2.7-4-ga3ac9646" => {"lastTagName":"@strudel/codemirror@1.2.7","lastVersion":"1.2.7","refCount":"4","sha":"a3ac9646","isDirty":false} +15 info Looking for changed packages since @strudel/codemirror@1.2.7 +16 silly checking diff packages/codemirror +17 silly no diff found in @strudel/codemirror +18 silly checking diff packages/core +19 silly no diff found in @strudel/core +20 silly checking diff packages/csound +21 silly no diff found in @strudel/csound +22 silly checking diff packages/desktopbridge +23 silly no diff found in @strudel/desktopbridge +24 silly checking diff packages/draw +25 silly no diff found in @strudel/draw +26 silly checking diff packages/embed +27 silly no diff found in @strudel/embed +28 silly checking diff packages/gamepad +29 silly no diff found in @strudel/gamepad +30 silly checking diff packages/hs2js +31 silly no diff found in hs2js +32 silly checking diff packages/hydra +33 silly no diff found in @strudel/hydra +34 silly checking diff packages/midi +35 silly no diff found in @strudel/midi +36 silly checking diff packages/mini +37 silly no diff found in @strudel/mini +38 silly checking diff packages/mondo +39 silly no diff found in mondolang +40 silly checking diff packages/mondough +41 silly no diff found in @strudel/mondo +42 silly checking diff packages/motion +43 silly no diff found in @strudel/motion +44 silly checking diff packages/mqtt +45 silly no diff found in @strudel/mqtt +46 silly checking diff packages/osc +47 silly found diff in packages/osc/server.js +48 verbose filtered diff [ 'packages/osc/server.js' ] +49 silly checking diff packages/reference +50 silly no diff found in @strudel/reference +51 silly checking diff packages/repl +52 silly no diff found in @strudel/repl +53 silly checking diff packages/sampler +54 silly no diff found in @strudel/sampler +55 silly checking diff packages/serial +56 silly no diff found in @strudel/serial +57 silly checking diff packages/soundfonts +58 silly no diff found in @strudel/soundfonts +59 silly checking diff packages/superdough +60 silly found diff in packages/superdough/package.json +61 verbose filtered diff [ 'packages/superdough/package.json' ] +62 silly checking diff packages/supradough +63 silly found diff in packages/supradough/package.json +64 verbose filtered diff [ 'packages/supradough/package.json' ] +65 silly checking diff packages/tidal +66 silly no diff found in @strudel/tidal +67 silly checking diff packages/tonal +68 silly no diff found in @strudel/tonal +69 silly checking diff packages/transpiler +70 silly no diff found in @strudel/transpiler +71 silly checking diff packages/vite-plugin-bundle-audioworklet +72 silly no diff found in vite-plugin-bundle-audioworklet +73 silly checking diff packages/web +74 silly no diff found in @strudel/web +75 silly checking diff packages/webaudio +76 silly found diff in packages/webaudio/package.json +77 verbose filtered diff [ 'packages/webaudio/package.json' ] +78 silly checking diff packages/xen +79 silly no diff found in @strudel/xen +80 verbose updated @strudel/codemirror +81 verbose updated @strudel/csound +82 verbose updated @strudel/midi +83 verbose updated @strudel/osc +84 verbose updated @strudel/repl +85 verbose updated @strudel/soundfonts +86 verbose updated superdough +87 verbose updated supradough +88 verbose updated @strudel/web +89 verbose updated @strudel/webaudio +90 verbose git-describe undefined => "@strudel/codemirror@1.2.7-4-ga3ac9646" +91 silly git-describe parsed => {"lastTagName":"@strudel/codemirror@1.2.7","lastVersion":"1.2.7","refCount":"4","sha":"a3ac9646","isDirty":false} +92 info execute Skipping releases +93 silly lifecycle No script for "preversion" in "@strudel/monorepo", continuing +94 silly lifecycle No script for "preversion" in "@strudel/osc", continuing +95 silly lifecycle No script for "preversion" in "superdough", continuing +96 silly lifecycle No script for "preversion" in "supradough", continuing +97 verbose version supradough has no lockfile. Skipping lockfile update. +98 verbose version @strudel/osc has no lockfile. Skipping lockfile update. +99 verbose version superdough has no lockfile. Skipping lockfile update. +100 silly lifecycle No script for "version" in "supradough", continuing +101 silly lifecycle No script for "version" in "superdough", continuing +102 silly lifecycle No script for "preversion" in "@strudel/codemirror", continuing +103 silly lifecycle No script for "preversion" in "@strudel/webaudio", continuing +104 silly lifecycle No script for "version" in "@strudel/osc", continuing +105 verbose version @strudel/webaudio has no lockfile. Skipping lockfile update. +106 verbose version @strudel/codemirror has no lockfile. Skipping lockfile update. +107 silly lifecycle No script for "version" in "@strudel/codemirror", continuing +108 silly lifecycle No script for "version" in "@strudel/webaudio", continuing +109 silly lifecycle No script for "preversion" in "@strudel/csound", continuing +110 silly lifecycle No script for "preversion" in "@strudel/midi", continuing +111 silly lifecycle No script for "preversion" in "@strudel/soundfonts", continuing +112 silly lifecycle No script for "preversion" in "@strudel/web", continuing +113 verbose version @strudel/csound has no lockfile. Skipping lockfile update. +114 verbose version @strudel/midi has no lockfile. Skipping lockfile update. +115 verbose version @strudel/soundfonts has no lockfile. Skipping lockfile update. +116 verbose version @strudel/web has no lockfile. Skipping lockfile update. +117 silly lifecycle No script for "version" in "@strudel/midi", continuing +118 silly lifecycle No script for "version" in "@strudel/csound", continuing +119 silly lifecycle No script for "version" in "@strudel/soundfonts", continuing +120 silly lifecycle No script for "preversion" in "@strudel/repl", continuing +121 verbose version @strudel/repl has no lockfile. Skipping lockfile update. +122 silly lifecycle No script for "version" in "@strudel/web", continuing +123 silly lifecycle No script for "version" in "@strudel/repl", continuing +124 silly lifecycle No script for "version" in "@strudel/monorepo", continuing +125 verbose version Updating root pnpm-lock.yaml +126 silly version Skipped applying prettier to ignored file: packages/supradough/package.json +127 silly version Skipped applying prettier to ignored file: packages/superdough/package.json +128 silly version Skipped applying prettier to ignored file: packages/osc/package.json +129 silly version Skipped applying prettier to ignored file: packages/codemirror/package.json +130 silly version Skipped applying prettier to ignored file: packages/webaudio/package.json +131 silly version Skipped applying prettier to ignored file: packages/midi/package.json +132 silly version Skipped applying prettier to ignored file: packages/csound/package.json +133 silly version Skipped applying prettier to ignored file: packages/soundfonts/package.json +134 silly version Skipped applying prettier to ignored file: packages/web/package.json +135 silly version Skipped applying prettier to ignored file: packages/repl/package.json +136 silly version Skipped applying prettier to ignored file: pnpm-lock.yaml +137 silly gitAdd [ +137 silly gitAdd 'packages/supradough/package.json', +137 silly gitAdd 'packages/superdough/package.json', +137 silly gitAdd 'packages/osc/package.json', +137 silly gitAdd 'packages/codemirror/package.json', +137 silly gitAdd 'packages/webaudio/package.json', +137 silly gitAdd 'packages/midi/package.json', +137 silly gitAdd 'packages/csound/package.json', +137 silly gitAdd 'packages/soundfonts/package.json', +137 silly gitAdd 'packages/web/package.json', +137 silly gitAdd 'packages/repl/package.json', +137 silly gitAdd 'pnpm-lock.yaml' +137 silly gitAdd ] +138 silly gitCommit Publish +138 silly gitCommit +138 silly gitCommit - @strudel/codemirror@1.2.8 +138 silly gitCommit - @strudel/csound@1.2.8 +138 silly gitCommit - @strudel/midi@1.2.8 +138 silly gitCommit - @strudel/osc@1.3.2 +138 silly gitCommit - @strudel/repl@1.2.9 +138 silly gitCommit - @strudel/soundfonts@1.2.8 +138 silly gitCommit - superdough@1.2.5 +138 silly gitCommit - supradough@1.2.4 +138 silly gitCommit - @strudel/web@1.2.8 +138 silly gitCommit - @strudel/webaudio@1.2.9 +139 verbose git [ +139 verbose git 'commit', +139 verbose git '-F', +139 verbose git '/private/var/folders/hc/yf_zr55547sbcpz5rj7q85sc0000gn/T/3c85b5d4-56ae-40e9-8df7-db2bc1048402/lerna-commit.txt' +139 verbose git ] +140 silly gitTag @strudel/codemirror@1.2.8 git tag %s -m %s +141 verbose git [ +141 verbose git 'tag', +141 verbose git '@strudel/codemirror@1.2.8', +141 verbose git '-m', +141 verbose git '@strudel/codemirror@1.2.8' +141 verbose git ] +142 silly gitTag @strudel/csound@1.2.8 git tag %s -m %s +143 verbose git [ 'tag', '@strudel/csound@1.2.8', '-m', '@strudel/csound@1.2.8' ] +144 silly gitTag @strudel/midi@1.2.8 git tag %s -m %s +145 verbose git [ 'tag', '@strudel/midi@1.2.8', '-m', '@strudel/midi@1.2.8' ] +146 silly gitTag @strudel/osc@1.3.2 git tag %s -m %s +147 verbose git [ 'tag', '@strudel/osc@1.3.2', '-m', '@strudel/osc@1.3.2' ] +148 silly gitTag @strudel/repl@1.2.9 git tag %s -m %s +149 verbose git [ 'tag', '@strudel/repl@1.2.9', '-m', '@strudel/repl@1.2.9' ] +150 silly gitTag @strudel/soundfonts@1.2.8 git tag %s -m %s +151 verbose git [ +151 verbose git 'tag', +151 verbose git '@strudel/soundfonts@1.2.8', +151 verbose git '-m', +151 verbose git '@strudel/soundfonts@1.2.8' +151 verbose git ] +152 silly gitTag superdough@1.2.5 git tag %s -m %s +153 verbose git [ 'tag', 'superdough@1.2.5', '-m', 'superdough@1.2.5' ] +154 silly gitTag supradough@1.2.4 git tag %s -m %s +155 verbose git [ 'tag', 'supradough@1.2.4', '-m', 'supradough@1.2.4' ] +156 silly gitTag @strudel/web@1.2.8 git tag %s -m %s +157 verbose git [ 'tag', '@strudel/web@1.2.8', '-m', '@strudel/web@1.2.8' ] +158 silly gitTag @strudel/webaudio@1.2.9 git tag %s -m %s +159 verbose git [ 'tag', '@strudel/webaudio@1.2.9', '-m', '@strudel/webaudio@1.2.9' ] +160 error Error: Command failed with exit code 128: git tag superdough@1.2.5 -m superdough@1.2.5 +160 error fatal: tag 'superdough@1.2.5' already exists +160 error at makeError (/Users/jaderose/Documents/Github/cstrudel/strudel/node_modules/.pnpm/execa@5.0.0/node_modules/execa/lib/error.js:59:11) +160 error at handlePromise (/Users/jaderose/Documents/Github/cstrudel/strudel/node_modules/.pnpm/execa@5.0.0/node_modules/execa/index.js:114:26) +160 error at process.processTicksAndRejections (node:internal/process/task_queues:105:5) +160 error at async Promise.all (index 6) +160 error at async VersionCommand.gitCommitAndTagVersionForUpdates (/Users/jaderose/Documents/Github/cstrudel/strudel/node_modules/.pnpm/lerna@8.1.9_encoding@0.1.13/node_modules/lerna/dist/index.js:9957:11) +160 error at async VersionCommand.commitAndTagUpdates (/Users/jaderose/Documents/Github/cstrudel/strudel/node_modules/.pnpm/lerna@8.1.9_encoding@0.1.13/node_modules/lerna/dist/index.js:9933:18) +160 error at async Promise.all (index 0) From 17ef84ed708fde6ea4df17aa3a142e79ca119bf4 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 17 Jan 2026 19:11:08 -0800 Subject: [PATCH 120/124] sp --- packages/superdough/helpers.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index 584b19673..07199aff6 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -172,6 +172,7 @@ export const getADSRValues = (params, curve = 'linear', defaultValues) => { if (a == null && d == null && s == null && r == null) { return defaultValues ?? [envmin, envmin, envmax, releaseMin]; } + const sustain = s != null ? s : (a != null && d == null) || (a == null && d == null) ? envmax : envmin; return [Math.max(a ?? 0, envmin), Math.max(d ?? 0, envmin), Math.min(sustain, envmax), Math.max(r ?? 0, releaseMin)]; }; From f610965f4332837febe45743105da170e8b331ed Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 17 Jan 2026 19:12:20 -0800 Subject: [PATCH 121/124] Publish - @strudel/codemirror@1.3.0 - @strudel/csound@1.3.0 - @strudel/midi@1.3.0 - @strudel/repl@1.3.0 - @strudel/soundfonts@1.3.0 - superdough@1.3.0 - @strudel/web@1.3.0 - @strudel/webaudio@1.3.0 --- packages/codemirror/package.json | 2 +- packages/csound/package.json | 2 +- packages/midi/package.json | 2 +- packages/repl/package.json | 2 +- packages/soundfonts/package.json | 2 +- packages/superdough/package.json | 2 +- packages/web/package.json | 2 +- packages/webaudio/package.json | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/codemirror/package.json b/packages/codemirror/package.json index 9ad040976..c8cd4ae73 100644 --- a/packages/codemirror/package.json +++ b/packages/codemirror/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/codemirror", - "version": "1.2.8", + "version": "1.3.0", "description": "Codemirror Extensions for Strudel", "main": "index.mjs", "publishConfig": { diff --git a/packages/csound/package.json b/packages/csound/package.json index 19ba68199..4af015bba 100644 --- a/packages/csound/package.json +++ b/packages/csound/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/csound", - "version": "1.2.8", + "version": "1.3.0", "description": "csound bindings for strudel", "main": "index.mjs", "type": "module", diff --git a/packages/midi/package.json b/packages/midi/package.json index 38d0f0ca9..1881e0246 100644 --- a/packages/midi/package.json +++ b/packages/midi/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/midi", - "version": "1.2.8", + "version": "1.3.0", "description": "Midi API for strudel", "main": "index.mjs", "type": "module", diff --git a/packages/repl/package.json b/packages/repl/package.json index d28034926..1aff2bbe7 100644 --- a/packages/repl/package.json +++ b/packages/repl/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/repl", - "version": "1.2.9", + "version": "1.3.0", "description": "Strudel REPL as a Web Component", "module": "index.mjs", "publishConfig": { diff --git a/packages/soundfonts/package.json b/packages/soundfonts/package.json index 2403adfcf..a904942af 100644 --- a/packages/soundfonts/package.json +++ b/packages/soundfonts/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/soundfonts", - "version": "1.2.8", + "version": "1.3.0", "description": "Soundsfont support for strudel", "main": "index.mjs", "publishConfig": { diff --git a/packages/superdough/package.json b/packages/superdough/package.json index d1e9b45fc..8e2ffad63 100644 --- a/packages/superdough/package.json +++ b/packages/superdough/package.json @@ -1,6 +1,6 @@ { "name": "superdough", - "version": "1.2.5", + "version": "1.3.0", "description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.", "main": "index.mjs", "type": "module", diff --git a/packages/web/package.json b/packages/web/package.json index 8be1f89dc..29c478c55 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/web", - "version": "1.2.8", + "version": "1.3.0", "description": "Easy to setup, opiniated bundle of Strudel for the browser.", "module": "web.mjs", "publishConfig": { diff --git a/packages/webaudio/package.json b/packages/webaudio/package.json index 6b6980e99..48b40470a 100644 --- a/packages/webaudio/package.json +++ b/packages/webaudio/package.json @@ -1,6 +1,6 @@ { "name": "@strudel/webaudio", - "version": "1.2.9", + "version": "1.3.0", "description": "Web Audio helpers for Strudel", "main": "index.mjs", "type": "module", From 6e36bdfff7995fcd93c3b3575b00003f6a60958a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sun, 18 Jan 2026 12:08:12 +0100 Subject: [PATCH 122/124] Add tags for some of the new functions --- packages/core/controls.mjs | 34 ++++++++++++++++++++++++++++++ packages/core/pattern.mjs | 8 ++++++- packages/core/signal.mjs | 3 +++ packages/midi/midi.mjs | 1 + packages/superdough/superdough.mjs | 2 ++ 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 59d9f1aa3..834d963c3 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -702,6 +702,7 @@ export const { * any of the 8 individual FMs (e.g. `fmrel8`) * * @name fmrelease + * @tags fx, superdough, supradough * @synonyms fmrel * @param {number | Pattern} time release time * @@ -1553,6 +1554,7 @@ export const { fanchor } = registerControl('fanchor'); * Rate of the LFO for the lowpass filter * * @name lprate + * @tags fx, superdough * @param {number | Pattern} rate rate in hertz * @example * note("*16").s("sawtooth").lpf(600).lprate("<4 8 2 1>") @@ -1563,6 +1565,7 @@ export const { lprate } = registerControl('lprate'); * Cycle-synced rate of the LFO for the lowpass filter * * @name lpsync + * @tags fx, superdough * @param {number | Pattern} rate rate in cycles * @example * note("*16").s("sawtooth").lpf(600).lpsync("<4 8 2 1>") @@ -1573,6 +1576,7 @@ export const { lpsync } = registerControl('lpsync'); * Depth of the LFO for the lowpass filter * * @name lpdepth + * @tags fx, superdough * @param {number | Pattern} depth depth of modulation * @example * note("*16").s("sawtooth").lpf(600).lpdepth("<1 .5 1.8 0>") @@ -1583,6 +1587,7 @@ export const { lpdepth } = registerControl('lpdepth'); * Depth of the LFO for the lowpass filter, in HZ * * @name lpdepthfrequency + * @tags fx, superdough * @synonyms lpdepthfreq * @param {number | Pattern} depth depth of modulation * @example @@ -1595,6 +1600,7 @@ export const { lpdepthfrequency, lpdepthfreq } = registerControl('lpdepthfrequen * Shape of the LFO for the lowpass filter * * @name lpshape + * @tags fx, superdough * @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..) */ export const { lpshape } = registerControl('lpshape'); @@ -1603,6 +1609,7 @@ export const { lpshape } = registerControl('lpshape'); * DC offset of the LFO for the lowpass filter * * @name lpdc + * @tags fx, superdough * @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar */ export const { lpdc } = registerControl('lpdc'); @@ -1611,6 +1618,7 @@ export const { lpdc } = registerControl('lpdc'); * Skew of the LFO for the lowpass filter * * @name lpskew + * @tags fx, superdough * @param {number | Pattern} skew How much to bend the LFO shape */ export const { lpskew } = registerControl('lpskew'); @@ -1619,6 +1627,7 @@ export const { lpskew } = registerControl('lpskew'); * Rate of the LFO for the bandpass filter * * @name bprate + * @tags fx, superdough * @param {number | Pattern} rate rate in hertz */ export const { bprate } = registerControl('bprate'); @@ -1627,6 +1636,7 @@ export const { bprate } = registerControl('bprate'); * Cycle-synced rate of the LFO for the bandpass filter * * @name bpsync + * @tags fx, superdough * @param {number | Pattern} rate rate in cycles */ export const { bpsync } = registerControl('bpsync'); @@ -1635,6 +1645,7 @@ export const { bpsync } = registerControl('bpsync'); * Depth of the LFO for the bandpass filter * * @name bpdepth + * @tags fx, superdough * @param {number | Pattern} depth depth of modulation */ export const { bpdepth } = registerControl('bpdepth'); @@ -1643,6 +1654,7 @@ export const { bpdepth } = registerControl('bpdepth'); * Depth of the LFO for the bandpass filter, in HZ * * @name bpdepthfrequency + * @tags fx, superdough * @synonyms bpdepthfreq * @param {number | Pattern} depth depth of modulation * @example @@ -1655,6 +1667,7 @@ export const { bpdepthfrequency, bpdepthfreq } = registerControl('bpdepthfrequen * Shape of the LFO for the bandpass filter * * @name bpshape + * @tags fx, superdough * @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..) */ export const { bpshape } = registerControl('bpshape'); @@ -1663,6 +1676,7 @@ export const { bpshape } = registerControl('bpshape'); * DC offset of the LFO for the bandpass filter * * @name bpdc + * @tags fx, superdough * @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar */ export const { bpdc } = registerControl('bpdc'); @@ -1671,6 +1685,7 @@ export const { bpdc } = registerControl('bpdc'); * Skew of the LFO for the bandpass filter * * @name bpskew + * @tags fx, superdough * @param {number | Pattern} skew How much to bend the LFO shape */ export const { bpskew } = registerControl('bpskew'); @@ -1679,6 +1694,7 @@ export const { bpskew } = registerControl('bpskew'); * Rate of the LFO for the highpass filter * * @name hprate + * @tags fx, superdough * @param {number | Pattern} rate rate in hertz */ export const { hprate } = registerControl('hprate'); @@ -1687,6 +1703,7 @@ export const { hprate } = registerControl('hprate'); * Cycle-synced rate of the LFO for the highpass filter * * @name hpsync + * @tags fx, superdough * @param {number | Pattern} rate rate in cycles */ export const { hpsync } = registerControl('hpsync'); @@ -1695,6 +1712,7 @@ export const { hpsync } = registerControl('hpsync'); * Depth of the LFO for the highpass filter * * @name hpdepth + * @tags fx, superdough * @param {number | Pattern} depth depth of modulation */ export const { hpdepth } = registerControl('hpdepth'); @@ -1703,6 +1721,7 @@ export const { hpdepth } = registerControl('hpdepth'); * Depth of the LFO for the hipass filter, in hz * * @name hpdepthfrequency + * @tags fx, superdough * @synonyms hpdepthfreq * @param {number | Pattern} depth depth of modulation * @example @@ -1715,6 +1734,7 @@ export const { hpdepthfrequency, hpdepthfreq } = registerControl('hpdepthfrequen * Shape of the LFO for the highpass filter * * @name hpshape + * @tags fx, superdough * @param {number | Pattern} shape Shape of the lfo (0, 1, 2, ..) */ export const { hpshape } = registerControl('hpshape'); @@ -1723,6 +1743,7 @@ export const { hpshape } = registerControl('hpshape'); * DC offset of the LFO for the highpass filter * * @name hpdc + * @tags fx, superdough * @param {number | Pattern} dcoffset dc offset. set to 0 for unipolar */ export const { hpdc } = registerControl('hpdc'); @@ -1731,6 +1752,7 @@ export const { hpdc } = registerControl('hpdc'); * Skew of the LFO for the highpass filter * * @name hpskew + * @tags fx, superdough * @param {number | Pattern} skew How much to bend the LFO shape */ export const { hpskew } = registerControl('hpskew'); @@ -2182,6 +2204,7 @@ export const { orbit } = registerControl('orbit', 'o'); * otherPat.bmod(..) (to modulate another pattern with the bus) * * @name bus + * @tags fx, superdough * @param {number | Pattern} number */ export const { bus } = registerControl('bus'); @@ -2190,6 +2213,7 @@ export const { bus } = registerControl('bus'); * Postgain multiplier prior to sending the signal to the audio bus. * * @name busgain + * @tags fx, superdough * @synonyms bgain * @param {number | Pattern} number */ @@ -2252,6 +2276,7 @@ export const { voice } = registerControl('voice'); /** * The chord to voice * @name chord + * @tags music_theory * @param {string | Pattern} symbols chord symbols to voice e.g., C, Eb, Fm7, G7. The symbols can be defined via addVoicings * @example * chord("").voicing() @@ -2261,6 +2286,7 @@ export const { chord } = registerControl('chord'); * Which dictionary to use for the voicings. This falls back to the default dictionary if not provided * * @name dictionary + * @tags music_theory * @param {string} dictionaryName which dictionary (having been defined with `addVoicings`) to use * @example * addVoicings('house', { @@ -2275,6 +2301,7 @@ export const { dictionary, dict } = registerControl('dictionary', 'dict'); /** The top note to align the voicing to. Defaults to c5 * * @name anchor + * @tags music_theory * @param {string | Pattern} anchorNote the note to align the voicings to * @example * anchor("").chord("C").voicing() @@ -2284,6 +2311,7 @@ export const { anchor } = registerControl('anchor'); * Sets how the voicing is offset from the anchored position * * @name offset + * @tags music_theory * @param {number | Pattern} shift the amount to shift the voicing up or down * @example * chord("").offset("<0 1 2 3 4 5>") // alter the voicing each time @@ -2293,6 +2321,7 @@ export const { offset } = registerControl('offset'); * How many octaves are voicing steps spread apart, defaults to 1 * * @name octaves + * @tags music_theory * @param {number | Pattern} count the number of octaves * @example * chord("").octaves("<2 4>").voicing() @@ -2302,6 +2331,7 @@ export const { octaves } = registerControl('octaves'); * Remove anchor note from the voicing. Useful for melody harmonization * * @name mode + * @tags music_theory * @param {string | Pattern} modeName one of {below | above | duck | root} * @example * mode("").chord("C").voicing() @@ -3097,6 +3127,7 @@ Pattern.prototype.modulate = function (type, config, idPat) { * a `sometimes`. See example below. * * @name lfo + * @tags fx, superdough * @param {Object} config LFO configuration. * @param {string | Pattern} [config.control] Node to modulate. Aliases: c * @param {string | Pattern} [config.subControl] Sub-control name to append to the control key. Aliases: sc @@ -3150,6 +3181,7 @@ export const lfo = (config) => pure({}).lfo(config); * a `sometimes`. See example below. * * @name env + * @tags fx, superdough * @param {Object} config Envelope configuration. * @param {string | Pattern} [config.control] Node to modulate. Aliases: c * @param {string | Pattern} [config.subControl] Sub-control name to append to the control key. Aliases: sc @@ -3210,6 +3242,7 @@ export const env = (config) => pure({}).env(config); * a `sometimes`. See example below. * * @name bmod + * @tags fx, superdough * @param {Object} config Bus modulation configuration. * @param {string | Pattern} [config.bus] Bus to get modulation signal from * @param {string | Pattern} [config.control] Node to modulate. Aliases: c @@ -3236,6 +3269,7 @@ export const bmod = (config) => pure({}).bmod(config); * and sustains * * @name transient + * @tags fx, superdough * @param {number | Pattern} attack Emphasis on transients; between -1 (deaccentuate) and 1 (accentuate) * @param {number | Pattern} sustain Emphasis on the sustains; between -1 (deaccentuate) and 1 (accentuate) * @example diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index f22b29ff3..2b78a68f0 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1572,7 +1572,10 @@ export function fastcat(...pats) { return result; } -/** See `fastcat` */ +/** See `fastcat` + * @name sequence + * @tags combiners + */ export function sequence(...pats) { return fastcat(...pats); } @@ -2377,6 +2380,7 @@ export const rev = register( * Reverse a whole pattern. See also `rev` for reversing each cycle. * * @name revv + * @tags temporal * @memberof Pattern * @returns Pattern * @example @@ -3903,6 +3907,7 @@ export const phases = (list) => { * calls and/or in a single .FX(fx1, fx2, ..) call. The fx1, .. are _patterns_ which * establish the controls of the given effect. See examples. * @name FX + * @tags fx, superdough * @memberof Pattern * @returns Pattern * @example @@ -3953,6 +3958,7 @@ const _asArrayPattern = (pats) => { * by wrapping them inside a function in K (see example). * * @name K + * @tags generators, fx, superdough * @param {KabelsalatExpression | Function} expr Kabelsalat graph definition * @memberof Pattern * @returns Pattern diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 18090bbd8..9827ddd21 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -286,6 +286,7 @@ export const getRandsAtTime = (t, n = 1, seed = 0) => { * precise RNG, try `useRNG('precise')`. * * @name useRNG + * @tags generators, internals * @param {string} mod - Mode. One of 'legacy', 'precise' * @example * useRNG('legacy') @@ -341,6 +342,7 @@ export const binaryN = (n, nBits = 16) => { * Creates a binary list pattern from a number. * * @name binaryL + * @tags generators * @param {number} n - input number to convert to binary * s("saw").seg(8) * .partials(binaryL(irand(4096).add(1))) @@ -354,6 +356,7 @@ export const binaryL = (n) => { * Creates a binary list pattern from a number, padded to n bits long. * * @name binaryNL + * @tags generators * @param {number} n - input number to convert to binary * @param {number} nBits - pattern length, defaults to 16 */ diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index e03dcbd05..8ce26c2a6 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -577,6 +577,7 @@ export async function midin(input) { * note durations * * @name midikeys + * @tags external_io * @param {string | number} input MIDI device name or index defaulting to 0 * @returns {function((number | Pattern)=): Pattern} A function that produces a pattern. * When queried, the pattern will produces the most recently played midi notes and velocities, diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 45c77d676..bcf235f3c 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -40,6 +40,7 @@ export let maxPolyphony = DEFAULT_MAX_POLYPHONY; * start to die out in first-in-first-out order once the max polyphony has been hit * * @name setMaxPolyphony + * @tags fx, superdough * @param {number} Max polyphony. Defaults to 128 * @example * setMaxPolyphony(4) @@ -73,6 +74,7 @@ export function applyGainCurve(val) { * quadratic, exponential, etc. rather than linear * * @name setGainCurve + * @tags fx, superdough * @param {Function} function to apply to all gain values * @example * setGainCurve((x) => x * x) // quadratic gain From f8750901f0e08d7c7ff43daed5efa04daa47344f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Volhejn?= Date: Sun, 18 Jan 2026 12:16:50 +0100 Subject: [PATCH 123/124] Tag the remaining functions --- packages/core/pattern.mjs | 4 ++++ packages/core/signal.mjs | 9 ++++++++- packages/superdough/util.mjs | 4 +++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 2b78a68f0..b7a047bfc 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1653,6 +1653,7 @@ export const func = curry((a, b) => reify(b).func(a)); /** * Registers a new pattern method. The method is added to the Pattern class + the standalone function is returned from register. * + * @tags functional * @param {string | string[]} name name of the function, or an array of names to be used as synonyms * @param {function} func function with 1 or more params, where last is the current pattern * @param {bool} patternify defaults to true; if set to false, you will have more control over the arguments to `func` as they will be @@ -3843,6 +3844,7 @@ export const chebyshev = _distortWithAlg('chebyshev'); * Turns a list of patterns into a single pattern which outputs list-values * * @name parray + * @tags combiners * @returns Pattern */ export const parray = (pats) => { @@ -3865,6 +3867,7 @@ const _ensureListPattern = (list) => { * Can also be used to create a new synth via `s('user').partials(...)` * * @name partials + * @tags fx, superdough * @param {number[] | Pattern} magnitudes List of [0, 1] magnitudes for partials. 0th entry is the fundamental harmonic (i.e. DC offset is skipped) * @example * s("user").seg(16).n(irand(8)).scale("A:major") @@ -3886,6 +3889,7 @@ export const partials = (list) => { * Rotates the harmonics of one of the core synths ('sine', 'tri', 'saw', 'user', ..) by a list of phases * * @name phases + * @tags fx, superdough * @param {number[] | Pattern} phases List of [0, 1) phases for partials. 0th entry is the fundamental phase (i.e. DC offset is skipped) * @example * // Phase cancellation diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 9827ddd21..2f9fe4b18 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -286,7 +286,7 @@ export const getRandsAtTime = (t, n = 1, seed = 0) => { * precise RNG, try `useRNG('precise')`. * * @name useRNG - * @tags generators, internals + * @tags generators, math * @param {string} mod - Mode. One of 'legacy', 'precise' * @example * useRNG('legacy') @@ -376,6 +376,7 @@ export const binaryNL = (n, nBits = 16) => { * Creates a list of random numbers of the given length * * @name randL + * @tags generators * @param {number} n Number of random numbers to sample * @example * s("saw").seg(16).n(irand(12)).scale("F1:minor") @@ -434,6 +435,7 @@ export const scramble = register('scramble', (n, pat) => { /** * Modify a pattern by applying a function to the `randomSeed` control if present * + * @tags math * @param {Function} func Function from seed (or undefined) to seed (or undefined) * @param {Pattern} pat Pattern to update * @returns Pattern @@ -453,6 +455,7 @@ export const withSeed = (func, pat) => { * that use randomness, like `shuffle` and `sometimes`. * * @name seed + * @tags math * @param {number} n A new seed. Can be any number. * @example * $: s("hh*4").degrade(); @@ -1031,6 +1034,8 @@ export const keyDown = register('keyDown', function (pat) { * event durations, from the pattern that it is combined with. * For example `cyclesPer.struct("1 1 [1 1] 1")` would give the same as `"0.25 0.25 [0.125 0.125] 0.25"`. * See also its reciprocal, `per`, also known as `perCycle`. + * + * @tags temporal * @example * // Shorter events are lower in pitch * sound("saw saw [saw saw] saw") @@ -1049,6 +1054,7 @@ export const cyclesPer = new Pattern(function (state) { * event durations, from the pattern that it is combined with. * For example `per.struct("1 1 [1 1] 1")` would give the same as `"4 4 [8 8] 4"`. * See also its reciprocal, `cyclesPer`. + * @tags temporal * @synonyms perCycle * @example * // Shorter events are more distorted @@ -1066,6 +1072,7 @@ export const perCycle = per; * particular, where the event duration halves, the * returned value increases by one. `perx.struct("1 1 [1 [1 1]] 1")` would therefore be * the same as `"3 3 [4 [5 5]] 3"`. + * @tags temporal */ export const perx = new Pattern(function (state) { const n = Fraction(1).div(state.span.duration); diff --git a/packages/superdough/util.mjs b/packages/superdough/util.mjs index 48830541a..caa58fa80 100644 --- a/packages/superdough/util.mjs +++ b/packages/superdough/util.mjs @@ -110,7 +110,9 @@ export function getCommonSampleInfo(hapValue, bank) { return { transpose, url, index, midi, label }; } -/** Selects entries from `source` and renames them via `map` */ +/** Selects entries from `source` and renames them via `map` + * @tags internals + */ export const pickAndRename = (source, map) => { return Object.fromEntries(Object.entries(map).map(([newKey, oldKey]) => [newKey, source[oldKey]])); }; From 6981638f706edd5ab68144bead3599852b562966 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 18 Jan 2026 22:04:49 +0100 Subject: [PATCH 124/124] fix: move kabelsalat web to superdough --- packages/core/package.json | 1 - packages/core/repl.mjs | 8 -------- packages/superdough/package.json | 1 + packages/superdough/superdough.mjs | 17 +++++++++++++++++ pnpm-lock.yaml | 6 +++--- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index e558c574c..9316ec496 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -31,7 +31,6 @@ }, "homepage": "https://strudel.cc", "dependencies": { - "@kabelsalat/web": "^0.4.1", "fraction.js": "^5.2.1" }, "gitHead": "0e26d4e741500f5bae35b023608f062a794905c2", diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index 58a3532c2..bf86ba494 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -12,7 +12,6 @@ import { import { evalScope } from './evaluate.mjs'; import { register, Pattern, isPattern, silence, stack } from './pattern.mjs'; import { reset_state } from './impure.mjs'; -import { SalatRepl } from '@kabelsalat/web'; export function repl({ defaultOutput, @@ -31,7 +30,6 @@ export function repl({ id, mondo = false, }) { - const kabel = new SalatRepl({ localScope: true }); const state = { schedulerError: undefined, evalError: undefined, @@ -89,11 +87,6 @@ export function repl({ return silence; }; - const compileKabel = (code) => { - const node = kabel.evaluate(code); - return node.compile({ log: false }); - }; - // helper to get a patternified pure value out function unpure(pat) { if (pat._Pattern) { @@ -221,7 +214,6 @@ export function repl({ setcps: setCps, setCpm, setcpm: setCpm, - compileKabel, }); }; diff --git a/packages/superdough/package.json b/packages/superdough/package.json index 8e2ffad63..ce17ab5e5 100644 --- a/packages/superdough/package.json +++ b/packages/superdough/package.json @@ -37,6 +37,7 @@ }, "dependencies": { "@kabelsalat/lib": "^0.4.1", + "@kabelsalat/web": "^0.4.1", "nanostores": "^0.11.3" }, "engines": { diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 529392e99..79da4f916 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -260,6 +260,14 @@ export function loadWorklets() { return workletsLoading; } +let kabel; +async function initKabelsalat() { + const { SalatRepl } = await import('@kabelsalat/web'); + logger('[kabelsalat] ready'); + kabel = new SalatRepl({ localScope: true }); + return kabel; +} + // this function should be called on first user interaction (to avoid console warning) export async function initAudio(options = {}) { const { @@ -306,6 +314,7 @@ export async function initAudio(options = {}) { } catch (err) { console.warn('could not load AudioWorklet effects', err); } + await initKabelsalat(); logger('[superdough] ready'); } let audioReady; @@ -441,6 +450,14 @@ class Chain { } } +const compileKabel = (code) => { + if (!kabel) { + throw new Error('kabelsalat not loaded'); + } + const node = kabel.evaluate(code); + return node.compile({ log: false }); +}; + export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) => { // mapping from main FX and numbered FX chains to nodes const nodes = { main: {} }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 53437f8f5..468ceacf1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -234,9 +234,6 @@ importers: packages/core: dependencies: - '@kabelsalat/web': - specifier: ^0.4.1 - version: 0.4.1 fraction.js: specifier: ^5.2.1 version: 5.2.1 @@ -524,6 +521,9 @@ importers: '@kabelsalat/lib': specifier: ^0.4.1 version: 0.4.1 + '@kabelsalat/web': + specifier: ^0.4.1 + version: 0.4.1 nanostores: specifier: ^0.11.3 version: 0.11.3