[cheat codes 2] (rev 220714: LTS7.2.1 [LFOs + more!])

thanks so much for the very positive vibes, everyone! it means a great deal to see new adventures continue to form with the script :toolbox: :sparkles:

rev 220513: LTS 6


  • support for user script side-loading, which allows an artist to bundle their own Lua sketches with a saved collection – more on this below the break!
  • added parameter for adjusting levels for audio coming into the live buffer (@dgsiii , thank you for asking!)


  • delay bundles actually save + restore now!! (dang, this has been borked for so long, i had no idea lol)
  • delay bundle saving only takes 250ms instead of a full second (dang, a 1 second hold is long)
  • grid now redraws as soon as its plugged in (dang, how did i forget to do this before??)
  • if live buffer was recording when collection was saved, the rec flag was still enabled when it was reloaded, even though it wasn’t actually recording (no dang, just never noticed before!)

user script side-loading

okay, so, @TPeak , your question cracked a bit of daylight onto my brain in a really nice way:

the pattern sequencer on the 128 is a good target for repurposing for this, but i completely rewrote the sequencing mechanism for cheat codes yellow (for new folks, this is the working title for the next and final version of this script) and wanted to explore alternatives which could serve a dual purpose: getting this feature request met + getting folks scripting in a low impact way.

so, LTS 6 introduces a user script side-loading mechanism which allows you to roll your own executions of functions from the main script, or any other code you might want to run in parallel!

how it works

( edit: added expanders cuz it was a lot of text :slight_smile: )

location to save in

<~~ click to expand
  • save a collection (let’s use AEILL for this example)
  • under data > cheat_codes_2, you’ll find a collection-AEILL folder
  • inside of that folder, create a blank user_script.lua file (or import one!)
    • important! the file needs to be named user_script.lua in order for cheat codes to catch it when loading your collection
    • important! don’t put it inside of any of the other folders inside of the collection-AEILL folder (like, arps or banks, etc), just create it on the same level as those folders, eg:

how to build a user_script

<~~ click to expand

as of rev 220513: LTS 6, cheat codes has some callback actions to support user scripts:

  • user_script.init() is called when cheat codes finds a user_script.lua file inside of a loaded collection’s folder
  • user_script.transport_start() is called when the transport starts, useful to keep clock-based functions synced up with the main script
  • user_script.transport_stop() is called when the transport stops, useful to stop any clock-based functions running or resetting state when the main script stops

a boilerplate user script would then look like:

-- required container, do not change this:
local user_script = {}

-- this function is called whenever a loaded collection
--   has a 'user_script.lua' file in the collection:
function user_script.init()
  print("hello from the user script")

-- this function is called whenever the transport starts:
function user_script.transport_start()
  print("user script started")

-- this function is called whenever the transport stops:
function user_script.transport_stop()
  print("user script stopped")

-- required return, do not change this:
return user_script

other functions and variables can totally be added to this boilerplate! just prepend each function with user_script.. for variables, either use locals or try to pick unique variable names.
want to check to see if cheat codes already has something registered? just execute the keyword you want to use in the maiden repl (the >> at the bottom of maiden) and if it returns nil then you’re good!

example 1: delay snapshot step sequencer

<~~ click to expand

with a user script, totally! here’s a full script for a step sequencer which starts when the main script’s transport starts and stops + resets position when the transport stops, where you fill in my_delays[x].steps (x = 1 for L delay, x = 2 for R delay) to sequence snapshots:

cheat codes user script: delay sequencer example · GitHub

if you’re curious about the delay grid page’s snapshot numbering along the sides, it runs like this (with other functions present for orientation):

L L L length L rate R rate R length R R
1 9 x2 x2 x2 x2 1 9
2 10 /2 /2 /2 /2 2 10
3 11 rev rev 3 11
4 12 x x x x 4 12
5 13 x x x x 5 13
6 14 x x x x 6 14
7 15 x x x x 7 15
8 16 x x x x 8 16

