Norns and General Purpose Computing

a story (with code snippets), and some reflections.

we had a show coming up. kelli and i tend to create a performance by assembling a rotating pile of instruments weeks ahead of time and just seeing what emerges.

with electronic gear we’re typically limited by what we can manage with our hands— neither of us is particularly inclined towards “safe” reproduction and tend have a lot of live elements to negotiate. during a practice i played the same chord sequence on the juno for about twenty minutes, occupying both hands. but i also have feet! unfortunately i’m now old and not stretchy enough to turn the cutoff knob with my toe.

during last september’s seven hour marathon performance at half hidden i’d prepared a simple norns script to capture an expression pedal and map that to the juno via sysex. unfortunately i have too many half-assembled norns test units, and couldn’t find the script as i couldn’t figure out which one i used for the show.

i’d been out of practice with regards to scripting. begrudgingly, i started up maiden and took a deep breath.

function init()
  print("hello")
end

ok, this isn’t so bad. it works. i’m using a usb midi dongle to connect the juno, and another midi dongle which just has an expression pedal input. i plug them in, stare into space, wrack my brain, then open the norns reference docs. my memory is jogged.

juno = midi.connect(1)
pedal = midi.connect(2)

pedal.event = function(d)
	data = midi.to_msg(d)
	print(data.type, data.cc, data.val)
end

and there’s now data coming out of the pedal! but the main reason i wanted to find that old code was because i couldn’t remember what was involved with the sysex. search engine led me to this juno reference which just lists clumps of bytes.

well, i knew how to send one byte of midi, so let’s not get fancy here:

function juno_filter(x)
	juno:send{0xf0} -- sysex begin
	juno:send{0x41} -- manufacturer
	juno:send{0x32} -- type (cc)
	juno:send{0x00} -- ch
	juno:send{0x05} -- param (filter)
	juno:send{x}    -- value
	juno:send{0xf7}
end

add this to pedal.event and suddenly it works. and i realize that the reason i didn’t go to great lengths creating a git repository for this (destined for the arctic vault) was because it was so ridiculously simple. i’d written less than twenty lines of code, in half that many minutes— after spending nearly an hour scouring a range of storage devices in order not to have to reproduce the above.

now my foot joined the band, and musical phrases gained another shape. with the enthusiasm of my rediscovered agency, i decided i should add a simple midi looper to capture a phrase and instantly repeat it back. this would give me the opportunity to momentarily free my hands and attend to what i have found to be an often overlooked but fundamentally essential element of performance: playing the mixer. (this subject is a separate essay).

i quickly confirmed i could print out note messages from the juno. i was starting to think about storing data in a table, initializing clocks and measuring time— it all is pretty straightforward— but then i remembered i did something pretty similar for mlr. that pattern recording code was abstracted and turned into a library which is in the norns core, called pattern_time. i haven’t used it since and couldn’t possibly remember the syntax, but again it’s been documented well in the norns reference. after a few minutes i was astonished again:

pattern = require 'lib/pattern_time'
p = pattern.new()
message = "..."
function playback(d)
	juno:send(midi.to_data(d))
end

function init()
	p.process = playback
end

juno.event = function(d)
	data = midi.to_msg(d)
	p:watch(data)
end

function key(n,z)
	if n==3 and z==1 then
		if p.rec == 0 and p.play == 0 then -- empty, start recording
			p:rec_start()
			message = "rec"
		elseif p.rec == 1 then -- recording
			p:rec_stop()
			if p.count > 0 then
				p:start()
				message = "loop"
			else
				message = "..."
			end
		elseif p.play == 1 then -- playing
			p:stop()
			p:clear()
			message = "..."
		end
		redraw()
	end
end

function redraw()
	screen.clear()
	screen.move(64,40)
	screen.text_center(message)
	screen.update()
end

i needed the most minimal interface, so a single key to activate record and loop made sense. minimal visual feedback of the current state is concise. a simple improvement would be adding one line of code to increase the font size.

