Create: Computing AgesBrass Docs
Libraries

term

The text screen: cursor, colours, clearing and scrolling.

All computers

The term library controls the text screen of the computer. The screen is a grid of characters, and each cell has its own text colour and background colour. print and write add text where the cursor stands and go to the next line by themselves, like a typewriter; term lets you put the cursor anywhere, choose colours, clear and scroll. It is what you need for status screens, dashboards, menus and buttons.

Coordinates count from 1: (1, 1) is the top left corner, x is the column (to the right), y is the row (downwards).

Brass
term.clear()
term.set_cursor(1, 1)
print("Vault of iron")
term.set_cursor(20, 1)
term.write("1234 ingots")
Screen
Vault of iron      1234 ingots

The size of the screen depends on the computer:

computercolumns × rowscolours
Tube Computer40 × 14none: a teletype printing on paper
Transistor Mainframe51 × 19none: green phosphor
Minicomputer51 × 19none: amber phosphor
Personal Computer51 × 1916 colours
Microcontroller40 × 1216 colours
Modern Computer64 × 2416 colours

Every computer has the term library. On the older ones, colours are accepted but not shown (see below). For drawings in pixels under the text, see gfx; the Screens and monitors guide shows how both fit together.

Functions
term.get_size()The size of the screen in characters: {w = 51, h = 19} on a Personal Computer.
term.set_cursor(x, y)Moves the cursor to column x, row y.
term.get_cursor()Where the cursor is: {x = ..., y = ...}, counted from 1.
term.write(text)Writes text at the cursor, on one line, without going to a new line.
term.clear()Clears the whole screen with the current background colour.
term.clear_line()Clears the row of the cursor with the current background colour.
term.scroll(n)Moves all the text of the screen up by n rows.
term.set_fg(color)Sets the colour of the text written from now on.
term.set_bg(color)Sets the background colour of the cells written from now on, and of the cells cleared by term.clear, term.clear_line and term.scroll.
term.colorsThe table of the 16 colours: white is 0, black is 15.

Cursor and size

The cursor is where the next character goes. print, write and term.write all start there and move it.

#

term.get_size()

→ table

The size of the screen in characters: {w = 51, h = 19} on a Personal Computer.

Returns
table
w, the number of columns, and h, the number of rows

Compute positions from it instead of writing numbers by hand, and the same program looks right on every computer: a Microcontroller (40 columns), a Personal Computer (51) or a Modern Computer (64).

Brass
local size = term.get_size()
term.set_bg(term.colors.black)
term.clear()
local corners = {{1, 1}, {size.w, 1}, {1, size.h}, {size.w, size.h}}
term.set_fg(term.colors.yellow)
for _, c in ipairs(corners) do
  term.set_cursor(c[1], c[2])
  term.write("+")
