Stormworks: Build and Rescue Wiki

This article is a stub. You can help Stormworks: Build and Rescue Wiki by expanding it.

Exploring the Stormworks Lua API

Exploring the Stormworks Lua API   [ edit page ]

After reading this you should proceed with Special advise and little known facts


  • Lua scripts run on the server and on the client, they can get into a state where they are not syncronized (e.g. math.random())! While the composite input and output are syncronized, the output on the screen can be different!
  • When vehicle are out of sight (too far away) they despawn. Once you are coming closer, the spawn again. The values of Memory components is being restored, but the lua scripts are reset (like on spawn from workbench). If you need to persist some data, use Memory components or use a No-Despawn-Block (NOT RECOMMENDED because of performance problems!).

Interacting with composite data / onTick

  • the function is called everytime the physics engine calculates a new tick. The frequency is ~60 times per second, but can be way less when you spawn in large vehicles or are in multiplayer
  • you are not allowed to access screen functions at this time, this needs to be done in onDraw()
  • if the execution of onTick() takes longer then 16ms, you effectively slow down the physics engine speed of the game (= slow down)
  • if the execution of onTick() takes longer then 100ms, the game will exit the function and discard the calculations done

Interacting with the Screen / onDraw

  • the function is called everytime the game draws a new frame. The frequency depends on your games FPS and is either ~60 times per second (no vsync) or ~30 FPS (with vsync)
  • you are not allowed to access composite input and output at this time, this needs to be done in onTick()
  • for every connected monitor, this function will be called once. If you have 5 monitors connected, the function will be called 5 times per frame.
  • if you the position of stuff to draw on the monitor is based on its size (e.g. screen.getWidth()/2) the stuff will look different on monitors with different sizes, but connected to the same script
  • if the execution of onDraw() takes longer then 16ms, you effectively slow down the FPS of the game
  • if the execution of onDraw() takes longer then 100ms, the game will exit the function and discard the drawn stuff

Interacting with http

You can send HTTP requests from within Lua.

Lua is executed on the server itself (a normal game or dedicated server) and on the every connected client. If a Lua script calls async.httpGet() every server/client will make a http request to it's own machine (localhost). This means, you cannot transmit any information inbetween players directly, and you cannot access any website on the internet. If a server/client has no webserver running on the specifiec port, the package will simply be dropped and the response_body will be exactly "connect(): Connection refused".

You can run your own webserver and pickup those requests and respond to them. Your webserver could also talk with other services (e.g. Youtube or Discord) and behave like a gateway for your lua script.

Important Information: This information is quite important if you want to develop a lua script with http.

Example code:

async.httpGet(port, url)

function httpReply(port, request_body, response_body) end

  • url must begin with a slash: /test?param=hello
  • rate limit: you are only allowed to send 1 http request per tick. If you send more they will be placed into a queue. Every tick, the oldest request from that queue will be processed. This means potential delay!
  • error handling: the error handling inside lua is very very bad. If you want some more documentation create an issue on

Available functions

For a complete list, go to and scroll down to the bottom part of the page. Here are the most important ones:

input.getNumber(i) -- read from composite input of the script (i = channel) input.getBool(i) -- read from composite input of the script (i = channel)

output.setNumber(i, v) -- write to the composite output of the script (i = channel, v = number) output.setBool(i, v) -- write to the composite output of the script (i = channel, v = boolean)

screen.XXX -- a lot of functions to draw stuff onto the monitor

screen.setColor(r,g,b,a) -- set drawing color (r=red, g=green, b=blue, a=alpha) -- r,g,b can range from 0 to 255 (as the "help tab" in the lua script component denotes) -- alpha is the transparency and also ranges from 0 to 255 (also denoted in the "help tab" mentioned above)

screen.getHeight() -- returns the height of the connected monitor in pixels

screen.drawRectF(x,y,w,h) -- draws a filled rectangle with the top left corner at x,y and a width of w pixels and a height of h pixels

Monitor coordinates

  • the top left corner is x=0, y=0
  • x is the axis to the right
  • y = is the axis to bottom

Practical Examples

Reading and writing to composite

Let's say we want to read to values from composite, add them together and output the result on the composite again.

function onTick() val1 = input.getNumber(1) -- read first value from composite number channel 1 val2 = input.getNumber(2) -- read second value from composite number channel 2

result = val1 + val2

output.setNumber(1, result) -- output result on composite number channel 1 end

Reading Battery value and drawing onto a monitor

batteryLevel = 0 -- initiate the variable

function onTick() batteryLevel = input.getNumber(1) -- every tick we read the current batteryLevel from the composite input (number channel 1) end

function onDraw() -- convert raw battery level to nice looking percent text: "99.9%" batteryPercent = batteryLevel * 100 oneDecimalPlace = string.format("%0.1f", batteryPercent) -- cut away more than 1 decimal digit

text = oneDecimalPlace .. "%"

-- draw text onto monitor screen.setColor(255,0,0) -- set drawing color to red

screen.drawText(1,2, text) -- draw the text, first char begins at x=1, y=2 end

Visualize rotation

Let's say you have a velocity pivot and want to visualize its current rotation.

rotation = 0

function onTick() rotation = input.getNumber(1) end

function onDraw() radiusOfCircle = 8


centerXOfCircle = 10 centerYOfCircle = 10

screen.drawCircle(centerXOfCircle, centerYOfCircle, radiusOfCircle) -- draw the border of a circle

angle = rotation * math.pi * 2 -- convert the rotation to a radians angle: 1 rotation == 360 deg == 2*math.pi

-- the point we rotate is on the upper/north end of the circle, which represents our angle=0 rPoint = rotatePoint(centerXOfCircle, centerYOfCircle, angle, centerXOfCircle, centerYOfCircle - radiusOfCircle)


-- draw a line from the center of the cirle to the rotated point -- the resulting line visualizes the rotation screen.drawLine(centerXOfCircle,centerYOfCircle, rPoint.x,rPoint.y) end

-- this functions rotates a point (px,py) and rotates it around a center (cx,cy) by an angle function rotatePoint(cx, cy, angle, px, py) s = math.sin(angle) c = math.cos(angle)

-- translate point back to origin px = px - cx py = py - cy

-- rotate point xnew = px * c - py * s ynew = px * s + py * c

-- translate point back x = xnew + cx y = ynew + cy return {x=x, y=y} end

Drawing a graph of battery level over time

data = {} -- here we will store the latest battery levels

MAX_DATA_LENGTH = 32 -- store a maxium of 32 battery levels which means the battery level of the last ~0.5 seconds (60 ticks per second)

function onTick() batteryLevel = input.getNumber(1)

-- push current battery level to the data table table.insert(data, batteryLevel)

if #data > MAX_DATA_LENGTH then -- remove the oldest entry in data table.remove(data, 1) end end

function onDraw() -- set background to black screen.setColor(0,0,0) screen.drawClear()

-- draw graph screen.setColor(0,255,0)

for k,v in ipairs(data) do x = k y = screen.getHeight() * (1 - v) -- calculate y depending on the battery level, and because we want 100% to be on top of the monitor and 0% at the bottom we need to do "(1 - v)" screen.drawRectF(x, y, 1, 1) end end

After reading this you should proceed with Special advise and little known facts