this story continues. the next day we realized we needed the plucked three-sisters resonant bass drum. it’s a massive under-utilization of technology, but i have a tiny rack and connect the norns to the crow, and write a couple lines to pulse the filter input when pressing the other norns key. it’ll be trivial to expand this to a foot switch, or tempo controlled clock, or even euclidean rhythm (there’s an er library, after all).

some scattered observations:

i probably won’t use this script beyond the performance, and i’ve likely convinced myself that it’s not even worth archiving. that is, if we truly embrace the perspective of this thread (still unfurling, stay with me). i didn’t document the script above or bother to write instructions how to use it or what it was for. i knew that K3 was the looper toggle because the code is small enough to simply look at the key handler. the combination of pedal control, note looper, and crow drum are not a “suite” but simply a selection of facilities to meet the needs of a moment.

yet while i keep this script alive for the duration of practice, it’s eminently approachable to adapt and evolve. i don’t need to think of every option i might need in the future: i focus on now. for example, i could write more code to make that one param byte in the sysex configurable, so i could map the pedal to various other juno params as seen in the reference. the norns parameter system is good for features like this! but i’m never going to menu-dive mid-performance to scroll magic numbers. if i wanted to experiment, i’d just hard-code a different number and relaunch the script. importantly, this keeps out a bunch of lines of “infrastructure” code which just muddy the visibility of that important bits.

this sort of abandon also helps focus on “the point” in this case which was to solve a musical problem (or put positively, to hugely expand musical possibilities). i needed more hands, i made very specialized virtual hands. i did not make a virtual hand training facility or virtual hand factory or virtual hand playground. what i’m getting at is that i didn’t set out to write an app.

more importantly i didn’t get distracted by the limitless possibilities of the code and change course into app building— where i might construct a versatile reusable tool for midi manipulation— because the opportunity presented itself, and clearly the code capability is right in front of us.

“general purpose computing” is an idea with which i’ve become somewhat fixated: a way of using computers that employs the system’s fundamental functionality: data (numbers) and processes (math). less abstractly, using computers for very basic programming, rather than a locked-up appliance that runs apps designed by others (/corporations).

there are several ways to approach the current norns landscape:

  • browse the norns catalog and play a huge variety of community made scripts: imaginative instruments that are a result of serious dedication
  • use norns as a development platform for creating intricate musical instruments, which may or may not be shared publicly
  • treat norns scripting as a sort of musical construction set, where code is fluid and changing

of course each of these are appropriate for different situations, and i’m not making any claims about the value one over the other. (in my own case: i needed a freezing reverb and happily saw that the community produced one— have dedicated much energy to my mlr instrument— and this text exemplifying the third).

there are some tradeoffs between each of these that i’d like to highlight, however.

being able to download a community script is phenomenal! but when it comes to customizing, there’s a substantial ramp-up time to being able to comprehend how a script is made and how/where to make changes. or perhaps you’re seeking a particular solution that is very specific, and it takes quite a lot of time to review a wide number of potential candidates. often the presence of features you don’t need tend to obfuscate the goal. you’ll be learning the user interface language of someone else.

writing your own large script project is such a joy, seeing it evolve and potentially get a level of polish that competes with commercial gear. if shared, hearing the music that others make with it. but there’s also a level of project management and sense of support obligation and thing-ness that such a project accumulates. sometimes a project becomes large enough that it’s difficult to change directions quickly, or reacquaint oneself after a few months away. someday you might think you need a changelog, or do a fix for major system version update.

norns is good at doing simple things, with very little code. i used the slightly-boring example of midi but the softcut system is equally simple and nothing remotely boring. the commands can be integrated into the same fundamental structures: data, decisions, clocks, events, etc. this is what i mean by general-purpose. freely creating and combining functionality using basic information.

i didn’t browse the internet looking to purchase a device which did midi looping. i didn’t look for a shareware midi manager to handle juno sysex data. i just had to remember that we all possess machines that can do so much, and some of them make it easy to assemble creative solutions with just a small amount of effort.

so i’m hoping this story inspires this sort of small effort— the impetus of the norns project from the start.

(and i’m hoping to hear more about micro-projects that people have done like this, but keep private!)

110 Likes

