Scheme dialect livecoding tracker


Norns, keyboard, timber engine



~ open / close repl

shift + ctrl start / stop playback

capslock toggle follow mode

shift + 1 - 4 mute track

enter open text editor (third column of track must be selected)

esc close text editor

shift + enter save expression in cell (while in text edit mode)

ctrl + c / v copy / paste

shift + esc open / close norns menu

ctrl + left / right switch menu tabs

shift + up / down fast nav between samples in params menu


function arguments description
(def) symbol, value define a symbol
(lambda) args, function anonymous function
(quote) ' expr returns unevaluated expression
(if) cond, expr 1, expr 2 evaluate expr 1 if cond is true, else evaluates expr 2
(when) cond, expr evaluate expr if cond is true
(@) track optional returns current position
(bpm) value set global bpm
(div) value set track speed divider
(length) value set pattern length
(jmp) pos jump to position
(skip) pos skip current step
(ever) N, expr evaluate expression every N cycle
(mute) track optional mute track
(sync) track optional sync positions to track, or to global pos
(save) id save pattern
(load) id load pattern
(note) value write note at current position
(sample) value write sample at current position
(pos) value set position of current sample
(param) value set current sample param
(help) - display help

(+) (-) (*) (/) (%) (^) (=) (eq) (>) (<) (<=) (>=) (rnd)


