Skip to content

Instantly share code, notes, and snippets.

@trentgill
Last active March 7, 2026 22:20
Show Gist options
  • Select an option

  • Save trentgill/1a07ef44fcdbe315abbbd1588669a15a to your computer and use it in GitHub Desktop.

Select an option

Save trentgill/1a07ef44fcdbe315abbbd1588669a15a to your computer and use it in GitHub Desktop.
scale helper for atrium in lieu of scripting
local N = {}
N.to_semi = {
["c"] = 0,
["c#"] = 1,
["db"] = 1,
["d"] = 2,
["d#"] = 3,
["eb"] = 3,
["e"] = 4,
["f"] = 5,
["f#"] = 6,
["gb"] = 6,
["g"] = 7,
["g#"] = 8,
["ab"] = 8,
["a"] = 9,
["a#"] = 10,
["bb"] = 10,
["b"] = 11,
}
N.Octs = { ["+"] = 12, ["-"] = -12 }
N.to_chord = function(istr)
-- "C E G Bb"
local t = {}
for str in string.gmatch(istr, "([^%s]+)") do -- split string on whitespace
str = string.lower(str)
local n = N.to_semi[str]
if n then -- direct note
table.insert(t, N.to_semi[str])
else -- check for octave shift
local oct = 0
repeat
local last = string.sub(str, -1, -1)
oct = oct + N.Octs[last]
str = string.sub(str, 1, -2)
n = N.to_semi[str]
until n
table.insert(t, n + oct)
end
end
return t
end
N.TWELVE_ILOG2 = 12 / math.log(2)
N.ji = function(ratio)
return math.log(ratio) * N.TWELVE_ILOG2
end
-- sanitize scale notes.
-- always filled with 25 values, and a length (for playback)
N.sanitize = function(t)
local len = #t -- memoize number of scale elements
for i = (len + 1), 25 do
t[i] = 0 -- fill with nulls
end
t.len = len -- save the active length (default to all vals)
return t
end
-- t can be a table of semitones away from C (see "Notes")
-- or a string of note names (eg "C Eb G# A+ B- B--")
-- where a trailing +/- adds/subtracts an octave
-- offset is optional, adding a number of semitones
-- just should be a string "just" to indicate inputs are in just intonation form
N.make_scale = function(t, offset, just)
if type(t) == "string" then
t = N.to_chord(t) -- convert string to table of nums
end
if type(offset) == "string" then
offset = N.to_semi[string.lower(offset)]
else
offset = offset or 0
end
for k, v in ipairs(t) do
if just then
t[k] = N.ji(v) + offset
else
t[k] = v + offset
end
end
return t
end
N.print = function(...)
local t = N.sanitize(N.make_scale(...))
print(string.format("{%s,len=%i}", table.concat(t, ","), t.len))
end
N.debug = function(...)
local t = N.make_scale(...)
print(table.concat(t, ", "))
end
return N
@trentgill
Copy link
Author

trentgill commented Mar 7, 2026

how to use it:

run lua in your terminal with lua -i -l “scale” which will open a lua repl in interactive mode with this script loaded as “scale”.

to write your scales you have a few different options, but the easy thing to do is just use the scale.print(…) function to spit out a properly formatted lua table that can be pasted into a patch.

 -- define a C major pentatonic
scale.print{0,2,4,7,9}

-- you can transpose by adding an offset at the end
scale.print({0,2,4,7,9}, 2) -- transpose up by 2 semitones (to D)

-- offsets can also be specified by note names
scale.print({0,2,4,7,9}, 'D') -- note: case doesn't matter

-- you can use just intoned ratios
scale.print({1/1, 9/8, 5/4, 3/2, 5/3}, 0, 'ji') -- note you MUST provide an offset (0 for C)

-- you can offset by a just ratio by making the offset explicitly just
scale.print({1/1, 9/8, 5/4, 3/2, 5/3}, scale.ji(3/2), 'ji') -- transpose up by a just 5th


--- string style

-- there's also support for note-named input
scale.print "C D E G A" -- returns {0,2,4,7,9}

-- you can still transpose
scale.print("C D E G A", "D") -- shifts up by 2 semitones

-- and you can octave shift specific vals with '+' and '-'
scale.print("C D E G A-") -- returns {0,2,4,7,-3} where -3 is (9-12), aka A(9) minus 1 octave(12)

-- multiple octave shifts allowed
scale.print("C- D+ E++ G-- A+++”)

-- you can use upper/lower case as you prefer
scale.print(“C d E f”)

-- and it supports both flat and sharp notation (but no doubleflat or doublesharp)
scale.print(“c# db C# Db DB dB”) — returns {1, 1, 1, 1, 1, 1}

-- NOTE: offsets can *not* have octave shifts applied
-- NOTE: string-style does *not* support just intonation

if you’re playing with this a lot, you’ll probably want to start with sp = scale.print so you can save some keystrokes!

@trentgill
Copy link
Author

Added a new function scale.debug which will let you see the output of your scales more easily while you edit. once you want to put it in a patch file, switch to scale.print to get the correctly formatted version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment