Norns display graphics

as i dip my toe in the norns pool, i find myself drawn to learning how the graphics system works.
norns/docs tipped me off to:

display_png (filename, x, y)

display png.


  • filename
  • x x position
  • y y position

looking for a bit more detail on the filename and expected file formatting.
i assume 128x64 (max) greyscale standard uncompress png.
should the code contain “.png” - and how do i specify path?
are x/y position the top corner of the image, or the center of the image?


This looks like a me question… :grin:

  • x/y is 0,0 on the display - same as text - top left corner

  • filename is a full path - so something like

screen.display_png("/home/we/dust/code/thing/thing.png", 0, 0)

and bob’s your uncle.

  • bigger images will just load into the space of the screen - they won’t resize to fit. (I suppose I should try negative/off-sceen coordinates to see what happens)

  • color pngs will work fine - cairo is doing the munging here

  • I don’t remember if I tested with compressed vs uncompressed. I don’t think it matters.

  • I don’t remember if I did much testing with layering things. I think the png will fill all it can, so try to add text after the display_png call.

  • this is likely very inefficient to load inside a loop or with heavy refresh rate.

I have some other goofy ideas/experiments for drawing sprites to animate. Let me know if you wanna see any of that (way more convoluted tho)


thanks this is all very helpful!

how about with smaller sprites that don’t fill the screen and run max 12fps?

totally interested! i want to make spritesssss

try it and see? I did zero stress testing on this :slight_smile:

yikes… now I have to go back and remember all the stupid file munging I did way back when. :stuck_out_tongue_winking_eye:

It was something along the lines of “create a 2 color png, read that into memory via a python script and create a pixel array in lua - then just fill pixels from the array.”

which is what I’m doing here:


been chewing on this very concept for a couple days now :slight_smile:


i’m having trouble implementing… so new to all this!


-- png demo

-- specify dsp engine to load: = 'TestSine'

function init()

screen.display_png("/home/we/dust/code/we/test/smile.png", 0, 0)


error message:

### SCRIPT ERROR: load fail

/home/we/dust/code/we/test/png-test:16: <eof> expected near 'end'

stack traceback:

/home/we/norns/lua/core/norns.lua:185: in function </home/we/norns/lua/core/norns.lua:185>

[C]: in function 'dofile'

/home/we/norns/lua/core/script.lua:140: in function </home/we/norns/lua/core/script.lua:140>

[C]: in function 'xpcall'

/home/we/norns/lua/core/norns.lua:186: in field 'try'

/home/we/norns/lua/core/script.lua:140: in function 'core/script.load'

(...tail calls...)

@shellfritsch you need to put the screen functions inside the redraw function

function redraw()

path tip:

you can use path.home for /home/we/dust and path.code for /home/we/dust/code

use .. to glue strings together


screen.display_png(path.code .. "we/test/smile.png", 0, 0)

(see others at


i think unfortunaely you want to be really restrained in your usage of a png-rendering function like that from any lua callback.

it is a great example of something that will peg the event loop (file access.)

if you really want to do sprites, we need functions to load a sprite sheet and blit different regions of it to the cairo surface from RAM.

this is also yet another application that would benefit from separate cairo thread and event loop (which is a perffecly straifghtforward concept, and also just a bit of a drag to actually add)


it’d make sense to add an additional frame buffer and a buffer copy… but this adds complexity to the whole system. will have to think about it.

1 Like

i agree, but i think even more importantly just implemen ‘blit from RAM’ first, ‘blit and swap’ later`

calling the png load multiple times from redraw() is really not great


thanks, that did the trick.


@tehn @zebra Just found a possible bug - trying to display_png a non-existent file looks like it crashes the display in some fashion (screen goes black, no other script will show anything) - requiring a ;restart

not sure how to get dbug for that though.

Filed issue #825

Also @shellfritsch sorry I left out the redraw() part of the equation. :sweat:


Hey all! I wanted to see where I may be going wrong with drawing a PNG to the screen while also displaying text to the screen.

In my sketch, all I have is a redraw() function since I’m still testing things out. Here is my entire code:

function redraw()

Which shows me the png in the bottom right corner and my text in the upper left. However, whenever I call redraw() (like when I press K1 to go in and out of the script), the text never draws again. Only the PNG shows up. Any thoughts on what I’m missing? I played with the order of drawing the PNG first, but that actually stops the text from displaying altogether.

Pictures for reference. Text and PNG at load:

PNG only and Text Blocked after redraw() called:

just a guess, but try explicit call to screen_level(15) before drawing the text. (or whatever level in [0, 15].)

(what is happening? under the hood, the call to display_png sets the cairo source to a surface composed of the image contents. for most of the screen drawing commands, you probably want to set a color source, which is what screen_level() does.)

this is not a great API behavior and we could call it a bug. @tehn @csboling @tyleretters etc: might be good to have display_png save and restore the source settings?)


i made an issue for it. i’ll look into this with this next round of screen improvements. screen_png bug · Issue #1491 · monome/norns · GitHub

i always call screen_level() in my setup functions and have never noticed this behavior before. good hypothesis.

1 Like

likely fix: save/restore for png-display by tehn · Pull Request #1492 · monome/norns · GitHub

tested and fixed. update release forthcoming or get newest main

1 Like

Sorry for the delay in getting back to you, but this worked like a charm! Thanks so much!