this idea is so satisfying and it resonates with something deep within me from when I was just learning to use computers at a young age (MS-DOS and QBasic in screen mode 13h for me). I greatly appreciate you sharing this.

this echoes thoughts I’ve shared with folks about Teletype, too. there is a sensation that the script is eminently disposable and that I am also programming my muscle memory at the same time that I am programming the machine, with an incredibly tight feedback loop. I have never experienced anything quite like it - even eurorack itself only seems to suggest this possibility.

as far as my own practice with private norns scripts, I used norns in much this way for my first year of owning it. I felt that it was somehow “wrong” to use it in this 3rd way and that a “serious” or “committed” user should be giving back to the community in the form of scripts. but there was a lot of joy for me in “doing stupid softcut tricks” by playing a softcut buffer at a different rate than I was writing to it, or at a different area of the buffer, or looping the writing a la delay while another part was playing it one-shot. with some of the cheapest commodity MIDI gear connected to this beautiful machine. without any pretense of sharing it, the onscreen UI of the most bare bones textual “dev” variety. it felt good… dirty and satisfying. I would like to get back into that mode :slight_smile:

24 Likes

so happy to read this post! all of my norns experience has been either using community catalog scripts or just making my own tiny ones for my own use per performance or purpose

e.g. i really wanted a clone of the ppooll act ‘beauty’ and found a thread of someone attempting to make an SC engine of it. 10 minutes of tweaking and troubleshooting and sick, i have a portable feedback circuit.

or - i really want a simple looper / sample mangler. it is trivially easy to take the softcut studies and jump off into exactly what i want and need. it is a godsend.

i admire all the people who put effort into making very well documented and maintained open source scripts - but it is so nice to have a private repo of my favorite snippets for my own use. like a little diary! (and i maintain enough code in my day job)

22 Likes

This has been the Janus problem for my practice with Norns: in some cases the scrips are so labyrinthine that either I’m intrigued and spend lots of time digging in, or I just give up and move on.

This has given me some impetus to pick up the studies again. Thanks!

8 Likes

To me, this idea carries on the Unix philosophy (Unix philosophy - Wikipedia), specifically the modularity. It’s a different mindset when you realize you can build something instead of having to buy something.

12 Likes

I don’t think this zen approach to programming is encouraged or taught frequently enough. We tend to regard improvised tools as inferior to engineered ones, but it takes improvisation to develop new forms of practice including the culture that supports the practice. Engineering can follow culture. When we put the engineering cart before the horse, the solutions often lack some x factor we might call “soul”.

That being said, I can also relate to the mindset that “I’ve been programming in the code factory factory all day and if I have to look at one more line of text, I’ll scream” at which point I can certainly understand wanting to use some finished and engineered products in order to make bug free music for an hour or two.

The trouble with programming is that it can become engineering before you notice the transition.

15 Likes

As a yet-to-be coder, I’m thrilled that there is a robust library of scripts developed by the community. But I’m also fascinated by the embodied philosophy @tehn is pointing to here.

This heightens my anticipation about joining the norns conversation as an active participant in the very near future!

14 Likes

While I’m not sure it’s quite as general purpose as the interface lua+norns exposes, the first few years of eurorack pre-norns I was mostly controlling my synth with a WW that used custom firmware for keyboards and loop recording (ty tehn for helping me debug some 64 power issues i had at the time :D).

My main “un-norn’d” use of crow right now is as a clock source. Input 1 when unpatched defaults to 90 BPM (my personal fave) with various intervals… i think like 8th notes, 8th note triplets, quarter notes and 1 bar. Patching input 1 with a voltage offset (for instance from a quadratt) will allow you to change the BPM to your liking, patching input 2 will make it work like a standard “clock divider”. In that same vein of “things that are useful don’t have to be complicated” I could very easily rewrite the script if for some reason I accidentally blew it away.

I’m a programmer by trade and I would be “into computers” even if I didn’t do it for a living, but I’m not sure that I would have ever gotten into linux or open tools in software if it wasn’t needed for work. As Jonny said, the “unix philosophy” sort of encapsulates the idea of general purpose computing, but it’s not something that’s really taught in a curriculum outside of higher education (at least in the midwest USA). I think most people don’t view a computer as a tool which I think in part is because most computing is notably not general purpose.

