Date: 2026-09-21
Today’s music: Digital Shedding by Eqovil, created with Strudel, selected by Jaiden
shedding is a technique that comes from jazz scenes in regards to practice an endeavor in private to improve one’s proficiency and build your own foundations. all of these clips were made by myself using strudel.cc (as well as vcv for some mels) and most have not been heard by anyone aside me (not even the collaborators mentioned).
A chance to practice your live Strudel set.
Do you have a performance name yet?
this section and written by Jaiden
Importing samples into strudel is a good way to get creative with new sounds outside of what comes with the program by default. There are a few different ways to import sounds into strudel, including using files you have on your own device, however this lesson will go over using github and freesound.
Github repositories are an easy way to get a whole pack of sounds from one link. This method uses the following format to import the sounds
samples('github:user/repo')
Here’s an example using sounds from this pack:
samples('github:tidalcycles/Dirt-Samples/master/')
setcpm(120/4)
$: note(run(6)).scale("d:minor").s("hoover").speed(.5).room(2).orbit(2)
$: s("hardkick:1 hit").fast("<2!3 4>").duck(2).duckatt("<.25!3 0.1>")
$: s("hardcore*16").orbit(2)
Some packs will allow you to access the sounds by typing in the name of the folder they’re in, while others will have the sound name in the strudel.json file like the examples below:
samples('github:switchangel/pad')
samples('github:yaxu/clean-breaks')
$: s("swpad:1").fit()
$: s("eeloil").loopAt(2)
Folders will sometimes have multiple sound files in them, which can be accessed using the name of the folder, followed by a colon and the index of the sound you want:
this example uses this folder
samples('github:tidalcycles/dirt-samples')
$: s("baa:<0 1 2 3 4 5>").fit()
The wonderful world of freesound.org has a vast variety of sounds, from thousands of drums and synths, to An old electric oven with a rotisserie spit. Searching for samples by hand like this can be more difficult, but can be rewarding when you find what you’re looking for. There are two ways to import a freesound link:
samples({name: 'url'}) or
samples({name: {key: 'url'}})
Both of these will work fine, but we’ll focus on the first method since specifying the key only changes the pitch of the sound which can be done in other ways as well. The “url” space requires a CDN link which can be found manually using the following steps:

There is also a browser extension made for parsing a CDN link automatically if you want to avoid these steps.
samples({oven: 'https://cdn.freesound.org/previews/866/866304_10586310-lq.mp3'})
$: s("oven").striate(12).clip(1)
$: note("f3 c3 a3 ~").s("sine")
Strudel has functions that are useful when it comes to sampling
samples('github:switchangel/pad')
samples('github:yaxu/clean-breaks')
$: s("swpad:1").fit()
$: s("eeloil").loopAt(2)
samples({j: 'https://cdn.freesound.org/previews/865/865921_12880153-lq.mp3'})
setcpm(90/4)
$: s("j").splice(16, "1 2 3*<1!5 4> [<2!3 4> <4 6>]")
$: s("bd ~ bd [bd*<0 2>]").bank("tr909").fast(2)
setcpm(120/4)
samples('github:yaxu/clean-breaks')
samples('github:kyrsive/ily2-sounds')
$: s("sport").scrub(".75 .5!2 [.1:<1 2>!4]")
$: s("ily_sing_147_Csm").scrub(".28!3 .14 .17").speed(.75)
Other functions I like using to change the way my sample sounds include distort(), crush(), coarse(), speed(), striate(), and slow(). The strudel documentation is a good resource to read into what these effects do
Pieces of paper will be folded and put into a hat with links to different github repositories. Students will pick a piece of paper at random and use the given samples to make something in Strudel to show off in class. Default sounds may also be used in the process, but try to incorporate some of the sounds your pack gives you as well 🐈
The following links will be written to choose from:
Please feel free to ask for help if you’re having trouble importing your sound or if you need some inspiration to start, good luck!
Lesson by Jaiden Kelly, licensed under
CC
BY-ND
4.0
SAMPLES
RESOURCES
NOTES
await initHydra() more infosetcpm(bpm/4)all(x=>x.scope())L5 stands on the shoulders of Processing and p5.js. You will be able to use your previous knowledge to get started right away and learn Lua in the process.
L5 was built to be fun, fast, and resource-light. It is in many cases faster than Processing or p5.js programs. It works cross-platform and on older machines. Lua’s syntax is simpler and constrained compared to JavaScript and Processing-Java. Some things are easier in L5, but some things are not. L5 currently does not have 3d functionality. It only supports one video format (ogg theora). It is a younger and still developing language dialect compared to its older siblings.
In most cases, L5 functions are similar or equivalent to Processing functions. In some cases, p5.js functionality has been targeted or added. For example, like in p5.js you can use HTML/CSS color names or hexadecimal instead of only the default RGB and RGBA values from Processing.
Unlike in Processing (Java) and p5.js (JavaScript), by default all variables in L5 (Lua) have global scope unless specified as local.
function setup()
name = "Nina"
end
function draw()
print(name) --prints out "Nina"
endTo create a variable with block scope, specify
local.
function setup()
local name = "Nina"
end
function draw()
print(name) --prints out "nil"
endTo concatenate variables together or to combine with
other strings, use ..
local name = "Nina"
print("My name is "..name) --prints out "My name is Nina"A comment starts with -- instead of
//
Multi-line comments can be created by surrounding them
with --[[ and ]]--.
The main data structure of Lua is the table. This is used to construct arrays and objects, for example.
wu_tang_clan = {"RZA", "GZA", "Ol' Dirty Bastard", "Inspectah Deck", "Raekwon", "Ghostface Killah", "Method Man", "Masta Killa"}And following from this…
You may have heard that Lua uses 1-based indexing. If you haven’t, well now you’ve heard it.
In other words, the first item in an ordered array is
1 where in many other C-based languages the
first ordered element is 0.
wu_tang_clan = {"RZA", "GZA", "Ol' Dirty Bastard", "Inspectah Deck", "Raekwon", "Ghostface Killah", "Method Man", and "Masta Killa"}
print("First rapper up is "..wu_tang_clan[1])
--prints: First rapper up is RZA.There are some cool advantages in Lua for working with ordered tables.
The # prepending the table name will always
give you its length.
print("There are "..#wu_tang_clan.." members of Wu Tang Clan")This makes it easy to work with tables in a loop.
You can build associative tables, commonly known as a dictionary, map or key-value pairs. You can access their value through specifying a named index, or through dot notation.
local characters = {
["Mario"] = "Plumber",
["Luigi"] = "Plumber",
["Bowser"] = "Monster"
}
print("Mario is a "..characters["Mario"])
--Alternatively, you could write:
print("Mario is a "..characters.Mario)The brackets are optional if your parameter is a single string with no spaces.
local characters = {
mario = "Plumber"
}You can easily add properties to a table, using bracket notation or dot notation.
local museum = {} --initializes the table
museum['location'] = "Los Angeles"
museum.name = "Museum of Jurassic Technology"For loops have simple syntax. By default the iterator increments by 1.
for i=1,5 do
print(i)
endYou can optionally specify to increment or decrement by a different value.
for i=100, 0, -2 do
print(i)
end
-- counts backwards from 100 to 0, decreasing by 2 each iterationIf you want to iterate through a table of values, the
syntax is clear and efficient in Lua since prepending
# to a table’s name gives you its length, and
since tables are ordered beginning at 1, we can count up
from 1 to the table’s length:
print("The Wu Tang Clan, starring:")
for i=1,#wu_tang_clan do
print(wu_tang_clan[i])
endLua also has special functions for working with tables.
ipairs iterates through an ordered table (aka
an array). pairs iterates through an
associative table (aka key-value pairs).
names = {"John","Paul","George","Ringo","Yoko"}
for i,name in ipairs(names) do
print(i,name)
endThis will produce
1 John
2 Paul
3 George
4 Ringo
5 Yoko
You will commonly see _ when you don’t need
to print out or work with the iterator.
names = {"John","Paul","George","Ringo","Yoko"}
print("The Beatles:")
for _,name in ipairs(names) do
print(name)
end
This will produce
The Beatles:
John
Paul
George
Ringo
Yoko
pairs is used to iterate over all key-value
pairs in a table.
local menu = {
breakfast = "yogurt",
lunch = "salad",
dinner = "tacos"
}
for key, value in pairs(menu) do
print("For "..key.." I ate "..value)
endThis will produce
For breakfast I ate yogurt
For dinner I ate tacos
For lunch I ate salad
Note: In an associative table of key-value pairs (rather than an ordered table array), there is no guarantee that the values will be output in a specific order. If you run the above code, you may find the dinner gets printed before lunch even though lunch was listed first in the table, but there are mechanisms to specify an exact order.
Lua uses if, elseif and
else to create conditionals. then
is required after your if and
elseif conditional.
if porridgeTemp > 75 then
print("This bowl of porridge is too hot!")
elseif porridgeTemp < 45 then
print("This bowl of porridge is too cold!")
else
print("This bowl of porridge is just right.")
endUnlike Java and JavaScript, there is no switch statement in Lua.
end rather than
curly brackets { } for specifying scope. Loops
also use do.++ or +=
shortcuts in Lua. To increment a variable by 1 for example,
you write x = x + 1; is not needed at the end of
lines| Lua | JavaScript | Java |
|---|---|---|
function func() ... end |
function func() {...} |
void func() {...} |
true and false |
true and false |
true and false |
a and b (logical AND) |
a && b |
a && b |
a or b (logical OR) |
a || b |
a || b |
not a |
!a |
!a |
a ~= b (not-equal) |
a != b |
a != b |
i = i + 1 (increment) |
i++ |
i++ |
i = i - 1 (decrement) |
i-- |
i-- |
a <= b and b < c |
a <= b && b < c |
a <= b && b < c |
for i=1, limit do ... end |
for (let i=0; i<limit; i++) {...} |
for (int i = 0; i < limit; i++) {...} |
for i=start, limit, step do ... end |
for (let i=start; i<limit; i+=step) {...} |
for (int i = start; i < limit; i += step) {...} |
for _, b in ipairs(table_of_balls) do ... end |
for (let b of arrayOfBalls) {...} |
for (Ball b : arrayOfBalls) {...} |
local myColor = '#FFCC00' |
let myColor = '#FFCC00' |
String myColor = "#FFCC00"; |
OOP is implemented in Lua using tables. For more information see the Using Objects and Data Structure Garden tutorials and Random Robot Objects example.
You are not required to follow a theme, but if you are struggling for an idea here are some ideas to get you started:
Important: No usage of LLMs. Include a screenshot and a
.love file in the submission. Using or porting
existing code is allowed, but if you borrow from elsewhere,
check its license, and be sure to give credit.