info-beamer Documentation


info-beamer is an interactive multimedia presentation framework. It is somewhat similar to fluxus or processing. info-beamer allows you to create impressive realtime visualizations using the Lua programming language. info-beamer is the player software used for the hosted service.

Installing info-beamer

info-beamer is available in two versions: The Open Source Version (also known as info-beamer) and the Raspberry PI version (also known as info-beamer pi). You can download the complete source code for the Open Source Version on github. Check the installation information if you need help getting started.

If you want to use info-beamer on the Raspberry PI, you can use the port of info-beamer which is available here. See the installation information on how to run info-beamer on your Raspberry PI.

First Steps

Hello World

info-beamer uses directories as presentable units. A minimal example consists of a single directory (called a node) containing a font file and a control file node.lua. Let's look at the example code in samples/hello:

gl.setup(1024, 768)

font = resource.load_font("silkscreen.ttf")

function node.render()
    font:write(120, 320, "Hello World", 100, 1,1,1,1)

Let's look at each line:

gl.setup(1024, 768)

This call will initialize a virtual screen of width 1024 and height 768. The virtual screen is the node's window to the world. The virtual screen is scaled up to the available display space.

font = resource.load_font("silkscreen.ttf")

This line will read the Truetype font silkscreen.ttf and create a font object font. This object can then be used to write output using the font.

function node.render()
    font:write(120, 320, "Hello World", 100, 1,1,1,1)

info-beamer will call the function node.render for each frame it will display on the screen. Inside of node.render it's up to you to decide what do show on each frame. In this example we use the previously create font object to write "Hello World" to the virtual screen.

The first two parameters of write are the x and y Position on the virtual screen. x is the horizontal position. A value of 0 is the leftmost position. y is the vertical position. The value 0 is the topmost position. Therefore x=0, y=0 is in the top left corner of the virtual screen.

"Hello World" is obviously the value we want to put on the virtual screen. 100 is the size of the output in screen units. 1,1,1,1 is color in RGBA format (a bright white).

To display this example using info-beamer, switch into the info-beamer source directory and type

user:~/src/info-beamer$ ./info-beamer samples/hello

This will start info-beamer. It will open the directory hello (called the hello node) and look for the file node.lua. This should get you a new window showing the "Hello World" text in the center to the screen.

info-beamer is a rapid development environment. If you update the code for your example and save it, info-beamer will pick up the changes and show them to you immediately. Let's try this: While info-beamer is still running and displaying the "Hello World", change the string "Hello World" to "Updated World" in node.lua and save the file node.lua. info-beamer will notice that the file changed and reload it. The output window will now show the text "Updated World" to you!

Displaying Content


info-beamer can load more than just font files. It will load various image formats using the resource.load_image function. Code using this function might look like this:

gl.setup(1024, 768)

background = resource.load_image("background.jpg")

function node.render()
    background:draw(0, 0, WIDTH, HEIGHT)

The image background.jpg will be loaded into the image object background. Inside node.render this background image is the drawn from coordinates 0, 0 (top left corner of the virtual screen) to WIDTH, HEIGHT (bottom right corner of the screen). WIDTH and HEIGHT will be initialized with the values from the gl.setup call.


You can load videos and display them. Doing so is quite similar to image loading. util.videoplayer provides an easy way to do this:

gl.setup(1024, 768)

video = util.videoplayer("video.mp4")

function node.render()
    video:draw(0, 0, WIDTH, HEIGHT)

util.videoplayer is a helper function that provides a small wrapper around video resources. It will automatically decode the video using the correct frame rate. Using the draw function displays the current video frame.

Rendering Child Nodes

A directory (called a node) can contain subdirectories. Each subdirectory is then loaded as a child node. The parent node can render child nodes like any other resource. Let's say we create two nodes called blue and red. We can let info-beamer play each of them individually. But what if we want to combine them for our presentation? This is where the nesting feature of info-beamer becomes useful. You start by creating another node called green. Then you just move the directories for node blue and red into the directory green. The file tree will the look like this:

-+- green -+- node.lua
           +- red ---- node.lua
           '- blue --- node.lua

Inside of green you can then render both red and blue into an image object using resource.render_child and display them on greens virtual screen.

The above setup is available in the directory samples/green. It contains node.lua and two child directories called red and blue. Let's look at green/node.lua:

gl.setup(800, 600)

function node.render()
    gl.clear(0, 1, 0, 1) -- green

    -- render to image object and draw
    local red = resource.render_child("red")
    red:draw(640, 20, 780, 580)

    -- render an draw without creating an intermediate variable
    resource.render_child("blue"):draw(50, 200, 300, 380)

It creates a new virtual screen sized 800x600. For each frame it clears the screen using gl.clear with a bright green.

Then it renders the child node red (aka the subdirectory red) into a new image object called red and draws it into the square from 640,20 to 780,580. The source code for green/red/node.lua looks like this:

gl.setup(100, 800)

function node.render()
    gl.clear(1, 0, 0, 1) -- red

It starts by setting up a 100x800 screen. Each time it is is rendered (which happens if resource.render_child is called), the child clears the screen by calling gl.clear with the color red.

green/blue/node.lua looks almost identical but clears the screen with a bright blue:

gl.setup(640, 480)

function node.render()
    gl.clear(0, 0, 1, 1) -- blue

You can start the example like this:

user:~/src/info-beamer/samples$ ../info-beamer green

You can also start both childs on their own:

user:~/src/info-beamer/samples$ cd green
user:~/src/info-beamer/samples/green$ ../../info-beamer red


user:~/src/info-beamer/samples/green$ ../../info-beamer blue

This is a great feature: You can develop nodes independently and later include them in other nodes.

Rendering from VNC