(list) (list?) (append) (apply) (begin) (car) (cdr) (cons)
(len) (get) (put) (nil?) (num?) (print) (concat) (map) (#t) (#f)


Functions can be executed either live in repl or from pattern cells.

(def A 1) - sets symbol A to 1

(print "Hello") - double quotes for strings

(def A (lambda () (print "Hello"))) - defines a function (A) with no arguments

(def A (lambda (a b c) (+ a b c))) - defines a function (A) which takes 3 arguments and returns their sum.

(get '(1 2 3 4) 1) - returns first element from list

(bpm 120) - set bpm to 120

(jmp) - set current track position to the very beginning

(pos (rnd 1 99)) - set random start position for current sample

(atk 0.25) - set current sample attack to 0.25

params shortenings
atk  - amp_env_attack
dec - amp_env_decay
sus - amp_env_sustain
rel - amp_env_release
detune - detune_cents
strtch - by_percentage
ctf - filter_freq
res - filter_resonance
ftype - filter_type
qlt - quality
fm-lfo1 - freq_mod_lfo_1
fm-lfo2 - freq_mod_lfo_2
f-lfo1 - filter_freq_mod_lfo_1
f-lfo2 - filter_freq_mod_lfo_2
p-lfo1 - pan_mod_lfo_1
p-lfo2 - pan_mod_lfo_2
a-lfo1 - amp_mod_lfo_1
a-lfo2 - amp_mod_lfo_2
fm-env - freq_mod_env
f-fm-env - filter_freq_mod_env
f-fm-vel - filter_freq_mod_vel
f-fm-pr - filter_freq_mod_pressure
f-track - filter_tracking
p-env - pan_mod_env
m-atk - mod_env_attack
m-dec - mod_env_decay
m-sus - mod_env_sustain
m-rel - mod_env_release

Known bugs-features:

copy-pasted expression cells are linked, so editing one would affect all others.




wait wait wait, lisp, plus trackers, plus norns??? :heart_eyes:


This looks awesome! Can’t wait to give it a go :slight_smile:

peeked at gh and really admire the way you’ve broken out various parts of the app as libs (while implementing a few shared by others)

the main lua file is only ~100 elegant lines of code

what a masterpiece!


Oh man, this is amazing. You’re a star.

Oh, and does anyone know if Bluetooth keyboards function with original norns?

1 Like

@Joakim, they do not.

This is awesome. I think you can go nuts with this once you work out how to use all the bits of code in the list.

Some quick tips from @its_your_bedtime

If you want to randomly jump the playheads around from row 1 to 16

(jmp (rnd 1 16))

If you want to slow down the bpm of a column eg divide the global bpm by 2

(div 2)

A couple of ahha moments,…For each track, the first cell is the sample number, the second cell is I think pitch, the 3rd cell is for code. Press enter on the 3rd cell to open. Write your code. Shift + Enter to get the code to accept. I was pressing Enter after writing the code and realise why it wasn’t working. Once the code is accepted the 3rd cell shows a *
You can then copy and paste that * to any other 3rd cell you like on the tracker.
If you want to change stuff globally, and not per cell, then that is a separate page ~


Thanks for kind words everyone!

Regarding copy-paste - pasted script cells are getting linked somehow, so editing one of them will affect others. Kinda useful sometimes but definitely a bug :slight_smile:


I’ve added nisp to the community project catalog. Refresh the catalog in maiden and install away.


Forgot to mention - its possible to use note+octave for pitch, eg c4 for C#4, as number value doesnt cover full range (1-127)

edit: added (length) operator for setting the total amount of steps. defaults to 16


This is great!! I have a few ideas for PRs too.
I love it so far


(sample (rnd 0 20))
(bpm 2000)
(note (rnd 0 127))



i have a few questions that im not sure about

im trying to scrub a longish sample using this command but couldn’t get any movement in numbers on the start time. Is there something else that should be added to this command? I would like to play about with the sample parameters extras more but couldn’t work out how to do it

How does load and save pattern work? Is that a preset or a pattern of all 4 channels? I’d like to be able randomly flip through different patterns. Is that done in the global text box ~?

Can you give me an “ever” example? Does this mean every x cycle do something? eg play 4 times, randomly jump. Can you use it like a song mode? eg play pattern 1 8 times, play pattern 2 8 times?

I’ve just made a few fixes for control commands so pos and others are now working as intended. :upside_down_face:

load/save, called from within script cell or repl acts almost as ones from params menu, except for not loading/saving sample params.

It requires a bit of setup for randomised pattern switches -

  • save a few patterns with integer names - 1, 2, 3, 4
  • place at the end of each pattern (load (rnd 1 4))
  • something like song mode is also possible with (ever 2 (load (rnd 1 4)))
  • not tested on high bpm :smile:
1 Like

the load/save patterns is working great and its good to be able to get access to the sample params stuff now - im using some filter modulation and sample stretch. And sample scrub works great also - thankyou!


I really love this script so far, but how can I call functions from a pattern?

Each third cell can store a script. Place cursor on it and press Enter to open text editor, Shift+Enter to save, Esc to close


Thank you very much, now it’s clear

This is excellent but am getting error codes when trying to use shift and up/down to whizz between samples in parameters page:-

stack traceback:
/home/we/dust/code/nisp/NISP.lua:79: in field ‘event’
/home/we/norns/lua/core/hid.lua:145: in function </home/we/norns/lua/core/hid.lua:135>
lua: /home/we/dust/code/nisp/NISP.lua:81: attempt to call a nil value (field ‘enc’)
stack traceback:
/home/we/dust/code/nisp/NISP.lua:81: in field ‘event’
/home/we/norns/lua/core/hid.lua:145: in function </home/we/norns/lua/core/hid.lua:135>
lua: /home/we/dust/code/nisp/NISP.lua:79: attempt to call a nil value (field ‘enc’)


any idea why this : (qlt 2) is not working?

/home/we/norns/lua/core/paramset.lua:195: invalid paramset index: quality_00
stack traceback:
[C]: in function 'error'
/home/we/norns/lua/core/paramset.lua:195: in function 'core/paramset.lookup_param'
/home/we/norns/lua/core/paramset.lua:142: in function 'core/paramset.set'
/home/we/dust/code/nisp/lib/_corelib.lua:142: in function </home/we/dust/code/nisp/lib/_corelib.lua:138>
(...tail calls...)
/home/we/dust/code/nisp/lib/lisp.lua:84: in field 'collect'
/home/we/dust/code/nisp/lib/lisp.lua:118: in field 'eval'
/home/we/dust/code/nisp/lib/lisp.lua:165: in field 'run'
/home/we/dust/code/nisp/lib/tracker.lua:66: in field 'evaluate'
/home/we/dust/code/nisp/lib/tracker.lua:88: in field 'exec'
/home/we/dust/code/nisp/lib/lisp.lua:59: in field 'on_step'
/home/we/norns/lua/lib/beatclock.lua:70: in function 'beatclock.advance_step'
/home/we/norns/lua/lib/beatclock.lua:76: in function 'beatclock.tick'
/home/we/norns/lua/lib/beatclock.lua:22: in field 'event'
/home/we/norns/lua/core/metro.lua:165: in function </home/we/norns/lua/core/metro.lua:162>
1 Like