Gengi - norns game framework

gengi (pronounced: gaen-ghee)

a norns game engine

This came to be as part of the development process for thumper. I’ve solidified some decent patterns for developing the basics of a game engine, and I wanted to share a clean little example of that while @Snail and I continue to hack away at something fun for y’all to play.

This repo will be updated as things happening in demos (for progress on these, see the thumper thread) prove useful and worth keeping around. Please feel free to hack on it as a way of contributing to the norns game dev ecosystem! The thumper thread is a bit of a mad-woman’s diary at the moment… Once we have a solid renderer, audio backends are sure to follow :slight_smile: . Then the game/instruments will start to appear!


  • one (1) norns


  • see the script notes for usage of the example script
  • how would I add a sprite or something else on screen?
    modify redraw() and add your drawing functions between clear() and update(). Check out the following code:
function redraw()
  • how do I switch between screens in control functions?
    this involves my dirty trick to keep the code so light:
    • rendering/key/encoder functions look up behavior based on global state stored in the game table
    • screens are an easy way to add text/graphic overlays tangled with key/encoder functions. A third screen could easily be added called “world”.
    • when you want to go to another screen, manipulate the game table. Consider this example:
game.screen[1].event = function(self,bool)
  if bool then 
    print("starting game!")
    -- 3rd screen contains the world    
    game.screen.selected = 3   
    -- assign a drawfunc so that the character gets drawn
    game.my_character.draw = game.my_character.draw.drawfunc 



Just thought of a screen for a level editor:

editor = {
  tiles = { my, tile, objects, selected=1 }
  tools = {
      name="tile picker",draw=function(self)
        set_screen_bright(); set_font_small()
        screen.move(0,10); screen.text_left(
      name="place",draw=function(self, xy)
        set_screen_dim(); set_font_small()
        screen.move(0,10); screen.text_left(
        set_screen_dim(); set_font_small()
        screen.move(0,10); screen.text_left(  
  xy = {1,1},
  draw = function(self)
    local tool =[]
    local tile = self.tiles[self.tiles.selected]
    set_font_small(); set_screen_dim()
    screen.move(0,10); screen.text_center(
  event = function(self,bool)
    local tool =[]
    local tile = self.tiles[self.tiles.selected]
    local x,y = self.xy[1],self.xy[2]
    if tool = "place" then
                -- game.screen[3] is the world
      if bool then game.screen[3].tiles[x][y] = tile end
    elseif tool = "erase" then
      if bool then game.screen[3].tiles[x][y] = nil end

Use your imagination for the key and encoder funcs but they basically just involve firing off events and scrolling whatever happens to be in focus :slight_smile:

This reveals a pattern for chaining draw function lookups combined with state. This is probably not great for performance in the long run, but eventually these calls can be optimized by composing these functions in clever ways.

Not sure if any of this is a good idea, but it’s a fun framework for really flexible UIs.


Not that I really understand much about it, but to me this seems like a great idea! :slight_smile:


I’ve not dug into this code yet, but a quick question - you mention sprites in the description. How should someone create a sprite? Is this a graphic file like png? some bitmap that’s read into a table?

I did some stuff like this last year but have completely forgotten all that. Hoping I can re-learn a bit from your code. :slight_smile:

I’m using the term loosely – Ive been just typing in tables of pixel values in the source code.

Theyre basically just drawing functions that draw something at a given offset.

1 Like