The Lua scripting in Peakboard with MoonSharp

April 20, 2024 · updated September 22, 2026

Peakboard, Lua, MoonSharp, Scripting, .NET

In April 2024 I finished an internal rework of the Lua scripting in Peakboard. The scripts on the boards did not change. Underneath, MoonSharp, a Lua interpreter written in C#, connects the Lua of a board with the .NET runtime. This article describes the surface between the two languages.

Lua is small and was made for embedding. MoonSharp implements Lua 5.2 from 2011, so a script written years ago still runs. Two things surprise developers who come from C#. Indexing starts at 1, and the table is the one data structure for everything.

With Building Blocks, you snap blocks together and the editor writes the Lua. The screenshot below shows the Script Editor with the switch between Script Mode and Block Mode (1), the canvas with the blocks (2), the trash can (3) and the generated Lua (4). The send e-mail block at the top becomes the mail.send call in line 1. Those five lines are what MoonSharp runs, the same way as a script typed in Script Mode.

Peakboard Script Editor, block mode with the generated Lua code

MoonSharp is a .NET assembly, so a script runs in the same process as the rest of the runtime. There is no native library and no separate process. A board reads a data source, updates a control and writes back to a machine inside one tick, and none of those steps crosses a process boundary.

MoonSharp does not expose .NET objects on its own. Every type a script may use gets a descriptor class. Every member is marked visible by hand, so nothing leaks by accident. The designer needs the same surface for validation, completion and mock data, and each runtime needs it to execute the script. The list only grows, and it has to be the same on both sides.

Lua has one number type, and .NET has int, long, decimal and double. An integer, a decimal and a long all arrive as the same Lua number, and the way back needs a rule for every member.

Collections need the same care. A generic list and a non-generic list are different types to .NET, and an enumerable has no index. A data source returns whatever its own layer produces, so every collection is wrapped into one shape that knows its count and returns item n.

In block mode a local variable carries a type, but the type exists only in the editor. The generated script is plain Lua with its one number type, and MoonSharp never sees a type. The next screenshot shows the Add variable dialog, opened with the plus next to Local variables in the palette (1). It asks for the Variable name (2) and the Variable type (3), here String.

Peakboard Script Editor, adding a local variable with an explicit type

Data rows have no fixed shape. Their columns come from the query the board author wrote, so there is no class to describe. The lookup happens while the script runs. A missing column returns nil instead of an error, and the board keeps running.

That’s it.

If you have questions feel free to contact me.

Peakboard scripting documentation →
MoonSharp →
Lua 5.2 reference manual →