14 Likes

This thread is really cool and very deep thank you…
I have been surprised by the reference to Beauty from PPOOLL!

Since I have a Norns I wanted to clone this bad guy and of course I saw this SC link in the past.
This was my first Norns goal or first project.
I failed like a starting pianist who by a piano for playing his favorite Beethoven pieces… But i was really interested by code!

Your comment made me confident (somebody did this quick and is happy).

So I had to try again today and —— succès ! I was in the game to not ask how you did it, I can do by mi self…

Dirty but sound close to the instrument I use to play in PPOOLL playground in MAX.
Dirty but in my favorite music computer écosystème and without MAX.

That’s not nothing for me!
A Norns Happy user in France since 3 years…

10 Likes

the story continues:

sharing code (and ideas) is a form of potential preservation. after yesterday’s post @ioflow (who holds this forum together!) had recalled that i shared the code during the marathon set in the twitch stream (it was that kind of show).

the code
s = require 'sequins'

m = midi.connect(1)

function init()
  params:add_number("filter","filter",0,127,64)
  params:set_action("filter", function(x) jp("vcf_f",x) end)
end

jmap = {
  lfo_rate = 0,
  lfo_delay = 1,
  dco_lfo = 2,
  dco_pwn = 3,
  dco_noise = 4,
  vcf_f = 5,
  vcf_r = 6,
  vcf_env = 7,
  vcf_lfo = 8,
  vcf_kbd = 9,
  vca = 0xA,
  a = 0xB,
  d = 0xC,
  s = 0xD,
  r = 0xD,
  sub = 0xF
}
  

function no(n,t)
  m:note_on(n,127)
  clock.run(noff,n,t) 
end

function noff(n,t)
  clock.sleep(t)
  m:note_off(n)
end



--m.event = function(data) tab.print(data) end

function jp(p,x)
  m:send{0xf0}
  m:send{0x41}
  m:send{0x32}
  m:send{0x0}
  m:send{jmap[p]}
  m:send{x}
  m:send{0xf7}
end

function panic() for i=0,127 do m:note_off(i) end end



a = s{0,5,3,8,s{15,19},12,s{24,29,8}}
a = s{0,3,8,5,12,15,s{19,24,31}}
a = s{0,3,8,5,12,24,s{19,24,31}:every(2)}

b = s{0,0,0,12,0,7,0,0,s{7,12,24}}

e = s{45,50,55,60,65,70}

c = s{false,false,false, true, false, s{false,true}, true, s{false}:every(2)}

tra = 60
tra2 = 48
ti = 0.1


n = function()
  while true do
    jp("s",5)
    jp("vcf_f",e())
    no(tra+a()+b(),ti)
    if c() then
      no(tra2+a()+b(),ti)
      jp("s",50)
      jp("vcf_f",60)
      clock.sync(2)
    else
      clock.sync(1/8)
    end
  end
end
  
ca = clock.run(n)

a few details jumped out after i saw this. i knew i started with the pedal-to-juno already worked out before the set. but at some point i clearly started editing it live— some sequins emerged so i could, well, hang out with visitors. the giveaway is this:

a = s{0,5,3,8,s{15,19},12,s{24,29,8}}
a = s{0,3,8,5,12,15,s{19,24,31}}
a = s{0,3,8,5,12,24,s{19,24,31}:every(2)}

the first two definitions just get overwritten by the third, so i was line-executing via maiden, testing things out without restarting the script. the likelihood then that this code is “not the final product” is assured by definition: changing the code was part of the process of the performance unfolding.

you’ll also see that despite my previous resistance to use parameters, i did use them this earlier time:

  params:add_number("filter","filter",0,127,64)
  params:set_action("filter", function(x) jp("vcf_f",x) end)

which suggests i just added these two lines then used the param system to map the pedal via the midi mapping system (perhaps with the ranges adjusted, that makes sense… but note, as easy to do with a few more numbers typed for some math scaling.)

and also, some “infrastructure” in the form of a lookup table for the various other sysex params:

jmap = {
  lfo_rate = 0,
  lfo_delay = 1,
  dco_lfo = 2,
  dco_pwn = 3,
  dco_noise = 4,
  vcf_f = 5,
  vcf_r = 6,
...

i don’t remember if i did this on the fly or not, but it is later used in the sequins to make more variation.

the variable names are terrible, but this is normal for thing-in-the-moment, when these obtuse strings are close to your brain.

none of what’s happening is complex: it’s just putting together a few existing musical facilities that the system provides— in a way that allows for a level of control much beyond stringing plugins together with some automation. it’s starting from a lower level. (and again, this example i think works well because it’s nothing genius or original— the point is that it illustrates a different approach to thinking about technological capability.)


what a serious nostalgia trip you just provided. (i recall reading so much about mode x and having it feel just beyond my grasp with assembly/etc. these were the golden demoscene years…)

though i’d say my quintessential general-purpose-computer experience was using basic on the apple 2. having the computer boot to a repl, expectantly. having that be the low level.

teletype and crow have a more narrow focus given their limited capabilities. the nature of these machines ask for a small scope and provide a direct-to-the-point command set. it takes dedication to not just keep going deeper with a more powerful/feature-full machine.

i couldn’t agree more. and am constantly seeking ways to defeat this pattern. i’m hoping this thread can be about that journey.

(not that engineering should be avoided. it’s a necessary task, when called for.)

20 Likes

For me it was the TRS-80 Color Computer 2, but same difference, just more constrained. I often wonder how much it “matters” that this was my introduction to computing. More than a little bit, I suspect.

4 Likes

that 256-square grid ( :wink: ) is the exact image I had in mind, and is permanently burned into my subconscious. the fabled mode X was always beyond my chops as well, and I settled for flinging of bytes at registers with stolen snippets to edit/animate palettes (and control OPL3 synths - a sort of proto-w/syn, at least in my mind).

teletype and crow definitely offer more focus, which often plays nicer with my scattered/distract-able brain. though the moderately richer domain of norns makes it more of a “bicycle for the mind” possibly - more to see, more to do, more to choose from.

5 Likes

I remember someone saying, “Before presets, everyone made their own sounds, back then that was just called using your synth!”

Similarly, it’s a great tragedy of modern life that we ended up with such a separation between “programming computers” and “using computers”. Not that everyone should have to do everything from scratch, but they should be much easier to shape to fit the users need.

9 Likes

I kinda feel like it has never been easier for the average person to do this than it is today. If you consider the choice of apps and software on offer, and the ease of browsing the library, let alone the web apps we have access to, we’re living in a golden age of computing!

The biggest win - you no longer have to fiddle with a command line and config files to achieve want you want to achieve, but you can if you want to!

7 Likes

Here’s such a one, it’s a glue script to get a graphics tablet plugged into a linux box talking to crow: Dave Riedstra / crow inscribe · GitLab. It currently outputs X / Y / Pressure / Gate (on contact) through the outputs, but could be modified to do a lot more stuff (like using those values to interact with running scripts). I don’t have the energy / time to flesh this out into a full project but I hope that aspects of this will be useful or interesting for others.

The story basically goes that my main interface to crow has been via the great snippets to talk to druid from vim. These use websockets, which means pretty much anything can talk to crow via a running druid session. I’ve got an entry-level graphics tablet on the shelf above my desk and some experience with using linux’s device input data from another project so I thought, “Hey, my euro system doesn’t really have a controller, what if…” and got to coding. Using websockets didn’t work very well because druid’s repl prints every lua snippet that’s sent to it to the terminal before it’s passed on to crow, and printing lines takes time. At this point I remembered, “Hey, druid is an open source python thing, what if I just re-used how it talks to crow in my own thing?” and after some browsing of the source, I was able to import one module and use two of its functions for good communication to crow with low enough latency to use with a controller.

Usage notes
  • make sure the druid source is in the same directory as the script (best done by getting crow-inscribe with git clone --recurse-submodules)
  • you’ll need the evdev python package as well (pip install evdev or sudo apt install python3-evdev)
  • you need to provide the device address to the script (something like /dev/input/event23), you can find the right one by scanning the output of libinput list-devices)
  • tweak the output ranges in lines 20-22 to suit
  • I swapped the Y axis so origin is in the bottom left instead of the top left (and therefore up is up)
  • if you want to explore other uses, start by modifying the crow_set_volts() function (using crow’s public API seems like the most sensible way to me)
