Florian Wesch

A new way of organizing code and assets

Posted Feb 29 2016 by Florian Wesch

info-beamer pi 0.9.5 will add a new optional way of organizing code and content of a node. This post describes how it works and what's possible with the new nested node feature.

Nesting child nodes

One of the core design ideas of info-beamer is the ability to nest nodes. So independant pieces of code could be used together for form a single visualization. In the early days of info-beamer this worked pretty well. I built multiple quite complex conference setups that way.

The basic idea is to have a single scheduling node at the top of your visualization and multiple child nodes that display different parts of it. The top level node could add common information like a clock or a running text. Child nodes would implement things like showing a twitter feed, showing pictures or conference scheduling infos.

This worked pretty well, but has a few downsides: Since child nodes were completely independant from the top level node, they couldn't reliably know if they are currently displayed as part of the top level node. A video child node for example could detect that it is being displayed, but it couldn't detect if the top level node switched to another child node. So it couldn't stop video playback.

For that reason my early visualizations used an external program (written in Python) to coordinate all the different nodes running in my conference visualizations. It would control the top level node and tell it when to switch to which child nodes. And it would control childs nodes and tell them to start and stop their display logic. This approach works but requires an external program.

Performance problems

Another problem of nested child nodes is that rendering a child nodes results in extra work for GPU: Calling resource.render_child renders a child node into an image object. This image object can then be rotated, scaled or moved like any other image object. Rendering a child node at 60 frame per second requires calling resource.render_child for each frame followed by drawing the resulting image object on screen. If you try to render multiple FullHD child nodes that way (for example during a transition between two of them) you probably won't get 60 frame per second as each child will first get rendered into an image object which is then rendered on screen. It would be great to avoid this extra rendering step.

Flat directories

Each node can only access files (for example images, videos, fonts or additional Lua code) in its own directory. This makes it very easy to share node code: Just zip the directory and you can be sure that everything required for your visualization is included.

But it also has its downsides: Everything has to be in the same directory. Your code couldn't access subdirectories except for rendering them with resource.render_child as described above.

Usually this flat structure isn't a problem as most visualizations either require only a small number of files, enforce a naming convention of files that makes thing easy to handle or are automatically generated, in which case you probably don't care what the resulting directory looks like.

But sometimes it gets messy. For a recent visualization for a big conference I tried to combine multiple parts of the visualization in a single node. There are a lot of benefits when doing that: The performance problems of using multiple nodes is gone and it's pretty easy to coordinate different parts of your visualization since everything is running in a single node within a single Lua environment. Additionally multiple parts of your visualization can work together in ways that are not possible when using child nodes, as those force you to use a squared area of a fixed size. The downside is that all files have to be in a single directory.


info-beamer pi 0.9.5 will have new function that makes it easier to organize your node code and assets if you run in any of the above problems. It it completely optional and existing code will of course continue to run unmodified. All you have to do is call node.make_nested(). Doing so will unlock child directories and their content to your node. You can use all resource loading functions to open any file within your node or any of its subdirectories. In addition to that the events child_add, child_remove, content_update and content_remove will now also be called for changes inside any of your node diretory or its subdirectories.

Content events

Lets look at the following code:

node.event("content_update", function(name)
    print("content update of "..name)

and the following directory layout:


Normally when starting info-beamer would result in the following output:

content update of playlist.txt

Touching child/childfile.txt wouldn't result in an event in the top level as its change is only delivered to the child node itself.

Changing the code to this

node.make_nested() -- New API call!

node.event("content_update", function(name)
    print("content update of "..name)

and starting info-beamer will now show that in addition to existing node events, events for files in child directories are now immediately delivered to your node as well:

content update of child/childfile.txt
content update of playlist.txt

Inside your event handler you can then access the file using the normal resource loading functions. Changing any file (for example by touching it) will immediately call the content_updated event handler again.

Likewise the content_remove event will notify you of any removed file in your node directory or any child directory.

Childs events

Of course info-beamer also notifies you of any directory changes that happen in any subdirectory. Lets have a look at this code:


node.event("child_add", function(child)
    print("child added: ", child)

If you create a subdirectory child/foo/bar you'll get these events:

child add: child
child add: child/foo
child add: child/foo/bar

If you wish you can now also directly draw any of your childs, even the nested ones, with resource.render_child. Of course that would cause the same performance problems described above.

Normally it makes sense to call node.make_nested() inside the top-level scope of your node.lua file, as seen in the examples above. That way the nested feature is activated when the node code loads and before any rendering happens. If you wish you can also call it later. In that case any node events related to subdirectories and their files will be delivered when calling node.make_nested().


The two global variables CONTENTS and CHILDS will always contain the current state of the filesystem inside the current node. You can query existing files and child directories.

The new node.make_nested function returns two tables containing the information about all contents and all childs.

ALL_CONTENTS, ALL_CHILDS = node.make_nested()

assert(ALL_CHILDS[''] == CHILDS)

ALL_CONTENTS and ALL_CHILDS contain information in the same way CONTENTS and CHILDS do, except for each child directory. So ALL_CONTENTS[''] is the same table as CONTENTS while, for example, ALL_CONTENTS['childs'] has information about all files in the childs subdirectory.

What's possible now

You can use the new superpower to build your own module system. Each subdirectory of your node could be a complete module that is more or less self contained and provides a defined interface on how to use it. A module loader can the dynamically load and unload modules as their directory is moved into the node directory.

Have a look at this example code of a module loader. Each subdirectory that contains a file called module.lua is handled by the module loader which provides life-cycle (loading/unloading) and content (file added/updated or removed) events that are automatically called. Additionally each module in the example code should offer a draw function which gets called for each module in the top level loop of the root node.lua.

The example code contains only a single example subdirectory which contains a minimal example of how to write a module. But you can just copy it to show-image, modify the draw function to draw an image and you already have two modules loaded at once that both draw to the screen. You can then enable or disable them by just moving the directory in or out of the top level directory.

Of course this module loader code is only one of many possible uses of the new node.make_nested feature. I'm sure other ways of using it to build a pluggable visualization system are possible.

The great thing is: info-beamer doesn't force a particular way of using this feature. You're free to adapt it so it solves your problem.

Hidden gems

  • You should be able to load more existing lua modules now, since require can now also access subdirectories. You could previously do that by combining those lua-only subdirectories in a zip file called luabundle.zip, but directly using them is probably easier in most cases.
  • You can ignore all additional benefits and just use this feature to put assets of your node in their own subdirectories.
  • If you have no idea why you should invest the time to learn this feature and you don't run in any problems described, you can just ignore it completely and revisit it later once your visualizations get more complex.

Call for feedback

The feature in its current state isn't finalized yet. But I'm pretty sure it solves a lot of problems that I and others have faced while building more complex visualizations. I would really like to get some feedback on it before I release a new info-beamer pi version which officially adds this feature. So please let me know any feedback (good, confused or bad) you have. It will help make info-beamer even better.


info-beamer.com offers the most advanced digital signage platform for the Raspberry Pi. Fully hosted, programmable and easy to use. Learn more...

Get started for free!

Trying out the best digital signage solution for the Raspberry Pi is totally free: Use one device and 1GB of storage completely free of charge. No credit card required.

Follow @infobeamer on twitter to get notified of new blog posts and other related info-beamer news. It's very low traffic so just give it a try.

You can also subscribe to the RSS Feed RSS feed.

Share this post:

Share using Twitter

Questions or comments?
Get in contact!