HALion is 25 years old — and still no Scale Lock?

As someone who genuinely loves HALion, I find this very frustrating.

HALion is celebrating its 25th anniversary, yet after all these years it still doesn’t have something as basic and useful as a Scale Lock feature — a feature that can be found even in some free instruments and players.

I’m not writing this as someone who dislikes HALion. Quite the opposite. I’m one of its biggest supporters. I use it extensively, and on my YouTube channel I have detailed content dedicated specifically to HALion and its features.

That is exactly why I’m writing this with some frustration.

HALion is an incredibly deep and powerful instrument, and it feels strange that after 25 years we still cannot simply choose a musical scale and lock the keyboard/notes to that scale.

I really hope Steinberg takes this request seriously and considers adding a proper Scale Lock feature to HALion.

After 25 years, I honestly think HALion deserves to have it.

The only problem with that is if you use passing tones they will all be moved to scale notes instead…it’s a lot easier to just play in key :slight_smile:

If I understand the OP properly…

HALion has had this for a long time. I got into full HALion in the version 5, Cubase 7 era, and it’s been here all this time.

It is a modifer called " Tuning Scale • HALion Help • Reader • Steinberg"

I rarely see it set up in existing HALion libraries, so Sonic users often don’t get it, but if you’ve full HALion you can disable the HS compatibility thing, drop this module into any layer you wish (typically one copy in top parent level), and save yourself presets with whatever scale locks you like. I think Iconica might use it, but with limited options for users through the Macro UI.

Within the scale tuning module, presets are included for many scales already having been built for us, plus you can build your own, or even import scala files. I believe with all of the parameters of such a module can be adjusted in real time as well (bound to CCs or set up as VST automation lanes). You can also build switches to simply mute in and out different tunings as needed (Set up several modules, engage/disengage the one you want active in the moment as needed).

Extras: I don’t know if HALion supports MTS (MIDI tuning standard via sysex), but it does support universal tuning and PB range via RPN1/2, and you can tune individual notes via VST3 in real time. Of course the old pitch bend trick works, and Cubase even has built in modules for the PB method.

I’ve long thought it would be nice to update all the Libraries that come with HALion, Sonic Selection, etc, to make available the custom scale Tuning module for all users. It would simply be a matter of adding the module to all a library’s presets, and tacking on a Macro Editor page so Sonic users could access and control the parameters registered up by the module.

HALion itself has been able to do it for ages. Maybe someday it’ll be added to all stock HALion/Sonic libraries. I personally never thought to post a feature request for it because being a full HALion user I’ve always been covered. I can’t say I’ve seen others request it for Sonic instruments very often, if ever. It does seems like a good idea to add it though.

Thanks for your detailed reply. I’m actually familiar with the Tuning Scale module, I use it myself, and I’ve even designed my own HALion libraries using it.

However, this is not what I meant in my post.

I’m referring to a simple Scale Lock feature, like the Scale Assistant in Cubase and what we now see in many modern instrument plugins — even some free ones.

Simply put: a list of musical scales, choose one, and then play only the notes that belong to that scale.

That kind of direct, user-friendly Scale Lock is what I believe HALion is still missing.

Like this lua script that I asked for in Copilot? It opened a window into Chat GPT and came up with this.

scale assistant lua script for halion
--[[
    HALion Scale Assistant 1.04
    Generated via Chat GPT on 9/9/2026
    Forces incoming MIDI notes into a selected scale.
--]]

-- Define available scales (intervals in semitones from root)
local scales = {
    ["Major"]         = {0, 2, 4, 5, 7, 9, 11},
    ["Natural Minor"] = {0, 2, 3, 5, 7, 8, 10},
    ["Harmonic Minor"]= {0, 2, 3, 5, 7, 8, 11},
    ["Pentatonic"]    = {0, 2, 4, 7, 9},
    ["Blues"]         = {0, 3, 5, 6, 7, 10}
}

-- Build sorted list of scale names for UI
local scaleNames = {}
for name, _ in pairs(scales) do
    table.insert(scaleNames, name)
end
table.sort(scaleNames)

-- Parameters exposed to HALion UI
defineParameter("rootNote", "Root Note", 0, 0, 11, function() end) -- 0 = C, 1 = C#, etc.
defineParameter("scaleName", "Scale", 0, 0, #scaleNames - 1, function() end)

-- Mapping: originalNote -> mappedNote
local noteMap = {}
-- Counter: mappedNote -> active count
local noteCount = {}

-- Helper: force note into scale
local function forceToScale(note, root, scaleIntervals)
    local noteInOctave = (note - root) % 12
    local closest = scaleIntervals[1]
    local minDiff = 12

    for _, interval in ipairs(scaleIntervals) do
        local diff = (noteInOctave - interval) % 12
        if diff < minDiff then
            minDiff = diff
            closest = interval
        end
    end

    return note - noteInOctave + closest
end

-- Handle Note On
function onNote(event)
    local selectedScale = scales[scaleNames[scaleName + 1]]
    if not selectedScale then
        postEvent(event)
        return
    end

    local mappedNote = forceToScale(event.note, rootNote, selectedScale)

    -- Store mapping for this original note
    noteMap[event.note] = mappedNote

    -- Increment count for mapped note
    noteCount[mappedNote] = (noteCount[mappedNote] or 0) + 1

    -- Send Note On for mapped note
    local newEvent = clone(event)
    newEvent.note = mappedNote
    postEvent(newEvent)
end

-- Handle Note Off
function onRelease(event)
    local mappedNote = noteMap[event.note]
    if mappedNote then
        -- Decrement count for mapped note
        noteCount[mappedNote] = (noteCount[mappedNote] or 0) - 1

        -- Only send Note Off when no more originals are holding it
        if noteCount[mappedNote] <= 0 then
            noteCount[mappedNote] = nil
            local newEvent = clone(event)
            newEvent.note = mappedNote
            postEvent(newEvent)
        end

        -- Remove mapping for this original note
        noteMap[event.note] = nil
    else
        -- Pass through if no mapping
        postEvent(event)
    end
end

function onInit()
    print("Scale Assistant Script Loaded (Chord-Safe)")
    print("Available Scales: " .. table.concat(scaleNames, ", "))
end

So remember, you don’t need to be a scripting/coding wizard to build things anymore. It took less than 10 minutes to get something that seems to be usable, test it in HALion, and post it here. All I did was enter plain English prompts into an AI (free access in this case, I got to it through a Copilot query).

I just gave it a try with rootNote as the default of 0 (C), and again at 5 (F), and it seems to work. This script is set up to play the closest note down if something out of scale is pressed. One could ask GPT to change that behavior, or to put in different options (play higher, play nothing, play a random note still in scale, etc.).

Since I have no macro established I found the parameters to tweak by opening a parameter list:

On scaleName value:
0 = Pentatonic
1 = Harmonic Minor
2 = Major
3 = Natural Harmonic
4 = Blues

I’m not sure if this script got the naming part right so scale names instead of a number would show up in macro pages. It seems like it does set up a table for that but I’m wondering if it could be done in the same pass as defining the parameter and having it ready to show up in a column there? It’s been a while since I personally made a script that set up a name field like that, so I can’t remember how to check that as I sit here.

If it got it wrong it shouldn’t be too hard to correct with a couple of prompts into the AI, or just tweak it to whatever your Macro editor needs.

It may be that there’s just not that much demand for it. I don’t think I ever stick to a scale, playing or recording.

Also, most keyboard controllers nowadays already have some sort of scale lock feature built in, which effectively eliminates the need of doing it in the plugin itself.