so, incorporating that example would look like:

  • build a set with delay snapshots
  • save it as a collection
  • place the example script in that collection’s data/cheat_codes_2 folder
  • build your own delay snapshot step sequencer by editing the user_script.lua file in maiden to match your saved snapshots and how you want the sequencer to behave, eg:
    • line 13: [1] = 1
      • change [1] to [9] to execute on the 9th step
      • change = 1 to = 6 to call up the 6th snapshot instead of the first
    • line 17: end_point = 64
      • change 64 to 16 to execute a 16-step sequence (or change the start_point, too?!)
    • line 19: sync = 1/4
      • change 1/4 to 1 to change from 1/16th notes to whole notes (assuming in 4/4)
  • reload the collection and your user script will be loaded!

example 2: pad step sequencer

<~~ click to expand

this one’s the same concept of building your own step sequencer inside of cheat codes, all with code, but focused on sequencing pads instead of delay snapshots:

cheat codes user script: pad sequencer example · GitHub

same cadence applies:

  • build a set
  • save it as a collection
  • place the example script in that collection’s data/cheat_codes_2 folder
  • build your own pad step sequencer by editing the user_script.lua file in maiden to match your whims!

closing note

user script side-loading can open up a ton of new gestures, which i’m very excited to explore with y’all!!

there are a lot of functions in cheat codes which could be looped in, but the script is kinda sprawling (it represents basically my learning process for ~3 years), so i’m very happy to help point folks toward the functions they want to subvert and remix! just lmk :slight_smile: :gem:


This update is a wild piece of work, I appreciate the work you do. Really looking forward to diving in!


I’m interested to experiment with using 2 LPs as a 128 grid with CC2, but every time I try these instructions, when I get to the last bullet “reboot the script”, it always resets “grid size” to 64 and “midigrid?” to yes in parameters. I’ve tried rebooting the norns before restarting the script, and saving or deleting presets, but in all cases it resets params to 64/yes.

I’m using 2 Launchpad Xs with a norns shield, and they function as a 128 grid with the ‘neauoire/tutorial/9_grid.lua’ script.

I’ve reviewed this forum (what a great community here!), searched for other steps, but must be overlooking something or perhaps something has changed?

Anyway, Dan - I’m so impressed with your work and looking forward to learning more about this remarkable script/instrument/world!

EDIT: After some power cycles, installing the grid-test script, setting CC2 params to 128 and midigrid yes, and saving presets, it seems to be working! I’m super excited to go throught the musichackspace courses and really dig into this!


You are an amazing human.


@dan_derks, are these grid copy/paste instructions still correct? i’m finding that in order to copy/paste i have to release the ALT key between the copy and paste steps. so, i’m thinking the instructions are actually more like:

enable pad focus mode → hold global ALT + press pad you want to copy → release global ALT → hold global ALT+ press pad you want to paste into.

does that sound right?


ooo stoked you’re digging into the script, thank you @jaseknighter !!!


it sounds like an accurate assessment of a bug, for sure! idk when this got in there, but it’s cleared up now and copy/paste follows those details!

rev 220517: LTS 6.1


  • pad copy/paste now works as historically described:

this is all aligned! the vertical 128 experience on the two horizontal launchpads is reportedly wonky experience, so the script will encourage the 64 layout for the launchpad’d – but you can override it in the params and save that and you should be good!

this is super sweet :hugs: i’m just hanging out, v v grateful for y’all <3


confirmed. :heart_eyes:

i am happily stumbling my way around cheatcodes, loving every moment. here are today’s results.


New Norns user here. Is there a way to get the output of the playing tape into the buffers?

I’m super excited about cheat codes and would like to use it in a workflow to build off of prerecorded modular jams. Idea would be something like this: load up longish audio and play in the tape, then start grabbing portions in realtime in Cheat codes buffers to loop, slice, etc. on top of the playing tape.

I see in the Crone signal flow diagram that the tape in comes after the softcut in, but is there anything I’m missing that could make such a workflow possible with the current script?


@SpectralClockworks, welcome!

(edited for incorrectness :P)
i don’t think this is possible given how norns is wired by default. it might be possible to perform some jack rewiring so the tape output is sent to softcut, similar to the approach described here.