10 Likes

Most are just slight variations on standard high-level abstractions. Some enable, some coerce. And some do neither.

While Emacs is still kicking.

It is easier than it was. But we’re still at a distance from true personalization.
(Whether it’s technology, or medicine, or social policy) We’ve only just begun, it seems.

Lots of interesting generators, but very few ways of composing and organizing material.

1 Like

This, also. I have such huge love and respect for the developers and maintainers out there. All of you. As someone who has yet to contribute but has it on their “to-do” list, I feel such a sense of awe sometimes at the level of creativity I see here daily.

I want to call out @dan_derks, @infinitedigits, and @tyleretters for being so insanely generous on a daily basis. That being said, there are so many others out there, here and on the discord, giving of their time and knowledge to help others.

I guess what I’m saying is not all heros wear capes. :anatomical_heart::anatomical_heart:

13 Likes

:star: much appreciated :slight_smile:

your examples exemplified here are really what make norns special. I also love norns as the musical construction set. especially when it comes to the prototyping of ideas - ideas can quickly become reality with the tools at hand. couple of examples: I was thinking about a cv sequencer that had multiple lanes asynchronously playing and prototyped it into grid+crow (code). I had an idea about different diameter circles rotating with beads that play chords only when they align and otherwise play single notes (demo, code). I made “lights out” on the grid (code). little projects like these took little time because of the ease the available musical construction set. and I don’t sweat that these projects didn’t have much life after coding them, they served to prototype the idea and answered the questions I had about the musical ideas I was thinking through. also just fun sometimes.

also realizing code is fluid helps to interact with a lot of norns scripts. I consider most of the code in norns “fluid” and constantly change other scripts to suit my own needs (e.g. recently modded colorwheel to add strings, even modded norns itself to get more softcut voices). I whole heartily encourage others to do the same. the barriers to doing so are surprisingly small. and if you’re stuck there is lots of help at hand :open_hands:

norns + supercollider

I’d like to stick a point onto the general computing when it comes to SuperCollider. definetly norns is slick when it comes to the music fundamentals - its super easy to connect midi, make complex sequences+clocks, and connect it an interface (i.e. screen+controls). but, it is also possible to do these things in SuperCollider (e.g. sequencing+clocks, midi, and even gui interfaces).

but because norns is such a great musical construct set, that you don’t have to do things in SuperCollider. why is this useful? well, it means you can write your idea in Lua. and Lua is a language that describes itself as being easy to learn. SuperCollider, on the otherhand, is not (though now there’s lots of help for learning SC).

a real example: I recently ported some code I wrote for a norns script “mx.samples” from lua into SuperCollider. they are a similar number of lines (Lua a bit shorter) but I have to say it was easier on my brain to do the Lua. you may judge yourself. but broadly speaking, I find the general purposefulness of the Lua language makes it easy to take shortcuts that are harder to do in SuperCollider. though they both work, I had to work harder in SuperCollider to do the exact same thing.

anyways, all of this to say is that I used to have to explore musical ideas using portmidi + custom code libraries / python dependencies, etc… but with norns I can explore musical space in a few dozen lines of Lua and enjoy and have time to explore some more.

27 Likes

the way lua blankets itself over SC with very easy to understand abstractions is quite wonderful.

i recently reinstalled linux on an older X1 thinkpad and within an afternoon of futzing i got the norns stack working “natively”. so rather than having to figure out how to do a buncha glue to make supercollider work nice with osc/midi i can instead focus on the more fun part of “just making music”.

i don’t think that running norns on “baremetal” outside of the shield/factory/fates is necessarily something that needs to be promoted given how high the support burden is with even the factory/3/4/fates variations, but given that it’s built on free software if you’re unable to afford a norns and you have a working computer you can still interact with the ecosystem given enough patience.

6 Likes

How much does it depend on a well designed environment?