end
local text = size.w .. " x " .. size.h
term.set_fg(term.colors.white)
term.set_cursor(math.floor((size.w - #text) / 2) + 1, math.floor(size.h / 2))
term.write(text)
Screen
Screen

The very same program on a Microcontroller, whose screen is smaller:

Brass
local size = term.get_size()
term.set_bg(term.colors.black)
term.clear()
local corners = {{1, 1}, {size.w, 1}, {1, size.h}, {size.w, size.h}}
term.set_fg(term.colors.yellow)
for _, c in ipairs(corners) do
  term.set_cursor(c[1], c[2])
  term.write("+")
end
local text = size.w .. " x " .. size.h
term.set_fg(term.colors.white)
term.set_cursor(math.floor((size.w - #text) / 2) + 1, math.floor(size.h / 2))
term.write(text)
Screen
Screen

A monitor does not change this size: a wall of monitors shows the same grid, only bigger.

See also term.set_cursor()

#

term.set_cursor(x, y)

Moves the cursor to column x, row y.

Parameters
x number
the column, 1 at the left
y number
the row, 1 at the top
Brass
term.clear()
term.set_cursor(5, 2)
term.write("Furnace 1: lit")
term.set_cursor(5, 3)
term.write("Furnace 2: out of coal")
Screen
    Furnace 1: lit
    Furnace 2: out of coal

The position must be whole numbers: term.set_cursor(10.5, 1) stops the program with bad argument #1 to 'set_cursor' (number has no integer representation). When you compute a position (to centre a text, for example), round it with math.floor.

A position outside the screen is allowed, without error, but what you write there is lost: the characters that fall off the screen are simply not drawn. Text that starts near the right edge is cut:

Brass
term.clear()
term.set_cursor(48, 1)
term.write("Pressure")
term.set_cursor(1, 2)
term.write("only Pres is visible")
Screen
                                               Pres
only Pres is visible

See also term.get_cursor() term.write()

#

term.get_cursor()

→ table

Where the cursor is: {x = ..., y = ...}, counted from 1.

Returns
table
x and y, the position of the cursor

Handy to remember a spot and come back to it later, to update a value without redrawing the rest:

Brass
term.clear()
term.set_cursor(1, 1)
write("Iron in vault: ")
local spot = term.get_cursor()
print("...")
-- later, when the count is known:
term.set_cursor(spot.x, spot.y)
term.write("1234 ")
Screen
Iron in vault: 1234

After term.write, the cursor stands just after the last character, even when that is past the right edge: writing up to the last column of a 51 column screen leaves x at 52.

See also term.set_cursor()

Writing

#

term.write(text)

Writes text at the cursor, on one line, without going to a new line.

Parameters
text any
the text to write (a number or any other value is converted, like tostring)

term.write is the precise tool of the screen: it writes exactly where the cursor is and touches nothing else. It differs from the global write and print on four points:

term.writewrite and print
at the right edgethe rest of the text is cutthe text continues on the next line
\nshown as a spacegoes to a new line
a tab \tshown as ?spaces up to the next multiple of 4 columns
at the bottom of the screennever scrollsscrolls the screen up
Brass
term.clear()
term.set_cursor(1, 1)
term.write("Coal:\t12\nIron:\t40")
term.set_cursor(1, 2)
write("Coal:\t12\nIron:\t40")
Screen
Coal:?12 Iron:?40
Coal:   12
Iron:   40

So use term.write for anything placed on a grid (a value in a box, a button, a status bar), and print for text that flows, like a log. Other control characters show as ? with both.

The text takes the current colours (term.set_fg, term.set_bg). Writing costs one extra instruction per 16 characters.

See also write() print() term.set_cursor()

Clearing and scrolling

These three functions fill cells with spaces in the current background colour, and none of them moves the cursor.

#

term.clear()

Clears the whole screen with the current background colour.

The cursor stays where it was, so a clear is nearly always followed by term.set_cursor(1, 1):

Brass
print("old line 1")
print("old line 2")
term.clear()
print("after the clear")
local c = term.get_cursor()
print("cursor on row " .. c.y)
Screen
after the clear
cursor on row 4

(The new text starts on row 3, under the place of the old lines.)

To paint the whole screen, choose the background first:

Brass
term.set_bg(term.colors.blue)
term.clear()
term.set_cursor(3, 2)
term.set_fg(term.colors.yellow)
term.write("ALL BLUE")
Screen
Screen

term.clear does not erase what gfx has drawn: call gfx.clear() for the drawings.

See also term.clear_line() gfx.clear()

#

term.clear_line()

Clears the row of the cursor with the current background colour.

Two common uses: erase a line before writing a shorter text on it, and paint a coloured bar across the screen.

Brass
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.green)
term.clear_line()
term.set_fg(term.colors.black)
term.write(" Storage OK")
term.set_cursor(1, 3)
term.set_bg(term.colors.red)
term.clear_line()
term.set_fg(term.colors.white)
term.write(" Boiler overheated")
term.set_bg(term.colors.black)
Screen
Screen

See also term.clear() term.set_cursor()

#

term.scroll(n)

Moves all the text of the screen up by n rows.

Parameters
n number
how many rows to move the text up; a negative number moves it down

The top rows disappear, and new empty rows (in the current background colour) appear at the bottom. With a negative n, the text moves down and the empty rows appear at the top. Scrolling by the height of the screen or more clears it. n must be a whole number.

Brass
term.clear()
term.set_cursor(1, 1)
print("line 1")
print("line 2")
print("line 3")
term.scroll(1)
Screen
line 2
line 3

The cursor does not move with the text, and gfx drawings stay where they are (gfx.scroll moves them). print scrolls by itself when it reaches the bottom; term.scroll is for a program that places its own lines, like a log under a fixed title (see the patterns below).

See also term.clear()

Colours

There are 16 colours, numbered from 0 to 15 in the order of Minecraft's dyes. Use their names through term.colors: term.colors.red is 14. The Colours page shows them all.

#

term.set_fg(color)

Sets the colour of the text written from now on.

Parameters
color number
a colour from 0 to 15, like term.colors.red

What is already on the screen keeps its colours: only the next characters change.

Brass
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(1, 1)
term.set_fg(term.colors.lime)
print("Water tank: 92 %")
term.set_fg(term.colors.yellow)
print("Lava tank: 35 %")
term.set_fg(term.colors.red)
print("Fuel tank: 4 %, refill!")
term.set_fg(term.colors.white)
Screen
Screen

The colour must be a number from 0 to 15. A colour name in quotes is an error here (unlike gfx, which accepts "red"): term.set_fg("red") stops with bad argument #1 to 'set_fg' (number expected, got string), and term.set_fg(16) with bad argument #1 to 'set_fg' (color must be 0..15). With a name in a variable, write term.colors[name].

Tip

The colours stay after the program ends: the prompt of the shell would be red too. End your programs with term.set_fg(term.colors.white) and term.set_bg(term.colors.black). The Stop button (Ctrl+T) resets them, and clears the screen.

See also term.set_bg() term.colors

#

term.set_bg(color)

Sets the background colour of the cells written from now on, and of the cells cleared by term.clear, term.clear_line and term.scroll.

Parameters
color number
a colour from 0 to 15, like term.colors.blue

A coloured background turns text into a label or a button. Remember to go back to black afterwards:

Brass
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(2, 2)
term.set_bg(term.colors.green)
term.set_fg(term.colors.white)
term.write(" OPEN ")
term.set_bg(term.colors.black)
term.write("  ")
term.set_bg(term.colors.red)
term.write(" CLOSE ")
term.set_bg(term.colors.black)
term.set_cursor(2, 4)
term.set_fg(term.colors.light_gray)
term.write("Click a button to move the gate.")
Screen
Screen

The buttons program turns such buttons into a clickable panel with os.pull_event("click").

See also term.set_fg() term.colors

#

term.colors

→ tablevalue

The table of the 16 colours: white is 0, black is 15.

Returns
table
the 16 colour names, each giving its number
numbernamenumbername
0 white8 light_gray
1 orange9 cyan
2 magenta10 purple
3 light_blue11 blue
4 yellow12 brown
5 lime13 green
6 pink14 red
7 gray15 black

The same names and numbers work in gfx. Here they are on a Personal Computer:

Brass
term.set_bg(term.colors.black)
term.clear()
local names = {"white", "orange", "magenta", "light_blue", "yellow", "lime", "pink", "gray",
  "light_gray", "cyan", "purple", "blue", "brown", "green", "red", "black"}
for i, name in ipairs(names) do
  local column = 2 + math.floor((i - 1) / 8) * 25
  local row = 2 + (i - 1) % 8 * 2
  term.set_cursor(column, row)
  term.set_fg(term.colors.gray)
  term.write("[")
  term.set_bg(term.colors[name])
  term.write("    ")
  term.set_bg(term.colors.black)
  term.write("]")
  term.set_fg(term.colors.white)
  term.write(" " .. term.colors[name] .. " " .. name)
end
Screen
Screen

See also term.set_fg() term.set_bg() Colours

Monitors and older screens

Monitors. A CRT Monitor or an LCD Monitor touching the computer shows its screen: the same grid, the same characters and colours, scaled to fill the monitor (several monitors side by side make one big screen, up to 8 × 6 blocks). The program does not see a difference: term.get_size gives the size of the computer's own screen. The CRT Monitor keeps the look of the computer (green or amber on the older ones, green phosphor for the Tube Computer's paper), the LCD Monitor always shows the colours. See Monitors.

Phosphor screens. The Transistor Mainframe (green) and the Minicomputer (amber) have monochrome screens: every character lights up in the colour of the phosphor, and background colours are not shown. term.set_fg and term.set_bg still work (no error), so a program written for a colour screen runs unchanged, but its colours vanish. Here is the "OPEN / CLOSE" example above on a Transistor Mainframe:

Brass
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(2, 2)
term.set_bg(term.colors.green)
term.set_fg(term.colors.white)
term.write(" OPEN ")
term.set_bg(term.colors.black)
term.write("  ")
term.set_bg(term.colors.red)
term.write(" CLOSE ")
term.set_bg(term.colors.black)
term.set_cursor(2, 4)
term.set_fg(term.colors.light_gray)
term.write("Click a button to move the gate.")
Screen
Screen

The buttons cannot be told apart from the text any more. On these screens, show things with characters instead: brackets around buttons, > for a selection, a bar of # for a level.

Brass
term.clear()
term.set_cursor(2, 2)
term.write("[ OPEN ]  [ CLOSE ]")
term.set_cursor(2, 4)
term.write("Water  [##########----------]  50 %")
term.set_cursor(2, 5)
term.write("Lava   [###-----------------]  15 %")
term.set_cursor(2, 7)
term.write("> Start the pumps")
term.set_cursor(2, 8)
term.write("  Stop the pumps")
Screen
Screen

The teletype. The Tube Computer prints on a sheet of paper, 40 characters by 14 lines, in dark ink. It is still the same kind of screen: print makes the paper go up, term.set_cursor, term.clear and term.scroll work as on the others. Only the colours are not printed, and the paper cannot be clicked: clicks in the terminal window send no click event (a monitor attached to a Tube Computer can still be pressed). There is no gfx on the Tube Computer.

Brass
print("== SILICON REPORT ==")
print("boules pulled:    12")
print("wafers cut:       72")
print("rejected:          3")
Screen
== SILICON REPORT ==
boules pulled:    12
wafers cut:       72
rejected:          3

Common patterns

A centred title. Subtract the length of the text from the width, halve it, and round down:

Brass
local function center(y, text)
  local size = term.get_size()
  term.set_cursor(math.floor((size.w - #text) / 2) + 1, y)
  term.write(text)
end
term.clear()
center(1, "BOILER ROOM")

A status bar at the bottom. Paint the last row, write on it, then put the cursor back where it was, so that the print calls of the rest of the program are not disturbed:

Brass
local function status(text)
  local size = term.get_size()
  local back = term.get_cursor()
  term.set_cursor(1, size.h)
  term.set_bg(term.colors.gray)
  term.set_fg(term.colors.white)
  term.clear_line()
  term.write(" " .. text)
  term.set_bg(term.colors.black)
  term.set_cursor(back.x, back.y)
end
status("Press line: running, 37 items/min")

Redraw one line without flicker. A program that clears the whole screen and redraws everything every second makes the screen blink: on a slow computer the drawing takes several ticks, and players see it half done. Draw the fixed parts once, then overwrite only the values that change. Pad the new text with spaces so that a shorter value erases the end of the longer one:

Brass
term.clear()
term.set_cursor(1, 1)
term.write("Iron ingots:")
term.set_cursor(1, 2)
term.write("Gold ingots:")
local function show(row, value)
  term.set_cursor(14, row)
  term.write(string.format("%-8d", value))  -- 8 columns, padded with spaces
end
show(1, 1234)
show(2, 56)
show(1, 99)  -- "1234" becomes "99", no "9934"
term.set_cursor(1, 4)  -- leave the cursor under the screen you drew
Screen
Iron ingots: 99
Gold ingots: 56

A screen with a title bar. Everything together: a coloured bar with a centred title, values in two colours, and a status bar:

Brass
local size = term.get_size()
term.set_bg(term.colors.black)
term.clear()

local function bar(y, color, text)
  term.set_cursor(1, y)
  term.set_bg(color)
  term.set_fg(term.colors.white)
  term.clear_line()
  term.set_cursor(math.floor((size.w - #text) / 2) + 1, y)
  term.write(text)
  term.set_bg(term.colors.black)
end

local function value(y, label, amount, unit, ok)
  term.set_cursor(3, y)
  term.set_fg(term.colors.light_gray)
  term.write(label)
  term.set_cursor(24, y)
  if ok then term.set_fg(term.colors.lime) else term.set_fg(term.colors.red) end
  term.write(amount .. " " .. unit)
end

bar(1, term.colors.blue, "STEAM ENGINE - BOILER ROOM")
value(3, "Boiler heat", 18, "/ 18", true)
value(4, "Water intake", 1620, "mB/t", true)
value(5, "Engines running", 6, "of 8", false)
value(6, "Stress capacity", 98304, "su", true)
bar(size.h, term.colors.gray, "updated every second")
Screen
Screen

A two-column dashboard. Two panels side by side, each with its own title, for two parts of a factory:

Brass
local size = term.get_size()
local half = math.floor(size.w / 2)
term.set_bg(term.colors.black)
term.clear()

local function panel(x, title, color, rows)
  term.set_cursor(x, 1)
  term.set_bg(color)
  term.set_fg(term.colors.black)
  term.write(string.format(" %-" .. (half - 2) .. "s", title))
  term.set_bg(term.colors.black)
  for i, row in ipairs(rows) do
    term.set_cursor(x + 1, i + 2)
    term.set_fg(term.colors.white)
    term.write(row[1])
    term.set_cursor(x + half - 8, i + 2)
    term.set_fg(row[3])
    term.write(string.format("%6d", row[2]))
  end
end

panel(1, "SMELTING", term.colors.orange, {
  {"Iron ingots", 1234, term.colors.lime},
  {"Gold ingots", 312, term.colors.lime},
  {"Copper ingots", 18, term.colors.red},
})
panel(half + 1, "PRESSING", term.colors.light_blue, {
  {"Iron sheets", 640, term.colors.lime},
  {"Brass sheets", 96, term.colors.yellow},
  {"Gold sheets", 0, term.colors.red},
})
Screen
Screen

A coloured log under a fixed title. Each line has a coloured tag. When the screen is full, term.scroll moves the lines up and the title is drawn again on top:

Brass
local size = term.get_size()
local row = 2

local function title()
  term.set_cursor(1, 1)
  term.set_bg(term.colors.gray)
  term.set_fg(term.colors.white)
  term.clear_line()
  term.write(" Item sorter log")
  term.set_bg(term.colors.black)
end

local function log(color, tag, text)
  if row > size.h then
    term.scroll(1)
    title()
    row = size.h
  end
  term.set_cursor(1, row)
  term.set_fg(color)
  term.write(tag)
  term.set_fg(term.colors.white)
  term.write(" " .. text)
  row = row + 1
end

term.set_bg(term.colors.black)
term.clear()
title()
local cargo = {"iron ingots", "copper ingots", "gold nuggets", "andesite", "brass sheets"}
for i = 1, 20 do
  local item = cargo[(i - 1) % #cargo + 1]
  log(term.colors.lime, "[ OK ]", (i * 7 % 50 + 14) .. " " .. item .. " sorted")
  if i == 9 then
    log(term.colors.yellow, "[WARN]", "vault B is 90 % full")
  end
end
log(term.colors.red, "[FAIL]", "vault B full, belt stopped")
Screen
Screen