Compare commits

..

10 Commits

Author SHA1 Message Date
alex 24ac74e0ca make cpm a global control (like cps), and deal with it in cyclist 2026-11-01 11:25:06 +00:00
yaxu fdaa559377 Update README.md 2026-10-26 15:13:52 +01:00
Alex McLean 064cf07e26 Merge pull request 'Minor edit: change "human" to "person"' (#2162) from gamar3is/strudel:main into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/2162
2026-10-25 11:02:53 +01:00
gamar3is d5362c208c Minor edit: change "human" to "person"
The use of the term "humans" to refer to people reinforces the anthropomorphisation of LLM's, suggesting they are specific entities instead of tools in a strange sci-fi way. Replacing "humans" with "people" can address this.
2026-10-25 10:56:50 +01:00
Alex McLean 7dd20fee57 Merge pull request 'humane dev policy' (#2158) from post-ai into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/2158
2026-10-24 00:45:07 +02:00
Alex McLean 5ab79f2c6f Merge pull request 'gitignore .vscode except for settings.json' (#2160) from tyow/localstrudel:gitignore-vscode into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/2160
2026-10-24 00:37:55 +02:00
tyow 67ce7c8ad6 gitignore .vscode except for settings.json 2026-10-23 17:30:15 -04:00
Alex McLean e00b6f6216 Merge pull request 'Update contributors in 'getting started' page' (#2157) from contributors into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/2157
2026-10-19 21:22:53 +02:00
yaxu 37097a92af Update website/src/pages/learn/getting-started.mdx 2026-10-18 15:22:41 +02:00
yaxu 669217ee7d Update website/src/pages/learn/getting-started.mdx 2026-10-18 13:02:53 +02:00
9 changed files with 104 additions and 50 deletions
+3
View File
@@ -131,3 +131,6 @@ fabric.properties
samples/*
!samples/README.md
.idea/
.vscode
!settings.json
+1 -1
View File
@@ -37,5 +37,5 @@ All community members should be treated with respect and kindness. We welcome co
* LLM-generated text (e.g. commit messages, issue comments, forum posts etc) generated by LLMs will be flagged, and ultimately deleted.
* We encourage asking each other questions and working through problems to promote collective knowledge and creative ideas (never telling each other to "just google it").
* We strongly discourage others from forking/importing our projects into corporate AI platforms, including Microsoft Github.
* All contributions must be submitted by a human who understands the changes, is happy to answer questions about them, and where possible, help maintain the code in the future.
* All contributions must be submitted by a person who understands the changes, is happy to answer questions about them, and where possible, help maintain the code in the future.
* In community discussion channels (at the time of writing, this refers to the uzu discord server), all discussion (including criticism) of LLM technology and use should be kept to designated channels.
+3 -3
View File
@@ -43,10 +43,10 @@ There are many ways to contribute to this project! See [contribution guide](./CO
## Community
There is a #strudel channel on the TidalCycles discord: <https://discord.com/invite/HGEdXmRkzT>
Strudel is a member of the 'Uzu' family of pattern-based live coding languages, along with TidalCycles and friends.
You can also ask questions and find related discussions on the tidal club forum: <https://club.tidalcycles.org/>
There are a range of strudel channels on the Uzu discord: <https://discord.com/invite/HGEdXmRkzT>
The discord and forum is shared with the haskell (tidal) and python (vortex) siblings of this project.
You can also ask questions and find related discussions on the Uzu web forum: <https://uzu.lurk.org/>
We also have a mastodon account: <a rel="me" href="https://social.toplap.org/@strudel">social.toplap.org/@strudel</a>
+25
View File
@@ -2702,7 +2702,32 @@ export const { seconds } = registerControl('seconds');
export const { songPtr } = registerControl('songPtr');
export const { uid } = registerControl('uid');
export const { val } = registerControl('val');
/**
* Plays the pattern at the given cycles per second. The cps can be patterned. This changes the global tempo, so it generally
* makes sense to have either `cps` (or `cpm` - cycles per minute) set by only one of your patterns.
* Note that this currently does not work when the 'Sync across Browser Tabs / Windows' setting is switched on.
* @tags temporal
* @example
* s("bd hh sd hh").cps(0.5) // = 1 cycle every 2 seconds
* @example
* // a smooth change over 4 cycles
* s("bd hh sd hh").cps(sine.slow(4).range(0.5,1.5))
*/
export const { cps } = registerControl('cps');
/**
* Plays the pattern at the given cycles per minute. The cpm can be patterned. This changes the global tempo, so it generally
* makes sense to have either `cpm` (or `cps` - cycles per second) set by only one of your patterns.
* Note that this currently does not work when the 'Sync across Browser Tabs / Windows' setting is switched on.
* @tags temporal
* @example
* s("bd hh sd hh").cpm(90) // = feels like 180 bpm
* // a smooth change over 4 cycles
* s("bd hh sd hh").cpm(sine.slow(4).range(30,70))
*/
export const { cpm } = registerControl('cpm');
/**
* Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration.
*
+8 -6
View File
@@ -84,10 +84,10 @@ export class Cyclist {
// cps changes first, so the durations of the simultaneous haps
// are adjusted accordingly.
if (aBegin.eq(bBegin)) {
if (a.value.cps !== undefined) {
return b.value.cps !== undefined ? 0 : -1;
if (a.value.cps !== undefined || a.value.cpm !== undefined) {
return b.value.cps !== undefined || b.value.cpm !== undefined ? 0 : -1;
}
if (b.value.cps !== undefined) {
if (b.value.cps !== undefined || b.value.cpm !== undefined) {
return 1;
}
return 0;
@@ -97,6 +97,8 @@ export class Cyclist {
haps.forEach((hap) => {
const targetTime = (hap.whole.begin - this.checkpoint.cycles) / this.cps + this.checkpoint.seconds;
let hapCps = hap.value.cps;
hapCps ??= hap.value.cpm != null ? hap.value.cpm / 60 : undefined;
if (tickEndTime < targetTime) {
// If the targetTime exceeds the tickEndTime, this means
// the cps has slowed down, and the latter haps from the query
@@ -104,10 +106,10 @@ export class Cyclist {
// They'll be scheduled during the next tick.
return;
}
onSchedule?.(hap, targetTime + this.latency, hap.value.cps ?? this.cps);
if (hap.value.cps !== undefined && this.cps != hap.value.cps) {
onSchedule?.(hap, targetTime + this.latency, hapCps ?? this.cps);
if (hapCps !== undefined && this.cps != hapCps) {
cpsChanged = true;
this.cps = hap.value.cps;
this.cps = hapCps;
this.num_ticks_since_checkpoint = 0;
this.checkpoint = {
cycles: hap.whole.begin,
-12
View File
@@ -2476,18 +2476,6 @@ export const apply = register('apply', function (func, pat) {
return func(pat);
});
/**
* Plays the pattern at the given cycles per minute.
* @tags temporal
* @deprecated
* @example
* s("<bd sd>,hh*2").cpm(90) // = 90 bpm
*/
// this is redefined in repl.mjs, using the current cps as divisor
export const cpm = register('cpm', function (cpm, pat) {
return pat._fast(cpm / 60 / 1);
});
/**
* Nudge a pattern to start earlier in time. Equivalent of Tidal's <~ operator
*
-4
View File
@@ -217,14 +217,10 @@ export function repl({
} catch (err) {
console.warn('injectPatternMethods: error:', err);
}
const cpm = register('cpm', function (cpm, pat) {
return pat._fast(cpm / 60 / scheduler.cps);
});
return evalScope({
all,
each,
hush,
cpm,
setCps,
setcps: setCps,
setCpm,
+63 -23
View File
@@ -1,5 +1,68 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`runs examples > example "" example index 0 1`] = `
[
"[ 0/1 → 1/4 | s:bd cps:0.5 ]",
"[ 1/4 → 1/2 | s:hh cps:0.5 ]",
"[ 1/2 → 3/4 | s:sd cps:0.5 ]",
"[ 3/4 → 1/1 | s:hh cps:0.5 ]",
"[ 1/1 → 5/4 | s:bd cps:0.5 ]",
"[ 5/4 → 3/2 | s:hh cps:0.5 ]",
"[ 3/2 → 7/4 | s:sd cps:0.5 ]",
"[ 7/4 → 2/1 | s:hh cps:0.5 ]",
"[ 2/1 → 9/4 | s:bd cps:0.5 ]",
"[ 9/4 → 5/2 | s:hh cps:0.5 ]",
"[ 5/2 → 11/4 | s:sd cps:0.5 ]",
"[ 11/4 → 3/1 | s:hh cps:0.5 ]",
"[ 3/1 → 13/4 | s:bd cps:0.5 ]",
"[ 13/4 → 7/2 | s:hh cps:0.5 ]",
"[ 7/2 → 15/4 | s:sd cps:0.5 ]",
"[ 15/4 → 4/1 | s:hh cps:0.5 ]",
]
`;
exports[`runs examples > example "" example index 0 2`] = `
[
"[ 0/1 → 1/4 | s:bd cpm:50 ]",
"[ 1/4 → 1/2 | s:hh cpm:57.6536686473018 ]",
"[ 1/2 → 3/4 | s:sd cpm:64.14213562373095 ]",
"[ 3/4 → 1/1 | s:hh cpm:68.47759065022574 ]",
"[ 1/1 → 5/4 | s:bd cpm:70 ]",
"[ 5/4 → 3/2 | s:hh cpm:68.47759065022574 ]",
"[ 3/2 → 7/4 | s:sd cpm:64.14213562373095 ]",
"[ 7/4 → 2/1 | s:hh cpm:57.6536686473018 ]",
"[ 2/1 → 9/4 | s:bd cpm:50 ]",
"[ 9/4 → 5/2 | s:hh cpm:42.34633135269821 ]",
"[ 5/2 → 11/4 | s:sd cpm:35.85786437626905 ]",
"[ 11/4 → 3/1 | s:hh cpm:31.52240934977427 ]",
"[ 3/1 → 13/4 | s:bd cpm:30 ]",
"[ 13/4 → 7/2 | s:hh cpm:31.522409349774268 ]",
"[ 7/2 → 15/4 | s:sd cpm:35.85786437626905 ]",
"[ 15/4 → 4/1 | s:hh cpm:42.34633135269819 ]",
]
`;
exports[`runs examples > example "" example index 1 1`] = `
[
"[ 0/1 → 1/4 | s:bd cps:1 ]",
"[ 1/4 → 1/2 | s:hh cps:1.1913417161825448 ]",
"[ 1/2 → 3/4 | s:sd cps:1.3535533905932737 ]",
"[ 3/4 → 1/1 | s:hh cps:1.4619397662556435 ]",
"[ 1/1 → 5/4 | s:bd cps:1.5 ]",
"[ 5/4 → 3/2 | s:hh cps:1.4619397662556435 ]",
"[ 3/2 → 7/4 | s:sd cps:1.3535533905932737 ]",
"[ 7/4 → 2/1 | s:hh cps:1.1913417161825448 ]",
"[ 2/1 → 9/4 | s:bd cps:1 ]",
"[ 9/4 → 5/2 | s:hh cps:0.8086582838174552 ]",
"[ 5/2 → 11/4 | s:sd cps:0.6464466094067263 ]",
"[ 11/4 → 3/1 | s:hh cps:0.5380602337443567 ]",
"[ 3/1 → 13/4 | s:bd cps:0.5 ]",
"[ 13/4 → 7/2 | s:hh cps:0.5380602337443567 ]",
"[ 7/2 → 15/4 | s:sd cps:0.6464466094067262 ]",
"[ 15/4 → 4/1 | s:hh cps:0.8086582838174547 ]",
]
`;
exports[`runs examples > example "_euclidRot" example index 0 1`] = `
[
"[ 0/1 → 1/5 | note:c3 ]",
@@ -2789,29 +2852,6 @@ exports[`runs examples > example "cosine" example index 0 1`] = `
]
`;
exports[`runs examples > example "cpm" example index 0 1`] = `
[
"[ 0/1 → 1/3 | s:hh ]",
"[ 0/1 → 2/3 | s:bd ]",
"[ 1/3 → 2/3 | s:hh ]",
"[ 2/3 → 1/1 | s:hh ]",
"[ 2/3 → 4/3 | s:sd ]",
"[ 1/1 → 4/3 | s:hh ]",
"[ 4/3 → 5/3 | s:hh ]",
"[ 4/3 → 2/1 | s:bd ]",
"[ 5/3 → 2/1 | s:hh ]",
"[ 2/1 → 7/3 | s:hh ]",
"[ 2/1 → 8/3 | s:sd ]",
"[ 7/3 → 8/3 | s:hh ]",
"[ 8/3 → 3/1 | s:hh ]",
"[ 8/3 → 10/3 | s:bd ]",
"[ 3/1 → 10/3 | s:hh ]",
"[ 10/3 → 11/3 | s:hh ]",
"[ 10/3 → 4/1 | s:sd ]",
"[ 11/3 → 4/1 | s:hh ]",
]
`;
exports[`runs examples > example "crush" example index 0 1`] = `
[
"[ 0/1 → 1/6 | s:hh crush:16 ]",
+1 -1
View File
@@ -14,7 +14,7 @@ These pages will introduce you to [Strudel](https://strudel.cc/), a web-based [l
# What is Strudel?
[Strudel](https://strudel.cc/) is a version of [Tidal Cycles](https://tidalcycles.org) written in [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), initiated by [Alex McLean](https://slab.org) and [Felix Roos](https://froos.cc/) in 2022.
[Strudel](https://strudel.cc/) is a version of [Tidal Cycles](https://tidalcycles.org) written in [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), initiated by [Alex McLean](https://slab.org) and [Felix Roos](https://froos.cc/) in 2022, quickly growing into a collective free/open source project with major [contributors and maintainers](https://codeberg.org/uzu/strudel/activity/contributors) including [Jade Rowland](https://www.instagram.com/_switch_angel/) and [Aria](https://www.instagram.com/gglossingg/).
Tidal Cycles, also known as Tidal, is a language for [algorithmic pattern](https://algorithmicpattern.org), and though it is most commonly used for [making music](https://tidalcycles.org/docs/showcase), it can be used for any kind of pattern making activity, including [weaving](https://www.youtube.com/watch?v=TfEmEsusXjU).
Tidal was first implemented as a library written in the [Haskell](https://www.haskell.org/) functional programming language, and by itself it does not make any sound.