← all scripts / norns

norns-hud new

no description yet

undisclosed Imported automatically; the author has not disclosed AI use yet

install

In maiden's REPL (the >> prompt at the bottom of the editor), run:

;install https://github.com/vehka/norns-hud

Or add the splines catalog to maiden and install it from the project manager. Restart norns if the script ships a new engine.

readme from vehka/norns-hud

hud

Text and value editors drawn over a norns script's grid.

  • Help on hold: hold a grid key a little longer than usual and the grid shows what it does, or a value.
  • Popups: feedback without a keypress: SAVED, RANDOMISED, 120.
  • Editors: press the meta key (bottom-right by default), then a key, and that key's editor opens: a stepper (−10 −1 … +1 +10) or a row of choices. Meta blinks on the beat while it waits for the key; it stays lit while an editor is open. Press meta again to close.

While the hud is up the script's own LEDs are dimmed and the rows under it are switched off. Text that fits is centred and still; longer text scrolls. Hold text appears on the side of the grid away from your finger, preferring the top.

The font is 3×5 capitals with proportional widths: about four characters fit on a 16-wide grid, two on an 8×8. Lowercase is drawn as capitals; ♭, ♯ and ° are included.

Use

Copy lib/hud.lua into your script's lib/ folder, then wrap your grid:

local hud = include('lib/hud')

local g = hud.wrap(grid.connect(), {
  describe = function(x, y)            -- help text for a held key, or nil
    if y == 8 and x == 1 then return 'PLAY' end
  end,
  editor = function(x, y)              -- editor for meta + key, or nil
    if y == 8 and x == 15 then return hud.param('clock_tempo') end
    if y <= 7 then
      return hud.stepper{label = 'VEL', min = 0, max = 100,
        get = function() return vel[x] end,
        set = function(v) vel[x] = v end}
    end
  end,
})

g.key = function(x, y, z) ... end      -- unchanged
g:led(x, y, level); g:refresh()        -- unchanged
g:show('SAVED')                        -- popup

The wrapped grid works like the original: led, all, refresh, key, cols, rows, rotation, intensity and the rest. It keeps a copy of what the script draws so it can dim it and restore it afterwards. While nothing is shown, calls go straight to the grid. The meta key belongs to the hud: the script doesn't receive its presses and can't light it.

Editor layout (16×8)

rows 1-5   the value; the editor's label first, until a key is pressed
row  6     blank
row  7     stepper: [-10][-1] ▮▮▮▮▮▮▯▯▯▯▯▯ [+1][+10]   (tap the bar to jump)
           choice:  one key per option, the selected one bright
row  8     the script's grid, dimmed; meta lit

While an editor is open, the hud takes every key: row 7 adjusts, a visible key below opens that key's editor instead, meta closes. Keys held before meta was pressed still send their release to the script.

Editors

hud.stepper{label, get, set, min, max, step=1, big=10, format} Numbers. format(v) returns the shown text.
hud.choice{label, options, get, set, format} get() returns an index, set(i) takes one. More options than the grid is wide work as a stepper.
hud.toggle{label, get, set} OFF / ON; get() returns a boolean.
hud.param(id, {label, step, big}) Picks one of the above from the param's type: number and control → stepper, option → choice, toggle binary → toggle. Changes go through params, so PSETs, PMAP and param actions stay in step. For controls, ±1 is one fine step (1% of the range by default).

Every editor has :delta(d), for an encoder.

Methods

g:show(text, opts) Popup. text is a string or a function (re-read every frame). opts.time: seconds, nil (default) = long enough to read once, false = until g:hide(). opts.pos: 'auto' (default), 'middle', 'top', 'bottom', or a row number to keep clear of.
g:hide() Remove the popup.
g:edit(editor) Open an editor without meta, e.g. from K2.
g:close() Close the editor and meta mode.
g:editing() The open editor, or nil.
g:delta(d) Nudge the open editor's value; false if none is open. Handy in enc().
g:showing() true while the hud draws over the grid.
g:described(x, y) true if the key's help text was shown during its latest press. Check it on release to skip the key's action after a help hold.
g:set(opts) Change options.
g:cleanup() Stop the hud's clocks; call from your script's cleanup().
hud.width(text) Width of text in grid columns.

Options

option default
describe nil function(x, y) → text or nil. Re-read every frame while shown.
editor nil function(x, y) → editor or nil, for meta + key.
meta bottom-right {x, y} for the meta key; false for none. Only active when editor is set, unless given explicitly.
meta_level 4 Meta brightness while the hud is closed.
hold 0.5 Seconds before a held key's help appears.
dim 0.25 Brightness of the script's LEDs under the hud; 0 switches them off.
separator 'blank' Row between text and grid: 'blank', 'line' or 'none'.
level 15 Text brightness.
speed 10 Scroll speed in columns per second.
pause 0.6 Seconds still before scrolling.
gap 6 Columns between repeats of looping text.
popup_time 1.5 Seconds a popup that fits stays up.
fps 20 Frame rate while the hud is up.
block_covered false Ignore presses on keys hidden under hold text.
font 3×5 A custom font: {height = n, fallback = '?', glyphs = {[char] = {row strings}}}.

All key events reach your g.key while the hud is closed, including after a help hold. Act on release and check g:described(x, y) if a help hold shouldn't trigger the key (the demo does this).

Demo

hud.lua is a 16-step sequencer with PolyPerc and a cutoff LFO:

  • grid rows 1–7: notes (brightness shows velocity)
  • row 8: play, randomise, clear, undo · root, scale, LFO rate, cutoff, release · tempo, meta
  • hold a row 8 key: what it is and its value
  • meta + a step: that step's velocity; meta + a row 8 param key: edit it
  • E2 tempo, E3 root (or the open editor's value), K2 save PSET, K3 play/stop

Tests

lua5.3 tests/test_hud.lua

Runs the library against a fake clock, grid and paramset.

discussion

Like this script? React with 👍 or ❤️ below (needs a GitHub account). Reactions count as likes (♥) on splines, updated daily.