a live-coding interface that interconnects norns.


I made this specifically for the upcoming flash crash performance. this script is a cross between a live coding environment and a tracker - its sorta a tracker you sequence with code. the code you can sequence is any norns lua code. using code you can access any of the standard norns features (midi, osc, crow), and I’ve also added interfaces to “lite” versions of several of my own scripts (like oooooo, supertonic, amen, and mx.samples). some basic features of internorns:

  • sequence pitches with text, in chords (e.g. Cm7/Eb) or notes (e.g. a4 eb3)
  • sequence midi devices and add cc lfos
  • sequence crow with pitches, automatically generating voltage/envelope
  • play/rec into three stereo loops via softcut
  • built-in drum synth that can be sequenced/modulated in realtime
  • built-in sample player that can be quantized
  • sequence notes/chords from any instrument in mx.samples
  • special tape stop / start global fx

internorns works quite simply: it runs an internal sequencer that runs code. there is an internal clock that makes 4 steps per beat, and 4 beats per measure. at each step it checks to see if there is code to run in that step in the current measure and attempts to run it. code can be added to steps using the built-in functions.

see data/getting-started.lua to get started and preview how it works. the process is music.


  • computer
  • norns
  • midi device (optional)
  • crow (optional)


start the internorns script on norns.

now choose an editor to live-code, I suggest usig either maiden (in the browser), visual studio code, or vim. instructions for each are below.


open up a webbrowser to http://norns.local/maiden/#edit/dust/data/internorns/getting-started.lua.

you can select any code and press ctl+enter to send that code to the norns.

note: requires latest version of maiden.

visual studio code

download visual studio code and then install the Norns REPL extension. use software like sftp drive to mount your norns on your computer. then you can directly edit ~/dust/data/internorns/getting-started.lua.

press ctl+enter to send the current line to the norns.


lines from a norns script can be quickly and easily run using vim.

to use with vim, first download wscat - a utility for piping commands to the maiden websocket server.

chmod +x wscat
sudo mv wscat /usr/local/bin/

then you can edit your .vimrc file to include these lines which will automatically run
the current selected line when you press ctl+c:

set underline
nnoremap <C-c> <esc>:silent.w !wscat<enter>
inoremap <C-c> <esc>:silent.w !wscat<enter>i

now whenever you use the key combo ctl+c it will send the current line in vim into maiden!

open up dust/data/internorns/getting-started.lua in your editor to learn how to use internorns.


make sure you install all of the following (or update if you already have them):


then restart your norns.


:exploding_head: :exploding_head: :exploding_head:

this looks awesome, thank you for making it and sharing!


Uuh nice one. I’ve been livecoding on vanilla :icecream: Maiden and this kind “crutches” will definitely be nice and helpful for taking simple things to another level and bringing people onboard livecoding fun.

Respect and thank you and rave on :keyboard: = :musical_keyboard:

PS looking forward to some epic crashes on flash crash.


excellent excellent work and superb tutorial/demo


this is absolutely brilliant



Zack, my favourite part of the video is when you say “that’s about it”, the rest of us have our jaws on the floor and you’re like, “yeah, ok, let’s make some music”.

This is next level, really opens up the power of Maiden to live coding and expressing oneself in a very fluid way, with exceptionally well documented code to boot.

As always, Thank You.


gonna look thru the docs but does the midi spec allow for a table sequencing program changes?

1 Like

thanks for the kind words @mattallison, @Gahlord, @tyleretters, @glia, @xmacex, and @Justmat!

hm - right now its not easy to do program changes - only cc’s (through cclfo(..)) and midi notes (through play(..)). it wouldn’t be hard to add a thing to do program changes, a new program change interpreter would be needed (i.e. almost identical to this, but program changes instead of cc) and then a shim could be added to access it more easily (i.e. almost identical to the cclfo).


v1.1.0 - bass added

little demo (internorns sequencing bass synth + sh-01a + drum samples):


v1.2.0 - more synths + midi hooks

here’s a demo script that uses internorns making use of the new midi hooks. basically I’m hooking the op-z sequencer and utilizing internorns engines for almost all the sounds (lead is mx.samples epiano, chords are mx.samples strings, bass is internorns bass, the arp is op-z → norns → make noise strega), even the samples get sequenced based on the incoming midi note.


