(norns/circle/01) drone in three worlds

Thanks for going to the trouble of digging up your replies. I’ll try searching more first before I ask another question! Also, amazing work with softcut. It’s a real joy to work with.

yr most welcome.

also, i think you just want mode_number = (mode_number %3) + 1 - you were incrementing twice.

break down

x        -- in range [1, 3]
+ 1      -- maps to range [2, 4]
- 1      -- you need this to get back in range [1, 3] before taking modulo!
% 3      -- maps [1, 3] -> [0, 2]
+ 1      -- now back to  [1, 3]

just under a month to go!

curious to hear how people are doing— have the softcut studies been clear?

any questions we can answer? anyone need suggestions?

1 Like

I haven’t even started yet :grimacing: but I plan on getting things going tonight!

1 Like

me: if I have 4-5 hours to spend on Norns scripting I’ve got a ton of things to do to Kria and at least two other scripts that need to be finished and published

also me:

(the three worlds sounds a bit similar in that video but they aren’t - and they all bear quite long term listening - I’ve found myself just leaving it on)

I’m way too lazy to have gone through and curated loops etc - all three worlds are aleatoric systems that evolve over time - all seem kind of slow to respond to the controls in that video since they are designed to be slow moving but they have a strong influence over time!

It’s been a fantastic way of learning softcut!

Need to fix a stupid bug in the world switching and make the visualisations now


Was enjoying working on it until i stupidly broke my norns and had to ship it off for repair - i’m an idiot.

I referred a lot to the softcut studies and to scripts like MLR and Cranes/Cheat codes - i’d say these were more useful resources than the API documentation as i work better with seeing real use-cases. Will be super curious to dig into the repo when everyone pushes up their scripts!

1 Like

I think in general the API docs can be kind of mysterious until you’ve seen an example. I still ended up digging around in other peoples source code (but FULL marks to maiden for making that trivial - it’s a lovely environment to mess about in for the Lua side).

Essentially the studies are a good introduction but as soon as you want to go a bit deeper the docs are a little brief. On the other hand it’s so easy to look at source and examples it’s not too bad

(I probably should add that compared to an awful lot of things I’ve worked on over the years it is absolute luxury in terms of documentation. :slight_smile: )

1 Like

Here’s a video I just recorded on my phone. I think my first attempt is done.
A huge thanks to @tehn, @zebra, @neauoire, and anyone else who has contributed to the tutorials.

I figured out some basic approaches/strategies I want to take in the future – for instance, making parameters that are adjusted by encoders and used for graphics scaled from 0. - 1., and then mapping them separately for the sonic and the animated elements.

I am curious – I read somewhere about making all variables and functions local to avoid messing with system vars. I’ve also noticed this in a lot of the scripts that are already out there. Should I just make everything that I declare local? And for instance, is local scope at the top level accessible from functions that are declared/nested below it? Sorry if these terms aren’t making sense; here’s an example:

local billy = 10
local function addBilly(arg)
    local foo = arg + billy
    return foo

Is that all copacetic? Can I add billy or do I need to keep that var global? Thank youuuu

Edit: also, do I need to do anything in particular to get my script to go into Tape? I’m not seeing levels when I’m making sound. maybe this is a DIY shield thing, or a config thing?



maybe worth noting that i did invest a little time in making the softcut API doc comments slightly more verbose in latest update / master branch.

but yea it does need a manual. there are a number of little caveats and gotchas.

for example @andrew recently ran into (i think) the hard limit on rate, which is a ratio of +/- 64 (or 6 octaves.) exceeding this will crash the audio process! (woops.) (fix in flight)


so in general it is a good idea to keep scope in mind and make things local when they can be. in particular it’s a really good idea to keep vars in functions local.

but actually @ppqq and @tehn made a really nice change a while back which is to swap out the global environment between scripts. this means you can absolutely declare globals with impunity in scripts, and in fact this is now recommended practice for top-level variables. (it is less verbose and makes it so you can access those vars in the REPL.)

you still do need to watch out for overriding fields in system globals. there’s not really a feasible way for us to prevent you from, say, breaking the menu by saying menu.redraw = nil or something.

i believe that in the present version of lua you can always access locals declared in surrounding scopes. (in older versions there were weird edge cases with nested functions.)

oh but @wheelersounds i guess i should say that in response to that example (and i know it’s not real, so this is just a point for discussion/pedantry), IMHO it’s much better practice to make functions that don’t rely on any implicit knowledge of state outside their scope. so in that case, to just pass billy as a function parameter.

