From 65dd79e374ce791bf67c7de217b45c10de20f7ff Mon Sep 17 00:00:00 2001 From: "Lu[ke] Wilson" Date: Sat, 29 Nov 2025 17:44:33 +0000 Subject: [PATCH 01/40] add lots of synonyms --- packages/core/controls.mjs | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 21a6d9624..0f9ba4d84 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -465,6 +465,7 @@ export const { fmi, fm } = registerControl(['fmi', 'fmh'], 'fm'); * * @name fmenv * @param {number | Pattern} type lin | exp + * @synonyms fme * @example * note("c e g b g e") * .fm(4) @@ -474,12 +475,13 @@ export const { fmi, fm } = registerControl(['fmi', 'fmh'], 'fm'); * ._scope() * */ -export const { fmenv } = registerControl('fmenv'); +export const { fmenv } = registerControl('fmenv', 'fme'); /** * Attack time for the FM envelope: time it takes to reach maximum modulation * * @name fmattack * @param {number | Pattern} time attack time + * @synonyms fmatt * @example * note("c e g b g e") * .fm(4) @@ -487,7 +489,7 @@ export const { fmenv } = registerControl('fmenv'); * ._scope() * */ -export const { fmattack } = registerControl('fmattack'); +export const { fmattack } = registerControl('fmattack', 'fmatt'); /** * Waveform of the fm modulator @@ -507,6 +509,7 @@ export const { fmwave } = registerControl('fmwave'); * * @name fmdecay * @param {number | Pattern} time decay time + * @synonyms fmdec * @example * note("c e g b g e") * .fm(4) @@ -515,12 +518,13 @@ export const { fmwave } = registerControl('fmwave'); * ._scope() * */ -export const { fmdecay } = registerControl('fmdecay'); +export const { fmdecay } = registerControl('fmdecay', 'fmdec'); /** * Sustain level for the FM envelope: how much modulation is applied after the decay phase * * @name fmsustain * @param {number | Pattern} level sustain level + * @synonyms fmsus * @example * note("c e g b g e") * .fm(4) @@ -529,10 +533,10 @@ export const { fmdecay } = registerControl('fmdecay'); * ._scope() * */ -export const { fmsustain } = registerControl('fmsustain'); +export const { fmsustain } = registerControl('fmsustain', 'fmsus'); // these are not really useful... skipping for now -export const { fmrelease } = registerControl('fmrelease'); -export const { fmvelocity } = registerControl('fmvelocity'); +export const { fmrelease } = registerControl('fmrelease', 'fmrel'); +export const { fmvelocity } = registerControl('fmvelocity', 'fmvel'); /** * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`. @@ -862,7 +866,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 - * @synonyms duckatt + * @synonyms duckatt, datt * * @param {number | Pattern} time The attack time in seconds * @example @@ -874,20 +878,20 @@ export const { duckonset } = registerControl('duckonset', 'duckons'); * ducker: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit("2:5").duckattack("0.4:0.1") * */ -export const { duckattack } = registerControl('duckattack', 'duckatt'); +export const { duckattack } = registerControl('duckattack', 'duckatt', 'datt'); /** * Create byte beats with custom expressions * * @name byteBeatExpression - * @synonyms bbexpr + * @synonyms bbexpr, bb * * @param {number | Pattern} byteBeatExpression bitwise expression for creating bytebeat * @example * s("bytebeat").bbexpr('t*(t>>15^t>>66)') * */ -export const { byteBeatExpression, bbexpr } = registerControl('byteBeatExpression', 'bbexpr'); +export const { byteBeatExpression, bbexpr } = registerControl('byteBeatExpression', 'bbexpr', 'bb'); /** * Create byte beats with custom expressions @@ -931,24 +935,26 @@ export const { pw } = registerControl(['pw', 'pwrate', 'pwsweep']); * Controls the lfo rate for the pulsewidth of the pulse oscillator * * @name pwrate + * @synonyms pwr * @param {number | Pattern} rate * @example * n(run(8)).scale("D:pentatonic").s("pulse").pw("0.5").pwrate("<5 .1 25>").pwsweep("<0.3 .8>") * */ -export const { pwrate } = registerControl('pwrate'); +export const { pwrate } = registerControl('pwrate', 'pwr'); /** * Controls the lfo sweep for the pulsewidth of the pulse oscillator * * @name pwsweep + * @synonyms pws * @param {number | Pattern} sweep * @example * n(run(8)).scale("D:pentatonic").s("pulse").pw("0.5").pwrate("<5 .1 25>").pwsweep("<0.3 .8>") * */ -export const { pwsweep } = registerControl('pwsweep'); +export const { pwsweep } = registerControl('pwsweep', 'pws'); /** * Phaser audio effect that approximates popular guitar pedals. @@ -1615,12 +1621,12 @@ export const { delaytime, delayt, dt } = registerControl('delaytime', 'delayt', * * @name delaysync * @param {number | Pattern} cycles delay length in cycles - * @synonyms delayt, dt + * @synonyms delays, ds * @example * s("bd bd").delay(.25).delaysync("<1 2 3 5>".div(8)) * */ -export const { delaysync } = registerControl('delaysync'); +export const { delaysync } = registerControl('delaysync', 'delays', 'ds'); /** * Specifies whether delaytime is calculated relative to cps. From 6981638f706edd5ab68144bead3599852b562966 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 18 Jan 2026 22:04:49 +0100 Subject: [PATCH 02/40] 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 From 62bf9abed336c4638bc5a2f760e74ec1b2f942e1 Mon Sep 17 00:00:00 2001 From: Aria Date: Mon, 19 Jan 2026 09:52:49 -0800 Subject: [PATCH 03/40] Add retrig option for LFOs --- packages/core/controls.mjs | 4 +++- packages/superdough/modulators.mjs | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 834d963c3..a5a1c0a12 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -3046,6 +3046,7 @@ registerSubControls('lfo', [ ['skew', 'sk'], ['curve', 'cu'], ['sync', 's'], + ['retrig', 'rt'], ['fxi'], ]); registerSubControls('env', [ @@ -3132,13 +3133,14 @@ Pattern.prototype.modulate = function (type, config, idPat) { * @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 * @param {number | Pattern} [config.rate] Modulation rate. Aliases: r + * @param {number | Pattern} [config.sync] Tempo-synced modulation rate. Aliases: s * @param {number | Pattern} [config.depth] Relative modulation depth. Aliases: dep, dr * @param {number | Pattern} [config.depthabs] Absolute modulation depth. Aliases: da * @param {number | Pattern} [config.dcoffset] DC offset / bias for the waveform. Aliases: dc * @param {number | Pattern} [config.shape] Shape index. Aliases: sh * @param {number | Pattern} [config.skew] Skew amount. Aliases: sk * @param {number | Pattern} [config.curve] Exponential curve amount. Aliases: cu - * @param {number | Pattern} [config.sync] Tempo-synced modulation rate. Aliases: s + * @param {number | Pattern} [config.retrig] If > 0.5, the LFO will retrigger on each event. Aliases: rt * @param {number | Pattern} [config.fxi] FX index to target * @param {string | Pattern} id ID to use for this modulator * @returns Pattern diff --git a/packages/superdough/modulators.mjs b/packages/superdough/modulators.mjs index 05674baa0..76fc4c7fd 100644 --- a/packages/superdough/modulators.mjs +++ b/packages/superdough/modulators.mjs @@ -98,6 +98,7 @@ export const connectLFO = (id, params, nodeTracker) => { fxi = 'main', depth = 1, depthabs, + retrig = 0, ...filteredParams } = params; const { targetParams, paramName } = getTargetParamsForControl(control, nodeTracker[fxi], subControl); @@ -109,7 +110,7 @@ export const connectLFO = (id, params, nodeTracker) => { const modParams = { ...filteredParams, frequency: sync !== undefined ? sync * cps : rate, - time: cycle / cps, + time: retrig > 0.5 ? 0 : cycle / cps, depth: depthValue, min, max, From 38b7bcb3d288562424e797f0ccc41111fb5a3773 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 19 Jan 2026 23:00:14 +0100 Subject: [PATCH 04/40] style tweaks --- .../repl/components/button/action-button.jsx | 9 +-- .../components/incrementor/Incrementor.jsx | 2 +- .../components/panel/AudioDeviceSelector.jsx | 4 +- .../panel/AudioEngineTargetSelector.jsx | 6 +- .../src/repl/components/panel/ExportTab.jsx | 7 +-- website/src/repl/components/panel/Forms.jsx | 2 +- .../src/repl/components/panel/PatternsTab.jsx | 2 +- .../src/repl/components/panel/Reference.jsx | 2 +- .../src/repl/components/panel/SelectInput.jsx | 20 ------- .../src/repl/components/panel/SettingsTab.jsx | 57 +++++++++++++++---- .../src/repl/components/panel/SoundsTab.jsx | 2 +- .../src/repl/components/textbox/Textbox.jsx | 14 ----- 12 files changed, 62 insertions(+), 65 deletions(-) delete mode 100644 website/src/repl/components/panel/SelectInput.jsx delete mode 100644 website/src/repl/components/textbox/Textbox.jsx diff --git a/website/src/repl/components/button/action-button.jsx b/website/src/repl/components/button/action-button.jsx index de674b696..846d36c7e 100644 --- a/website/src/repl/components/button/action-button.jsx +++ b/website/src/repl/components/button/action-button.jsx @@ -13,16 +13,13 @@ export function SpecialActionButton(props) { const { className, ...buttonProps } = props; return ( - + ); } export function ActionInput({ label, className, ...props }) { return ( -