-- drums
hook({name="opz",ch=1},{note_on=function(note,vel,ch)  if math.random()<0.5 then print((note-53)/16); sample.pos(2,(note-53)/16); sample.rate(2,1); end end})

-- vocals
hook({name="opz",ch=4},{note_on=function(note,vel,ch) engine.amplag(1,0.01); sample.pos(1,(note-53)/16); sample.level(1,0.6) end})
hook({name="opz",ch=4},{note_off=function(note,vel,ch)  engine.amplag(1,2); sample.level(1,0) end})

-- bass
hook({name="opz",ch=5},{note_on=function(note,vel,ch) engine.bassamp(0.8);engine.bassnote(note);end})
hook({name="opz",ch=5},{note_off=function(note,vel,ch) engine.bassamp(0.6) end})

-- strings
hook({name="opz",ch=8},{note_on=function(note,vel,ch) mx:on({name="string_spurs_swells",midi=note-12,velocity=120,amp=0.2}) end})
hook({name="opz",ch=8},{note_off=function(note,vel,ch) mx:off({name="string_spurs_swells",midi=note-12})  end})
hook({name="opz",ch=8},{note_on=function(note,vel,ch)  mx:on({name="sweep_violins",midi=note-12,velocity=120,amp=0.2}) end})
hook({name="opz",ch=8},{note_off=function(note,vel,ch) mx:off({name="sweep_violins",midi=note-12})  end})

-- lead piano
hook({name="opz",ch=6},{note_on=function(note,vel,ch)  mx:on({name="fender_rhodes",midi=note-12,velocity=120,amp=1.3}) end})
hook({name="opz",ch=6},{note_off=function(note,vel,ch) mx:off({name="fender_rhodes",midi=note-12})  end})

-- arp
crow.output[2].action="{ to(10,0.005),to(0,0.02) }";crow.output[2]()
crow.output[2].action="{ to(10,0.01),to(0,0.05) }";crow.output[2]()

in action (featuring snippets of Peter Brook):


This is a really interesting script and it’s making me want to learn more Lua :slight_smile:

I’m trying to figure out how to do something and i’m not sure if it’s even possible……

Attempting to make a really lightweight setup using an iPad to communicate with Internorns. Of course i can access Maiden through the web browser and that’s fine, but when using an external keyboard i find it often loses ‘focus’ and the arrow keys start taking me around the webpage rather than around the text i’m trying to edit, and trying to get the ipad to focus on the code again becomes a pain. I’m wondering if there’s a tablet equivalent of the VS Code integration? I tried SSH-ing in with a terminal app and i can load Maiden with maiden repl but then I’m stuck doing one line at a time, what I really want is to edit a .lua file and be able to move around at will, build a whole script and hit cmd+enter to activate a line or bunch of lines. In case its not obvious i’m not very good at terminal related stuff so this might be super-easy… or totally impossible. Anyone have any ideas?

The dream setup:

Update: ok this is working now, with a little bit of advice/encouragement from @ypxkap (thanks!!). I just had to suck it up and figure out how to use Vim which i guess is what i was trying to avoid :sweat_smile:


This is super fun! Got the example to run via vim on the local device. The only thing I was missing from maiden was the output window, which I discovered is a websocket coming from matron. The following shell script combined with GNU screen (I hear tmux is what the cool kids use) for multiple windows sends the websocket output via curl. There’s some random binary character noise I don’t know how to silence but it was quick and did what I wanted.

curl --include \
     --no-buffer \
     --header "Connection: Upgrade" \
     --header "Upgrade: websocket" \
     --header "Host: localhost:5555" \
     --header "Origin: http://localhost:5555" \
     --header "Sec-WebSocket-Version: 13" \

Related, the mx.samples example throws an exception. Looking into it.

rc: could not run 'mx:on({name='steinway_model_b',midi=63,velocity=80,amp=1.3,attack=0});mx:on({name='steinway_model_b',midi=68,velocity=80,amp=1.3,attack=0});mx:on({name='steinway_model_b',midi=71,velocity=80,amp=1.3,attack=0});': /home/we/dust/code/mx.samples/lib/mx.samples.lua:322: attempt to index a nil value (field '?')