in the extreme case, you can make all your functions immutable (they don’t modify anything and just return whatever mutated state you need), then you’re doing Functional programming. (lua helps you with this because you can’t pass parameters by reference, and functions can return multiple values and/or tables.)

--- @returns new billy
function addBillly(arg, oldBilly) return oldBilly + arg end

or if you find yourself wanting to mutate external state a lot, without copying big tables on return, then you can make functions into “methods” that operate on specific data structures. then you’re doing Object Oriented programming. (lua helps with this by providing the “colon” syntax that hides a “self” argument)

Thing = { billy = 10 } 
--- (... insert boilerplate: Thing factory, &c)
--- mutates the `billy` of a Thing
function Thing:addBilly(arg) self.billy = self.billy + arg end

those programming patterns arose because things become pretty difficult to manage without them.

no, it should just work. but, what levels? TAPE doesn’t have a dedicated level meter since it just takes the main output bus. you mean you don’t see the output levels you’re hearing? and/or, you get silence in the recording even though output is audible?

anyone who hasn’t yet might like to check out the routing diagrams


yeah i noticed just after writing the above, super appreciate that - especially knowing now what are 1-indexed etc

Thank you again for being so helpful.

I realized that the TAPE output was all the way down in the mixer. I cranked it and now I can listen to everything!

deadline is march 1 (let’s say end-of-day) which is next sunday!


  • saturday feb 22 1-3pm EST
  • saturday feb 29 time TBA

basically i’ll be here working on my script, but fully available to help debug and answer questions in realtime here in the thread. if anyone wants to livestream during this time it could be fun to peek in on progress.

but in the meantime, please post questions and really hoping to see some creations.

and remember that keeping it simple is totally perfect! :star:


tomorrow 1-3pm EST!


truth be told, i haven’t had time to work on this at all until now, but i’ve been thinking about it.

here’s my approach:

  • i’m going to limit myself to 3 softcut voices… so 3 loops playing at once.
  • i’d like to curate the foundation, so i’ll start by identifying which parts of the sound to combine… so i’ll throw together a temporary interface for scrubbing loop positions… or perhaps skip that and just type values into the maiden REPL
  • aha, and i’ll figure out initial rates (which will translate as pitch transposition) for each loop

and then do this three times. once for each “world” — haven’t decided if i’ll mix source materials (the files included) or do one file per world/drone

technical questions welcome…


ok. first probably helpful to know the file length, so put this after the file load:


and then whipped together a simple interface for auditioning loop points:

if you want to see how i did it...
sc = softcut -- typing shortcut

function init()
  file = _path.code .. "nc01-drone/lib/dd.wav"

  for i=1,3 do





edit = 1
level = {1,1,1}
lst = {1,2,3}
lend = {2,3,4}
scrub = 1

function enc(n,d)
  if n==1 then
    level[edit] = util.clamp(level[edit] + d/10,0,1)
    sc.level(edit, level[edit])
  elseif n==2 then
    lst[edit] = lst[edit] + d*scrub
    sc.loop_start(edit, lst[edit])
    print(edit .. " s " .. lst[edit])
  elseif n==3 then
    lend[edit] = lend[edit] + d*scrub
    sc.loop_end(edit, lend[edit])
    print(edit .. " e " .. lend[edit])

function key(n,z)
  if n==3 and z==1 then
    edit = edit % 3 + 1
  elseif n==2 and z==1 then
    scrub = scrub * 10
    if scrub == 10 then scrub = 0.01 end

function redraw()

audio.file_info() returns ch, samples, sample rate so for this to be useful:

ch,length,sr = audio.file_info(file)
length = length/sr

and i added more util.clamp onto the loop points so they don’t cross, and print out the region so i can later hardcode in those values.

helpful hint: just go use a wave editor to find your loops! instead of scripting a loop finder for 30 minutes…


sounding good… got some numbers

pushed my code up to the repo in case anyone is interested.

ps. hi @justmat!


ok— hope some of you had success scripting today— and i’ll just assume no questions because the docs and tutorials are so good :slight_smile:

got started on the graphic bits… add some trig for auto-magic:


an alternative approach with the clock api could look as follows:

softcut.play(1, 1)
softcut.level_slew_time(1, 10.0)
    softcut.level(1, level)
1 Like