Pixel Painting for Norns (tools, workflows)


calling this a “tool” is really a big stretch, but I made it to quickly try graphics on the Norns and I did not find an alternative (yet).

HTML page on Codepen

Enjoy and feel free to improve :slight_smile:

  1. Enter Grid size
  2. Paint pixels
  3. Generate LUA


For this kind of workflow, I’d actually recommend creating small PNGs, perhaps with the free editor Piskel, loading them onto norns, and then calling screen.display_png.


Thanks for the suggestion.
I will see how it goes. My Norns and LUA career is going for about a week now :grinning:


Speaking of that workflow, i tried to look at how one would load a PNG on norns, and then read and manipulate its pixels. I couldn’t figure it out. This might be a way to keep images still as data.


would probably go like this

local pixels = screen.peek(…)
screen.poke(pixels, …)

Obviously this is pseudo code, but peek/poke are th functions you want to manipulate the actual bytes of the frame buffer


Ooh thanks! Sorry don’t want to hijack the thread of this cool tool.

this is cool. i really like the idea of building your own tools.

but i wouldn’t recommend going the “sprite as lua table” route for norns, though.

it’s a old trick from the past

storing sprites as static arrays is an old gamedev trick to speed up loading time at the sacrifice of program memory size.

it is not as useful as it used to be as on modern hw:

  • disk access time is significantly faster (faster disk, faster bus)
  • frequently accessed files are transparently cached

so 2 consecutive screen.display_png("my_sprite.BMP") have good chance of being as efficient as a single buff = screen.load_png("my_sprite.BMP") followed by 2 screen.display_image(buff).

why use screen.load_png then?

it may still be more efficient. but will that be critical in a script’s performance? not really.

the power of retrieving the sprite in a buffer var, w/ screen.load_png or screen.peek, is that it allows to manipulate programmatically that sprite and/or dynamically display only a sub-regions of it (cropping/scrolling).

BMPs are already sprite arrays!

a BMP file is basically a simple array of pixel values. it’s more optimized than an lua table (binary VS text).

norns’ screen.*_png functions support BMPs (as BMP standard is a subset of the PNG standard).

so using arrays for sprite is totally deprecated?

it depends.

PICO-8 still uses it but for different reasons (having all the ROM as a self-contained text file).

arrays are also quite useful for tilemaps, i.e. maps of sprites to be used as tiles to draw… a map.

software recommendations

i really really like Aseprite (commercial but worth it).

for tilemaps, Tiled (donationware) is pretty popular.

sometimes building you own tool is necessary: this presentation of the making of Fez is really inspiring.



Thanks for elaborating on this.
I come from the Commodore 64 days and just reimagined what I had in my old brain :smiley:
This array thing works for me now, but I’ll cherish your reply and will come back to it when I’ve learned even more.

Kind regards

1 Like

You can create a big file containing all your graphics then load it as an image in lua and then draw sections of it to the screen using…

image = Screen.load_png (filename)
Screen.display_image_region (image, left, top, width, height, x, y)

That makes it easy to clip things or scroll things

1 Like

Thanks. I have Aseprite ready to get going :sunny:

maybe i can piggyback a question onto this thread:
any tricks for drawing from .png straight to grid?
(also, i tried ‘screen.peek…’ but it seems to return a string(?.. when i use the output, the error says something like ‘number expected, got string’… i also tried to reference it as a 2-dimensional table, that doesn’t seem to work either), instead of a number between 0 and 15 as stated in the docs? how can i use screen.peek to get the strict numerical brightness values of each pixel? any examples of its use? perhaps an example of screen.peek applied directly to grid:led?)

1 Like

for screen.peek, the returned value is indeed a string, or more precisely a byte array encoded as a string (works as 1 pixel value (0xFF) = 1 byte = 1 char)

should go something like that (untested):

-- set x, y, width & height as wanted
local buff = screen.peek(x, y, x+width, y+height)

-- now we want pixel value at position (x2, y2) in the buffer 
local i = (y2 - 1) * width + x2 -- assuming x2 & y2 are 1-indexed
local pixel = string.byte(buff, i)

if you want to lookup only 1 pixel value:

local pixel = string.byte(screen.peek(x, y, x+1, y+1), 1)
1 Like

YES, I DID IT! :raised_hands: :smiley:

(although, my pixel-art drawing/design skills need work, i admit :rofl: )

aw, snapZ! i should’ve studied up the general lua more(there i was searching in the ‘util’ lib for something like ‘util.char2number’ :joy: )

i can’t thank you enough, @eigen …to me it’s not just the usage you’ve helped me with, but you’ve allowed my beginner-programmer mind to embrace the ‘model-view-controller’ paradigm more easily:
i really wanted the .png(or the lua table we store it in) to be the model, then i could think of the norns UI screen as the ‘view’(obviously), and a grid controller as… uh… well… the ‘controller’ :disguised_face: (seems obvious, i know, but now i’ll have an easier time implementing this level of organization :blush:)

and Thank You @Elektrik76 for starting such an awesome thread! :raised_hands:
(also, if i could make suggestions on your tool, because i still want to use it for smaller grids(anywhere between 3x3 to 8x8 designs seem just as easily handled directly by table(since you can see the values easily in a lua table, there’s an added benefit for beginners like me)) → if you could make it so we can drag across cells to draw multiple cells in one stroke, that’d speed-up the workflow so beautifully… → and a smaller request: add varibrightness field(we chose a varibrightness value between 1 and 15, and thereafter, any cells we draw will have that lighter shade))

Thank you, Both, Again :heavy_heart_exclamation:

oh and here’s my amateur lua code that got me working just for testing the idea out(not actually properly organized into something i’d clearly describe the way i did above about MVC, but i’m on my way):

function init()
  my_grid = grid.connect(1)

function uic()
  while true do
  buff=screen.peek(1, 1, 16,16)
    for x=1,16 do
      for y=1,6 do
      local j = (y - 1) * width + x
      local pixel = string.byte(buff,j)