info-beamer contains another useful feature for including content. It can act as a VNC client. VNC is a cross platform desktop sharing protocol. info-beamer implements the client side of this protocol. If you setup a server on a remote machine, info-beamer can connect to this machine and create a VNC object you can use to render the remote desktop in your presentation. Here is an example:

gl.setup(1024, 768)

vnc = resource.create_vnc("")

function node.render()
    vnc:draw(0, 0, WIDTH, HEIGHT)

This will try to create a connection to a running VNC server on The server must not be password protected (since info-beamer doesn't support any kind of authentication). If you create the server, be sure that you are in a secure environment. Inside node.render the content of the remote desktop is drawn onto the screen.

Using GLSL Shaders

info-beamer supports GLSL shaders. Shaders are small programs that run on the GPU. They enable various realtime effects using the raw power of your GPU. You can use fragment shaders with info-beamer. Fragment shaders calculate the color displayed for each visible pixel of rendered objects. They can be used to create stunning realtime effects. info-beamer enables you to pass numeric values and additional textures into the shader. This allows you to do all kinds of crazy stuff like for example blending videos with static textures.

samples/shader contains a small basic shader example:

gl.setup(640, 480)


function node.render()
        Effect = math.cos(*2)*3
    lua:draw(120, 40, 520, 440)

util.resource_loader is a utility function that makes resource loading very easy. You just give it a number of filenames. It will then detect which loader is responsible for the given file format and load the file into a global variable whose name is derived from the filename. The above code will load the image lua.png into the global variable lua. It will also load the shader shader.frag into the global variable shader. The resource loader will also make sure that changed files will be reloaded. So if you edit and save for example shader.frag, info-beamer will instantly reload the shader. You can see changes to your effect immediately. This is great for rapidly developing effects.

Inside of node.render we first clear the screen. Then we activate the shader, which was automatically created from the file shader.frag by util.resource_loader. We pass in a variable Effect which depends on a time value. Finally, we draw lua.png with the applied shader. This will create a dynamic effect.

Reference Manual

Resource Loading

image = resource.load_image(filename)

Loads the image file specified by filename into a texture object. Please note that image loading is case sensitive. info-beamer supports loading JPEG and PNG images.

On the PI: The maximum resolution of images that can be loaded is 2048x2048 pixels. Images bigger than that cannot be loaded into textures as that is a hardware limit of the PI.

You can use an openfile object instead of a filename. See resource.open_file.

Be sure to have a look at the blog post about garbage collection to make sure that your don't run out of memory when loading many images.

The returned image objects supports the following methods:

image:draw(x1, y1, x2, y2, [alpha, [tx1, ty1, tx2, ty2]])

Draws the image into a rectangle specified by the give coordinates.

alpha specified the opacity (1 being fully opaque, 0 being fully transparent). If not specified it defaults to 1.

The PI version supports 4 addition parameters that specify the texture coordinates to use when drawing. The default values are 0, 0 and 1, 1, which will draw the complete image from top left to bottom right.

width, height = image:size()

Returns the size of the image. Notice that you have to wait till the image is fully loaded before using this call otherwise you'll get zero for both values. Use :state() to find out if the image is fully loaded.


The current state of the image.

"loading" 0 0 While the image is loading
"loaded" width height Once the image is fully loaded
"error" "error message" If image loading failed

It's best to call this function each frame till the expected state is reached.


Only available for the PI version. Removes all resources allocated for this image. Using the image after calling dispose will return an error. This is mainly useful in combination with the slow_gc flag.

video = resource.load_video(filename, play_audio, looped, paused)

Loads any supported video file and returns a video object.

The play_audio flag is only available in the PI version. If set to true, info-beamer will play the audio track in the video.

You can use an openfile object instead of a filename. See resource.open_file.

If you start the video in paused mode you can use video:start to start it.

Be sure to have a look at the blog post about garbage collection to make sure that your don't run out of memory when loading multiple videos.

The video objects supports the following methods:

video:draw(x1, y1, x2, y2, [alpha, [tx1, ty1, tx2, ty2]])

Draws the current video frame into a rectangle specified by the give coordinates.

alpha specified the opacity (1 being fully opaque, 0 being fully transparent). If not specified it defaults to 1.

The PI version supports 4 addition parameters that specify the texture coordinates to use when drawing. The default values are 0, 0 and 1, 1, which will draw the complete image from top left to bottom right.

has_next_frame = video:next()

On the Open Source Version: Decodes the next frame of the video. Returns true, if a frame was decoded or false if there was no next frame.

width, height = video:size()

Returns the size of the video. Will return 0, 0 if the video is not fully loaded. It's recommended to use video:state instead.

fps = video:fps()

Returns the frame per seconds as specified by the video file.


On the PI it's possible to preload a video in a paused state. Playback won't start until video:start is called.


The current state of the video.

"loading" 0 0 0 While the video being loaded
"paused" width height The video is ready to be played
"loaded" width height Once the video is being played
"finished" width height Once the video playback finished. You should not use the video object any more.
"error" "error message" If video playback or loading failed

It's best to call this function each frame till the expected state is reached.


Only available for the PI version. Removes all resources allocated for this video. Using the video after calling dispose will return an error. This is mainly useful in combination with the slow_gc flag.

font = resource.load_font(filename)

Loads the given font file (in TTF or OTF format) and returns a font object. It supports the following methods:

You can use an openfile object instead of a filename. See resource.open_file.

width = font:write(x, y, text, size, r, g, b, [a])

Writes the provided text to the coordinates given in x and y. The color is given by r, g, b and a, the red, green, blue and alpha values. All values are expected to be between 0 and 1. The alpha value is optional, the default is 1.0 (opaque). The call will return the width of the rendered text in screen space.

text must be UTF8 encoded, if you intend to use characters outside the ascii range.

width = font:write(x, y, text, size, texturelike)

Only available on the Open Source Version: Mostly identical to font:write but will not use a solid color but the texturelike object. texturelike can be an image, a video or any other texturelike objects. The texture will be used for each character individually.

width = font:width(text, size)

Returns the width of the text when rendered with the given font size. This method can be used to find out the text width without rendering it first.

Available since info-beamer pi 0.8

string = resource.load_file(filename)

Will load the content of the specified file into a string value.

You can use an openfile object instead of a filename. See resource.open_file.

shader = resource.create_shader(fragment_shader)

Will create a new shader object. fragment_shader is the string containing the fragment part of the shader in the GLSL language.

The following varying and attribute values are defined withing the fragment shader:

uniform sampler2D Texture; // Incoming texture surface (image/video)
varying vec2 TexCoord; // Texture coordinate
uniform vec4 Color; // Active color/alpha (e.g. while drawing text)

The identity fragment shader is:

uniform sampler2D Texture;
varying vec2 TexCoord;
uniform vec4 Color;
void main() {
    gl_FragColor = texture2D(Texture, TexCoord) * Color;

Within the shader, the following defines are available:

// Active in all info-beamer glsl shaders

// Depending on the platform info-beamer is running on:


Will activate the shader and pass in the given variables. A call might look like this:

    float_value = 123.45,
    some_texture = image,
    some_color = {1, 0, 0, 1},

You can pass in numerical values. Inside the shader, they will be available as a float uniform values:

uniform float float_value; // will have the value 123.45

Textures (which can be images, videos or any other texturelike object) will be available as a 2d sampler:

uniform sampler2D some_texture;

You can also pass in table values with 2 to 4 elements. Those will be available as a corresponding vec{2,3,4} type. This can be used to pass in colors for example:

uniform vec4 some_color;

The shader will be active for the rest of execution of the node.render call unless deactivated using shader:deactivate.


Deactivates the active shader.

vnc = resource.create_vnc(hostname, [port])

This call will create VNC client. It will connect to the specified address. The remote desktop will be available as a texturelike object. If no port is given, the default value of 5900 will be used.

vnc:draw(x1, y1, x2, y2)

Draws the current remote desktop to the screen.

width, height = vnc:size()

Returns the size of the remote desktop. Will return 0x0 is the client is not yet connected to the VNC server.

alive = vnc:alive()

Returns a boolean value that indicates if the client is still connected to the server. If the client was disconnected (which can have various reasons like an invalid hostname, a closed connection or invalid VNC packets) it will not automatically reconnect. It's up to you to create a new client if you need a persistent connection.

image = resource.render_child(name)

Renders a child node into an image object. Rendering will call node.render within the child.

The returned image supports the same methods like images objects created by resource.load_image.

For performance reasons you should minimize the usage of childs nodes when using info-beamer pi. If you use render_child you create a new texture object that you have to draw. This means that there are two memory transfers required to render a child: First rendering the child into a texture and finally rendering that texture to be visible. It is probably a better idea to only have a single node and use different Lua modules to organize your code. You can see how that might work in this example project.

If you intend to use render_child, be sure to clean up the resulting image object using :dispose(). Otherwise you will run out of GPU memory in seconds. Have a look at a blog post covering how garbage collection works in info-beamer.

image = resource.create_snapshot([x, y, width, height])

Copies the current framebuffer output (all things drawn up to this point) into a new image. This allows you to feed the output of the node into a shader in the next iteration.

This function can only be called inside of node.render (or any functions called from there).

The returned image supports the same methods like images objects created by resource.load_image.

You can omit all parameters to capture the complete rendered output of the current node. If you specify all parameters it allows you to only capture parts of it. For example calling resource.create_snapshot with the arguments 0, 0, WIDTH, HEIGHT will capture the complete content rendered. The captured region must be within the dimensions set by gl.setup.

openfile = resource.open_file(filename)

You can open a file and keep the open file around. You can use this openfile object instead of a filename in functions like resource.load_image or resource.load_video.

If you have an openfile object you can use it later to load a resource even if the underlying file has been deleted.

The returned openfile objects supports the following methods:

new_openfile = openfile:copy()

An openfile object can only be used once as an argument to a resource loading function. Once used, the openfile object cannot be used again. If you want to use the same object again later you have to create a copy before you use it. Use this pattern:

local image = resource.load_image(file:copy())


Closes the file. You cannot use this object to load resources once it's closed.

OpenGL Related Functions

gl.setup(width, height)

Initializes the virtual screen of the current node. Will also update the global variables WIDTH and HEIGHT. You can change the screen size later from any context except node.render.

gl.clear(r, g, b, a)

Clears the virtual screen using the given color. All values are expected to be between 0 and 1. Calling gl.clear also deactivates the active shader.


Like the native glPushMatrix call, this will save the current ModelView matrix. This function can only be called inside of node.render or any functions called from there. To avoid errors, you can only push 20 matrices. This should be more than enough for normal visualizations.

The term matrix stems from OpenGl. They capture the current transformation that is applied to all render functions. Calls like gl.translate, gl.rotate and gl.scale change the transformation matrix so that subsequent calls to :draw are translated, rotated or scaled. You can use gl.pushMatrix to save a transformation state, then do some transformation, draw some images and later restore the state using gl.popMatrix.


Restores a previously saved matrix. Can only be called inside node.render or any function called from there.

gl.rotate(angle, x, y, z)

Produces a rotation of angle degrees around the vector x, y, z. Consider using gl.perspective to see the scene in perspective mode (aka "real 3D"). It might look better.

gl.translate(x, y, [z])

Produces a translation by x, y, z. z is optional and defaults to 0. Consider using gl.perspective to see the scene in perspective mode. It might look better.

gl.scale(x, y, [z])

Scales by the given factors. z is optional and defaults to 1.


Resets the view to an orthogonal projection. It will create a projection where the top left pixel of the virtualscreen is at 0, 0 and the bottom right pixel is at WIDTH, HEIGHT. This is the default mode.

Calling this function will discard all matrices pushed by gl.pushMatrix().

gl.perspective(fov, eyex, eyey, eyez, centerx, centery, centerz)

This will create a perspective projection. So objects in the distance appear smaller which won't happen in the default orthographic projection.

The field of view is given by fov. The camera (or eye) will be at the coordinates eyex, eyey, eyez. It will look at centerx, centery, centerz. The up vector of the camera will always be 0, -1, 0.

Here is a useful example if you want to switch from gl.ortho to gl.perspective. It sets up a projection that closely matches the way gl.ortho sets up its projection matrix. So 0, 0 is in the top left corner while WIDTH, HEIGHT is in the bottom right corner:

local fov = math.atan2(HEIGHT, WIDTH*2) * 360 / math.pi
gl.perspective(fov, WIDTH/2, HEIGHT/2, -WIDTH,
                    WIDTH/2, HEIGHT/2, 0)

Calling this function will discard all matrices pushed by gl.pushMatrix.

Misc Functions

Returns a timestamp as a floating point number that will increment by 1 for each passing second. The timestamp is relative to the start of info-beamer. The timestamp only increments at the start of the currently handled event (frame rendering, UDP/TCP handling, etc). So you cannot measure duration inside those events. Of course you can measure time between events.


Exits info-beamer. Only available on the PI version. It's disabled unless you define the environment variable INFOBEAMER_ALLOW_EXIT=1.


You can pass values from environment variables into your Lua code. If you define a environment variable INFOBEAMER_ENV_XXX you can access its value by calling

local value = sys.get_env "XXX"

Of course you can replace XXX with any other string. Normally it's a better idea to just use JSON files and load them using util.file_watch.


A normal Lua string value. Will contain the string value pi on the Raspberry pi and desktop on the opensource/desktop version.


A normal Lua string value containing the version number of info-beamer/info-beamer pi.

Node Functions


You should overwrite this function with your own code. This function will be called by info-beamer (or by a parent node using resource.render_child). It should create the current scene by (for example) writing text (see font:write) or drawing images or videos (see image:draw and video:draw).


Nodes always have a name and a path. The name is the directory name of the node. The path is the full path to the node from the toplevel node.

If you send data to a node using TCP (see the input event) or UDP (see osc and data events), you address the node using its full path. Using node.alias, you can give your node an alias name. This name must be unique in a running info-beamer instance.

If you provide an empty string as new_alias, this node will act as a catch-all node that receives all events unless other nodes are a better match.

hid = node.event(event_name, event_handler)

Registers a new event handler. Possible event_names are described below. Event handlers will be called in the order of registration.

Event handlers can be unregistered since info-beamer pi 0.9 using node.event_remove. Save the hid (handle id) value and provide it to node.event_remove to remove a registered event handler.

node.event_remove(event_name, hid)

Removes an event handler previously registered by node.event.

node.set_flag(flag, [status])

Sets runtime flags that change some behaviour. Only available on the PI version. The following flags are available:

  • slow_gc: Disables the per frame garbage collection and switches to a more relaxed GC mode. This might give you a better performance in exchange for more memory usage. If you activate this, be careful: You'll have to manually dispose rendered child nodes (using :dispose()). Otherwise they will be created faster than the garbage collector can remove them. See a blog post about this.
  • no_clear: Removes the implicit gl.clear call while rendering a node.
  • close_clients: When set, info-beamer will disconnect all connected clients if the node code (node.lua) is reloaded. Only available on the PI version.
  • preserve: When set info-beamer will preserve the currently rendered output for the next frame. So you can overdraw outdated areas instead of drawing everything each frame. Settings this option has a performance penalty. Only available on the PI version.
  • no_jit: Disable the just in time compiler. Only available on the PI version.

status is true by default (and will activate the flag). Use false to deactivate a flag again.

node.client_write(client, data)

Write data to a connected client. See the node.event/connect, node.event/disconnect and node.event/input node events. This can be used to create interactive command line interfaces for your node. See the sample node in samples/parrot for a short example.


Closes a connected client. Won't trigger the node.event/disconnect callback.


Resets the node error status. Every time a node raises an error that is not intercepted by pcall its error status is set. You can see the error status in the second column of the flag information in the console output.

Node Events

info-beamer allows you to listen to various events. All events must be registered using node.event. The following events are available:

node.event("child_add", function(child_name) ... end)

Registers an event handler that is called if info-beamer detects that a child node was added to the current node. The name of the new child node is provided in child_name. Example usage:

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

node.event("child_remove", function(child_name) ... end)

Registers an event handler that is called if info-beamer detects that a child node was removed from the current node. The child name is provided in child_name.

node.event("content_update", function(filename) ... end)

Registers an event handler that is called if info-beamer detects that a file was created or modified in the current node. This allows you to detect updated resources. While you can handle these events manually you can also use higher level functions like util.file_watch, util.auto_loader or util.resource_loader.

Please notice: There is always going to be a race condition between this event being delivered and the state of the file, since it is possible that the file was deleted or changed again before the event got delivered.

If you can, you should always update files atomically: Create them with a temporary name and rename them to their final name once the file is completely written. info-beamer will also detect that and since the file content is replaced atomically you'll never run into half-written files. This also avoids problems when you have an external process that updates files while your info-beamer visualization is starting up: Functions like util.file_watch, util.auto_loader or util.resource_loader initially load the specified/detected files. If your external process is concurrently writting those, resources might not get loaded correctly.

If you have fast updating resources you could use resource.open_file here to pinpoint a file for later usage. Of course that doesn't help against half written files. Only atomically replacing them will. See also the discussion on this issue.

node.event("content_remove", function(filename) ... end)

Registers an event handler that is called if info-beamer detects that a file was removed from the current node.

node.event("data", function(data, suffix) ... end)

Registers a new event handler that will be called if UDP data is sent to the node. You can send udp data like this:

user:~$ echo -n "path:data" | netcat -u localhost 4444

Where path is the complete path to the node (in case of nested nodes) and maybe a suffix. data is the data you want to send. info-beamer listens for incoming UDP packets on port 4444.

info-beamer will dispatch packets to the node that best matches the specified path. Let's say you have two nodes:


If you send a packet to nested, the data callback will be called in the node nested. If you send a packet to nested/child/foobar the data callback will be called in nested/child. suffix will have the value foobar. See util.data_mapper for an easier way to receive and dispatch udp data packets. You can give your node a unique alias name using node.alias. This might be useful if you use OSC clients that don't support changing the paths they create.

node.event("osc", function(suffix, ...) ... end)

info-beamer also supports OSC (open sound control) packets via UDP. If you send an OSC packet containing a float to node/slider/1, the osc callback will be called with the suffix slider/1 and the decoded osc values. See util.osc_mapper for an easier way to receive and dispatch osc packets.

node.event("input", function(line, client) ... end)

info-beamer allows incoming TCP connections to port 4444. You'll be greeted by a welcome line and are expected to provide a node name. info-beamer will return ok! if you provide a valid node name. From this moment on, info-beamer will feed you the output of the node. This can be used for debugging a node from remote.

user:~$ telnet localhost 4444
Info Beamer 0.2-beta.fdd72a ( Select your channel!
[node output]

Any text you type while connected will trigger the input event. The input event will be given the provided line. This can be used to feed a node with input from outside sources. The second argument client is an opaque value that can be used to differentiate between multiple clients connected to the same node. See the node.event/connect and node.event/disconnect event.

You can also save the client value for late use by the node.client_write function.

node.event("input", function(line, client)
    print("Input was: " .. line)

    -- send something back to the client that sent us data
    node.client_write(client, "data was received")

node.event("connect", function(client, prefix) ... end)

Once a TCP client connects to the info-beamer and selects the node, the connect event will be called. The first argument provided is an opaque client value. This value is also provided by the node.event/input and node.event/disconnect event and can be used in combination with e.g. coroutines to handle clients connections as a session. See the sample node in samples/parrot for a short example.

The prefix argument specifies the path suffix a client might have provided while connecting to this node.

node.event("disconnect", function(client) ... end)

info-beamer will call this event once a TCP client disconnects from the current node. Its only argument is an opaque client value. See node.event/input and node.event/disconnect for more information.

Utility Functions

All utility functions could be rewritten using the functions described above. They are provided for your convenience.

When using info-beamer pi, you can run

$ info-beamer -d userlib.lua

to see the source code for all of the following functions. For info-beamer you can browse the source.

target = util.resource_loader(table_of_filenames, [target])

Creates a resource loader that will load the resources from the given filenames and put them in global variables. Example usage:


This will load the font font.ttf and put the font object into the global variable font. The global variable image will contain the image object. And so on.

The util.resource_loader will also detect changes to the files and reload them.

By default the resource loader will make loaded resources available as global variables. If you want to load resources into a custom table, just provide that table as the optional second argument. The return value of the resource loader call is the table the resource loaded uses.

Please also read the notice for the node.event/content_update function regarding safely replacing files.

target = util.auto_loader([target])

Creates a resource loader that tries to automatically load all files found in the node's directory. It will put the loaded resources into the table given by target. If no table is provided, the auto_loader will create a new table and return a reference. Use it like this to autoload resources into the global namespace:


-- if a file some_image.jpg existed, it is now available
-- as a global variable:

Or if you want to avoid name collisions with existing global variables, you can use auto_loader like this:

local resources = util.auto_loader()

-- See all resources loaded/loading

util.auto_loader initializes resource loading before returning and watches for changes afterwards. Since image and video loading as an asynchronous operation, it might take a moment for the resources to be usable.

Please also read the notice for the node.event/content_update function regarding safely replacing files.

util.file_watch(filename, handler)

Registers a handler that watches a file. The handler will be called once while calling util.file_watch and every time the file changes. The handler will receive the content of the file as a string. util.file_watch can for example be used the keep variables in sync with a config file:

util.file_watch("banner.txt", function(content)
    text = content

function node.render()

You can trivially load JSON files that way and store their content in a Lua variable:

local json = require "json"

local config
util.file_watch("config.json", function(content)
    config = json.decode(content)

-- config data is now available in variable 'config'

It's only intended to be used for loading text files which can be parsed immediately in the callback. So it should not be used to detect, for example, changing images files. Use node.event/content_update for that.

Please also read the notice for the node.event/content_update function regarding safely replacing files.


Loads the vertex and fragment shader from two files called basename.vert and basename.frag and returns a shader objects.

util.set_interval(interval, callback)

Adds a callback that will be called immediately and then each interval seconds if possible. The callback will only be called if the node is rendered: So it must be either the toplevel node or a node rendered with resource.render_child.

util.post_effect(shader, shader_opt)

Applies a shader effect to the current node output. This works by snapshotting the current output using resource.create_snapshot, clearing the output and drawing the snapshot with the given shader and its options.


Will create a OSC mapper that makes if simple to dispatch OSC messages to different functions. Think of it as Url-routing for OSC messages:

    ["slider/(.*)"] = function(slider_num, slider_arg1, ...)
    ["fader/1"] = function(fader_args)

The example will allow the node to receive two different type of OSC messages. If the node is called example, the following OSC path will trigger the slider callback function:


In the callback, the argument slider_num will be a string containing the value 123 while slider_arg1 will contain the first OSC argument.


Provides the same functionality as the osc_mapper, but handles simple UDP packets.

video = util.videoplayer(filename, [opt_table])

Provides a small wrapper around resource.load_video. Provides simplified playback of videos by handling framerate issues. opt_table is an optional table containing the following keys:

loop - it is a boolean value that indicates if the videoplayer should loop the video. It defaults to false.

audio - a boolean value to tell info-beamer to play audio too. This flag is only available in the PI version. It defaults to false.

paused - a boolean value to tell info-beamer to load the video but not play it yet. This flag is only available in the PI version. It defaults to false.

The behaviour between the desktop and the raspberry pi version differs in regards to playing videos:

On the PI version, videos will still play, even if the node using the video is not rendered by its parent node.

util.videoplayer will return a video object that has the following method:

video:draw(x1, y1, x2, y2)

Draws the current video frame into a rectangle specified by the give coordinates.

util.draw_correct(obj, x1, y1, x2, y2, alpha)

It is often necessary to display images, videos, child nodes or vnc streams using the correct aspect ratio (so they don't look stretched). util.draw_correct does this for you.

-- maybe wrong, stretches the image
image:draw(0, 0, WIDTH, HEIGHT)

-- keeps aspect ratio
util.draw_correct(image, 0, 0, WIDTH, HEIGHT)

st = util.screen_transform(rotate, [mirror])

When using the normal orthographic projection you can use this function to rotate and optionally mirror the screen. Basically this function will move the 0, 0 point to another corner of your screen.

The function will return another function you have to call inside node.render.

rotate can have the values 0, 90, 180 and 270 and specifies the clockwise rotation.

mirror is an optional boolean value that specifies whether the output should be mirrored. This might be useful if you're using some kind of back projection setup. By default the output isn't mirrored.

local st = util.screen_transform(90)

function node.render()
    -- your normal code here

files = util.open_files(filenames)

This helper function allows you to use resource.open_file on multiple files. The function conforms to the normal Lua error semantics. If all requested files can be opened you'll get two values: A boolean true value as well as a list of openfile objects. If opening any file fails it returns false as well as the error message.

local ok, files = util.open_files{"video1.mp4", "video2.mp4"}
if ok then
    -- do something with the files list
    print("error opening all files: " .. files)

Bundled third-party libraries

info-beamer provide access to a few bundle lua libraries that make development easier.


Pretty print the given Lua object. This function is automatically available, so you don't have to require it.


info-beamer provide the Lua CJSON library. Just require the json module.

local json = require "json"
pp(json.decode' {"foo": "bar"} ')


Often external sources provide data in the UTF8 encoding. So info-beamer bundles the luautf8 module.

Global Variables


Current width of the virtual screen as set by gl.setup. Writing to this variable has no effect. Use gl.setup to change the virtual screen size.


Current height of the virtual screen as set by gl.setup. Writing to this variable has no effect. Use gl.setup to change the virtual screen size.


This variable contains the native screen width. You can use it in gl.setup to set the resolution of the virtual screen to match the resolution of the currently configured video mode.


Same as NATIVE_WIDTH but contains the native screen height.


Name of the current node (its directory name).


Complete path of the node.


Table of available files in the directory of the node. The key contains the filename, the value is the timestamp (comparable to of the last change.


Table of child nodes. The key contains the childs name. These can the rendered using resource.render_child. The value is a timestamp (comparable to of the first detection of the child node.



Is it possible to run the info-beamer in fullscreen mode?

Yes. Just define the environment variable INFOBEAMER_FULLSCREEN to any non-empty value.

user:~$ INFOBEAMER_FULLSCREEN=1 info-beamer xyz

Port 4444 is already in use. How can I use another port?

Set the environment variable INFOBEAMER_PORT. The specified port will be used for both TCP and UDP.

How do I get the current time?

You probably tried calling os.time and it failed. The reason for that is, that this function might not be exported into the Lua environment of your info-beamer version. So there's no way to get the current time.

The reason for that is, that time is a complex thing. I didn't want to include a full featured time library into info-beamer when other programming languages already provide this feature. info-beamer does a few things and does them well. Time is not one of them.

So the way to get time into info-beamer is to have an external program fetch the time and send it to info-beamer. info-beamer not only makes this extremely simple - it was designed for that! It's always better to do complex calculations outside of info-beamer and rely on other programming languages that provide richer set of libraries and then just send the information that's needed for visualization to info-beamer. This also ensures that the visualization isn't slowed down, since all the calculation happens in another process than can run on a lower priority.

Having an external program send the time into info-beamer also makes it possible to test your code without adding complexity to your node code: Just send it different time values and you can see how it is handled.

For an example on how to send the time to info-beamer, see the code for an analog clock.

How can fetch content from HTTP?

You can't do that from within your node code. info-beamer doesn't support HTTP and likely never will, since there is a better way to do this: Just use an external program of you choice to do the HTTP requests. Then write the result into files within the nodes directory. The node can then pickup these files to display them.

This also makes the node stateless: Since the files are persisted in the filesystem, restarting the info-beamer won't lose any information.

Can I use existing Lua code or libraries?

Maybe. The Lua environment that is provided for a node is sandboxed. All potentially harmful functions (like os.execute) have been removed.

require has been wrapped to only load Lua modules from the current directory. Most simple Lua modules should work out of the box. Here is a JSON example:

Download the JSON module and put in your node directory. From within your node code you can then require this module, just as you would in normal Lua code:

json = require "json"
print(json.encode{foo = "bar"})

Native modules or precompiled modules cannot be loaded.

Can I automatically load json / lua files?

Yes. The auto_loader uses the table util.loaders. Entries in this table map filename suffixes to loader functions. These functions are called with a filename and are expected to return a value or raise an error.

To automatically load json files, use the following code:

json = require "json" -- see previous faq

util.loaders.json = function(filename)
    return json.decode(resource.load_file(filename))

-- invoke the auto loader

To automatically parse and evaluate all lua files except those that where previously loaded using require, use the following code:

util.loaders.lua = function(filename)
    local name, suffix = filename:match("(.*)[.]([^.]+)$")
    if package.loaded[name] then
        error("ignoring " .. name .. ": already loaded as module")
    return assert(loadstring(
        resource.load_file(filename), "=" .. PATH .. "/" .. filename

-- invoke the auto loader

I don't like Lua. Why not Javascript/Python/other language of your choice?

Lua is a fast. This is important, since the function node.render is called for each frame. Lua is small and simple. If you did any programming in another language, you should be able to learn the Lua basics in a few hours.

Lua can be sandboxed. Memory and CPU usage can be restricted. Multiple Lua environments (one for each node) can be created. This isolates nodes from each other and provides a clean environment to develop your code. If your node runs on your machine, you can be sure it'll run on any other info-beamer.

External scripting is an important part of the info-beamer. It's perfectly valid to do part of your nodes logic in an external script (for example to fetch data from twitter), write the data to a file and use the info-beamer to visualize the content. Instead of files you can also use UDP, OSC or TCP to update your node from any language.

Raspberry PI Version

info-beamer has a version available for the Raspberry PI called info-beamer pi. You can download it on the info-beamer pi download page.

info-beamer for the Raspberry was tested on Raspbian. If you use other distributions installing the dependencies might differ.

Setting up your PI

info-beamer uses quite a lot of GPU memory for loading images, videos, fonts and to smoothly draw on the screen. This all adds up.

It is strongly recommended to increase the memory available to the GPU. Edit /boot/config.txt and increase gpu_mem. A minimum of 192MB is recommended.

So change the gpu_mem line in /boot/config.txt to this:

gpu_mem 192

Once you have changed this setting, reboot your PI to activate it.

Update to the latest PI firmware

If you're using an older Raspbian installation, be sure to run a current firmware. To install a current firmware, run rpi-update. You have to restart your PI after that.

Prerequisites on Raspbian

Please have a look at the README.txt that is included in the info-beamer pi download. All required dependencies and instructions on how to install them are included. Don't worry, it's only a single command line.

Running info-beamer

Download the latest info-beamer release and unpack it in any directory. That's all. Your ready to run your first visualization.

user:~$ tar xfvz info-beamer.tar.gz
user:~$ cd info-beamer-pi
user:~/info-beamer-pi$ ./info-beamer samples/shader

The is a blog post about running info-beamer in production that gives some more hints for running info-beamer in a real world setup. It might also be helpful to check out the device setup hints page.

Runtime options

Hiding the background

On the PI video output consists of layers (see this blog post about the video scaler hardware).

info-beamer on the PI supports two different ways of hiding the background layer:

  • layer: Uses a black layer behind the normal info-beamer output.
  • console: Blanks the console and disables it, so it's not visible.

The layer mode might be slower, but the console mode requires root permissions. Use one of those:

user:~$ export INFOBEAMER_BLANK_MODE=console
user:~$ export INFOBEAMER_BLANK_MODE=layer

The console mode won't work if you are rotating the Raspberry Pi output by setting display_rotate in /boot/config.txt. But doing that is a bad idea anyway since you can use util.screen_transform to rotate the generated output using info-beamer. This is usally much faster since it doesn't have to use the video scaler hardware for the rotation.

You can also make the normal info-beamer output transparent. Use


to activate this mode. It can be used to overlay other output using an info-beamer visualization.


info-beamer on the PI supports different verbosity levels. The default level is 1. Up to level 4 is currently supported. Set the log level like this:

user:~$ export INFOBEAMER_LOG_LEVEL=3

VSync/Swap interval

Normally the output of info-beamer is synced to the refresh rate of the connected screen. So it's probably 60 frames per second. You can tell info-beamer to skip some frames. This lowers the framerate so you have more time per frame. For complicated visualizations this might be beneficial.

user:~$ export INFOBEAMER_SWAP_INTERVAL=1  # default (probably 60fps)
user:~$ export INFOBEAMER_SWAP_INTERVAL=2  # probably 30fps
user:~$ export INFOBEAMER_SWAP_INTERVAL=3  # probably 20fps

Background threads

Background loading uses a fixed set of worker threads. The default number depends on the number of CPUs detected. For the older single core Raspberry PI the number of threads is 6. For the Raspberry PI 2 the number is 12.

The number of threads also sets a limit on how many resources you can play/load at once. If the limit is hit, loading another resource will block until one of the threads is available again. If you ever need more than the default number of worker threads you can use INFOBEAMER_THREAD_POOL to specify the number of threads available for resource loading/playing:

user:~$ export INFOBEAMER_THREAD_POOL=12

Audio output

Video playback supports audio output. The Raspberry PI supports two audio output channels: Using HDMI or the analog mini phone jack. You can control the audio destination for the output by setting environment variables before calling info-beamer:

For output to HDMI:

user:~$ export INFOBEAMER_AUDIO_TARGET=hdmi

For output using the analog mini phone jack:

user:~$ export INFOBEAMER_AUDIO_TARGET=local

You cannot output to both of them at the same time.

Watchdog support

If you run info-beamer unsupervised it might be useful to automatically reboot the Raspberry if for any reason info-beamer stops working. The Raspberry has hardware watchdog support.

You can use the hardware watchdog support on the PI to automatically hard reboot the PI should info-beamer become stuck for X seconds. The recommended number for X is 30 seconds. To enable the watchdog, load the kernel module and set the following environment variable:

user:~$ modprobe bcm2708_wdog
user:~$ export INFOBEAMER_WATCHDOG=30

You probably need root access to open the /dev/watchdog device file.

Open Source Version

Development of info-beamer started with a version running on normal desktop computers. This version is still available and being worked on. You can download the full source code on github. Please note that the Open Source Version doesn't run on the Raspberry PI.

The Open Source Version should be compatible to the Raspberry PI version in most cases. So node code that you developed on one version should run on the other. When that's not the case, this documentation should provide information about the differences.


info-beamer tries to have dependencies that are available for most linux distributions. It shouldn't be necessary to compile obscure packages before compiling info-beamer. Here are the required 3rd party packages:

Dependency Why?
lua (5.1) for scripting (Note: lua 5.2 is not supported)
libevent (>2.0) io multiplexing
glfw3 opengl initialization
GL & GLU opengl & utility functions
GLEW accessing opengl extensions
ftgl truetype font rendering for opengl
DevIL reading image files
libavformat video decoding
markdown (python) (optional) for rebuilding the documentation

Prerequisites on Ubuntu

Ubuntu provides all required packages. Just execute the following command:

user:~$ apt-get install liblua5.1-dev
    libevent-dev libglfw3-dev
    libglew1.5-dev libftgl-dev libavcodec-dev
    libswscale-dev libavformat-dev libdevil-dev

Building From Source

info-beamer is provided as a source release on The best way to install info-beamer is to clone the github repository and type make inside the root directory:

user:~/src$ git clone
user:~/src$ cd info-beamer
user:~/src/info-beamer$ make
user:~/src/info-beamer$ ./info-beamer 
Info Beamer rev-foobar (
Copyright (c) 2012, Florian Wesch <>

usage: ./info-beamer <root_name>

If something goes wrong, maybe some common questions can help.


There is nothing special to do. info-beamer consists of only a single binary called info-beamer. You can move it to any directory you like (e.g. /usr/local/bin). Or you can call make install to install info-beamer into /usr/local/bin.

user:~/src/info-beamer$ sudo make install
install -o root -g root -m 755 info-beamer /usr/local/bin/

Common Questions

Where is the Windows/OSX version?

There is none. The info-beamer is currently linux only.

Can I use luajit2?

Yes. Although some sandboxing features will be disabled: luajit2 uses its own allocator which cannot be overwritten, so limiting memory usage isn't possible. Aborting nodes that use too much CPU time might be unreliable. If you only run nodes you trust, this shouldn't be a problem.

To use info-beamer in combination with luajit2, call make like this:

$ USE_LUAJIT=1 make

Or if you compiled your own version of luajit2:

$ USE_LUAJIT=1 LUA_CFLAGS=-I../luajit-2.0/src  LUA_LDFLAGS=../luajit-2.0/src/libluajit.a make

I get a GLFW initialization error

You probably compiled the Open Source Version for the Raspberry PI. While compiling works without problems, running this version won't work. The PI provides a very different environment: It only supports OpenGLES, video decoding requires hardware support and a lot of other changes. It was a huge effort to port info-beamer to the Raspberry PI. You can download the ported version here.

/usr/bin/ld: cannot find -llua5.1

I didn't find a reliable way to detect the library name for lua5.1. I'm using the linker flag -llua5.1 for linking. If your lua 5.1 library is called, try the following make command line:

user:~/src/info-beamer$ LUA_LDFLAGS="-llua" make

kernel load error

Note: This question is only relevant for the open source version for Linux.

The info-beamer uses the installed binary lua to precompile the included files kernel.lua and userlib.lua. info-beamer itself uses the install liblua. If the version number between the lib and the binary differs, you'll get an error. Make sure the binary lua is version 5.1, then recompile.

$ user:~$ lua -v
Lua 5.1.4  Copyright (C) 1994-2008, PUC-Rio


Thanks for your interest in info-beamer. info-beamer tries to be a simple framework for building interactive realtime presentations. Lets keep it that way.

Project Philosophy

  • Keep it simple. info-beamer avoids unnecessary clutter. If a simple external script could solve a problem, there is no need to do it in info-beamer. If a problem is solvable within Lua, there is no need to include a C-version (unless speed prohibits a Lua solution).
  • Keep it small. info-beamer is a simple self-contained binary. It shouldn't depend on files somewhere in the filesystem. Keep info-beamer portable.
  • Keep it robust. info-beamer tries to provide a crash free environment. It shouldn't be possible to crash info-beamer by using the provided API.
  • Keep it safe. info-beamer tries to provide a secure environment. Usercode is sandboxed and memory and processing time is limited. It should be safe to execute random node code without worrying about malicious behaviour.
  • Keep it statefree. It should be possible to develop a node independently from other nodes. Composing them shouldn't change their behaviour. OpenGL or other state should not leak into or from child nodes.
  • Keep it scriptable. info-beamer embraces scripting. Not just from the inside but also from the outside. It provides several ways to control a running instance: Changing files, sending UDP/OSC packets or using a TCP connection. Make it easy to script things.
  • Keep it readable. The core info-beamer code tries to be simple. It should be possible to read and hopefully understand the complete source code in one evening.
  • Keep it compilable. info-beamer tries to use libraries that are widely available. It shouldn't be necessary to build obscure dependencies. info-beamer uses a simple GNU Makefile. It shouldn't be necessary to include a buildsystem that creates files larger than all of info-beamers source code combined.


Feel free to fork and enhance info-beamer and send me pull requests. Please keep info-beamer clean and simple.