i mucked around a bit trying to do this but i don’t understand how tape is wired up well enough get it working (assuming it is even possible).

1 Like

i believe you’d check in params/levels

Thanks @edison & @jaseknighter!

I’d actually tried the level param for tape with no luck. I tried again after reading edison’s post, with oooooo loaded, and it actually worked fine. Went back to CC2, no dice.

I was ready to take the question off this topic, assuming it was a routing issue, but the fact that it seems to work with some scripts and not others makes me wonder…

ah! apologies… i’ve actually never tried it, but always assumed it worked that way. i’m sure dan will holler in a bit, but this would be a really handy way of jamming!

the other option is to just set the buffer to 32 seconds…. then load in your tape to a buffer. i believe it will load in the first 32 seconds of your recording that way. not perfect but had some really rad jams that way with old orgn recordings.

1 Like

No apologies needed at all, your comment was super helpful…I don’t know all the expected behavious so was really at a loss.

After browsing the oooooo code and meditating on the Crone a bit, I see that oooooo is using SoundIn within a custom engine. I believe CC2 uses the standard softcut ins (I’m not as able to skim the lua code as sc code). It looks like the softcut lit bus is just ext and engine, while the aux send (which I assume goes to the engine in) comes after tape and mirrors the level params.

I suspect what would be needed is a utility passthrough engine that just forwarded SC in to out. Maybe I’ll see if I can figure out how to hack that together.

hey y’all hope all’s well!

just to confirm, it’s totally possible to route TAPE playback into softcut (+ cheat codes) without any jack-hacking! :slight_smile:

if you navigate to PARAMETERS > SOFTCUT, there’s an input tape level which is likely -inf. if you bump that up to ~0dB, you’ll be able to route TAPE playback into the live buffers in cheat codes like you would with the incoming audio signal. it does require the tape level on PARAMETERS > LEVELS to be turned up as well – the default norns routing doesn’t allow bypassing monitoring the TAPE signal as you route it into softcut.

just tested + confirmed on my end, so please lmk if there’s anything else and i’ll be happy to help!

just for the code-curious, oooooo bypasses the softcut tape input parameter and sets the level directly depending on the recording mode selected: oooooo/oooooo.lua at main · schollz/oooooo · GitHub


Thanks @dan_derks !

Skimmed your post too fast at first, then tried the softcut params, and voila! (tbh a little bummed I couldn’t introduce myself with a bugfix or feature add PR or something like that, but all in good time!)


Thanks for this update, can’t wait to try it!

I was working on a track and had 8/9 iterations saved as collections, but noticed that I accidentally nuked my first arp pattern in my latest collection (I use the pattern sequencer to switch between arps, and love this feature). I was able to copy/paste the pattern data from a previous collection (patterns/17.data in my case, so 1st pattern of Bank C) but noticed these shadow-pattern data sets, would you mind shedding some light on these please?
Also if I understand correctly, the data in collection/arps is the last played arp before saving?

1 Like

This looks like a job for Block Diagram Guy



ehhh they’re an uninformed tactic. before i knew better, i realized i had wired up the pattern save mechanism to overwrite the data saved in the collection. so, i created a complementary ‘shadow pattern’ mechanism where changes could be written to disk and if the collection is re-saved, they commit as stored blocks on the meta-sequencer. if the script is restarted without saving, then they’re wiped.

i could’ve avoided that whole rigamarole by simply committing changes to a local table, which wouldn’t be written to disk unless the collection is saved, but i didn’t really have the knowledge at the time to do this, and i didn’t want to break historical behavior once i’d learned more. the next major version of the script follows better design :slight_smile:


Hi @dan_derks

Just checking - Am I right in thinking CC should do stop/start from external midi as well as with link?
Link works but midi doesn’t. While it does seem to keep the tempo via external clock

hihi, hope all’s well :slight_smile: \

absolutely! is your midi device enabled to control transport under the script’s transport parameters? any report of your settings will definitely help suss things out! :revolving_hearts:

small bump, cuz i was testing the above and needed to clear a non-derailing but still annoying bug:

rev 220524: LTS 6.2


  • if there is no user script, don’t let the transport try to call it