Learn TB
A program is just a list of instructions for the computer, written down one after another. TB is a small, friendly language for writing those instructions. You don't need to know anything about programming to start. Go one lesson at a time, and press Run under any code box to see it work.
You can change the code in the Playground as much as you like. Nothing you type can break your computer, and if something goes wrong TB just says "Oops!" and explains what it didn't understand.
1.Your first program: say
say shows something on the screen. Put what you want to show inside the round brackets,
and put words inside quote marks "like this" so TB knows they are text.
say("Hello, Tanisha!")
say("This is my first program.")
TB reads your program from top to bottom, one line at a time. Each line here is one instruction.
You can give say several things at once, separated by commas. It puts a space between them.
say("") with nothing in the quotes prints an empty line, which is nice for spacing things out.
say("I have", 2, "cats")
say("")
say("The end!")
Lines that start with # are comments: little notes for people.
TB skips them completely.
# This line is a note. TB ignores it.
say("Only this line does something.")
2.Values: numbers, text, true and false
Everything a program works with is a value. TB has a few kinds:
- Numbers, like
7,-3or2.5. - Text, inside double or single quotes:
"hello"or'hello'. - true and false, for yes and no.
- nil, which means "nothing here".
- Lists of other values, like
[1, 2, 3](more in lesson 5).
say(42)
say("forty-two")
say(true)
say(nil)
say([1, 2, 3])
To put a quote mark inside text, put a backslash in front of it: \".
\n starts a new line inside the text.
say("She said \"hi\" to me.")
say("line one\nline two")
3.Remembering things with let
let gives a value a name, so you can use it again later. Think of it as a labelled box.
let name = "Tanisha"
let age = 25
say(name)
say(age)
To change what's in the box later, use the name and = without let.
(let is only for making a new box.)
let mood = "sleepy"
say("This morning I felt " + mood)
mood = "happy"
say("After coffee I felt " + mood)
Names can use letters, numbers and _, but can't start with a number:
favourite_food, day2. Capital letters matter: Name and name
are different boxes.
4.Maths and joining text
TB can do sums. * means times and / means divide.
% gives the remainder after dividing (so 7 % 2 is 1).
Brackets work like in school maths.
say(2 + 3)
say(10 - 4)
say(6 * 7)
say(7 / 2)
say(7 % 2)
say((2 + 3) * 4)
+ also joins text together. If either side is text, TB joins them as text.
let first = "Tanisha"
let last = "Brahma"
say(first + " " + last)
say("I have " + 3 + " plants")
Text times a number repeats the text:
say("ha" * 3)
say("<3 " * 5)
Two helpers turn things into other things:
str(x)turns anything into text:str(5)is"5".num(text)turns text like"12"into the number12, so you can do maths with it.
let cups = num("2") + 1
say("Cups of tea today: " + str(cups))
5.Lists
A list keeps several values in order, inside square brackets, separated by commas.
let snacks = ["chocolate", "mango", "chips"]
say(snacks)
say(len(snacks))
len tells you how many things are in a list (or how many letters are in some text).
To get one thing out, put its position in square brackets. Careful: positions start at 0, so the first thing is at position 0, the second at 1, and so on. A negative position counts from the end.
let snacks = ["chocolate", "mango", "chips"]
say(snacks[0])
say(snacks[1])
say(snacks[-1])
You can swap one thing for another, and add new things to the end with push:
let snacks = ["chocolate", "mango", "chips"]
snacks[2] = "popcorn"
push(snacks, "ice cream")
say(snacks)
Two lists can be joined with +: [1, 2] + [3] is [1, 2, 3].
6.Making choices with if and else
if runs some lines only when something is true. The lines that belong to it go inside
curly brackets { }. else says what to do otherwise.
let hour = 22
if hour >= 22 {
say("Time for bed. Sleep well!")
} else {
say("Still time to play.")
}
You can check several things in a row with else if. TB uses the first one that is true.
let cups_of_water = 3
if cups_of_water >= 8 {
say("Amazing, so hydrated!")
} else if cups_of_water >= 4 {
say("Good going, keep sipping.")
} else {
say("Maybe have a glass of water now?")
}
Ways to compare things:
==is the same as (two equals signs, because one=is for putting things in boxes)!=is not the same as<less than,>more than,<=less than or the same,>=more than or the same
Join checks together with and and or, and flip one with not.
let sunny = true
let weekend = false
if sunny and not weekend {
say("Sunny, but it's a work day.")
}
if sunny or weekend {
say("Something nice is happening!")
}
In TB only false and nil count as "no". Everything else,
even 0 and empty text "", counts as "yes".
7.Doing things again and again: loops
while keeps repeating its lines as long as something is true.
Make sure something changes each time, or it will go on forever!
let n = 3
while n > 0 {
say(str(n) + "...")
n = n - 1
}
say("Surprise!")
for goes through a list one thing at a time. Each time round, the name you choose
(here flower) holds the next thing.
let flowers = ["rose", "tulip", "sunflower"]
for flower in flowers {
say("I picked a " + flower)
}
range makes a list of numbers to count with. range(5) is
[0, 1, 2, 3, 4] (it stops just before 5). range(1, 4) is [1, 2, 3].
A third number sets the step: range(10, 0, -5) is [10, 5].
for i in range(1, 4) {
say("Hug number " + str(i))
}
for also works on text, one letter at a time:
for letter in "TB" {
say(letter)
}
8.Making your own instructions: functions
A function is a little recipe with a name. You write it once with fn,
then use it as many times as you like. The names in the round brackets are its inputs.
fn greet(name) {
say("Hello, " + name + "! You look lovely today.")
}
greet("Tanisha")
greet("Mum")
return hands a value back, so you can use the answer somewhere else.
fn double(x) {
return x * 2
}
say(double(4))
say(double(double(4)))
Write a function above the place where you use it, because TB reads from top to bottom.
A function with no return gives back nil.
9.Functions that remember (and call themselves)
A function can be a value too: fn(x) { ... } without a name makes one,
and you can keep it in a box with let.
let shout = fn(text) {
return text + "!!!"
}
say(shout("I love you"))
Here is something magical: a function made inside another function remembers the boxes around it, even after the outer function has finished. This is called a closure. Each counter below has its very own memory.
fn make_counter() {
let count = 0
return fn() {
count = count + 1
return count
}
}
let hugs = make_counter()
hugs()
hugs()
say("Hugs so far: " + str(hugs()))
A function can also call itself, with a smaller problem each time. This is called recursion. Just make sure there's a point where it stops!
fn hearts(n) {
if n <= 0 {
return ""
}
return "<3 " + hearts(n - 1)
}
say(hearts(4))
10.Talking to your program: ask and care
ask shows a question and waits for you to type an answer and press Enter.
The answer comes back as text. (In the browser, the question pops up in a little box.)
let name = ask("What's your name? ")
say("Nice to meet you, " + name + "!")
The answer is always text, so if you want to do maths with it, turn it into a number with num:
let cups = num(ask("How many cups of tea today? "))
say("Only " + str(cups + 1) + " more and you'll be a professional.")
care is TB's special instruction. It prints a little heart and a kind message,
picked at random. Give it a name to make the message personal.
care()
care("Tanisha")
Try the Care bot example to see ask and
care working together.
11.Reading "Oops!" messages
Everyone who writes programs makes little mistakes all the time. It's completely normal! When TB can't understand something, it stops and tells you which line to look at and what confused it:
Oops! On line 2: I don't know what 'nmae' is. Did you mean 'name'?
Try running this one. It has a typo on line 2:
let name = "Tanisha"
say(nmae)
Some common "Oops!" messages and what they mean:
- I don't know what 'x' is. You used a name that hasn't been made yet,
or there's a spelling mistake. Check the spelling, or add a
let. - 'x' doesn't exist yet. Use 'let x = ...' first. You tried to change a box
before making it. Use
letthe first time. - This text never ends. Did you forget a closing "? A quote mark is missing.
- ... was never closed. Did you forget a ')'? (or a
}) Every opening bracket needs a closing one. - Position 3 is outside the list. Remember positions start at 0, so a list of 3 things has positions 0, 1 and 2.
- I can only subtract numbers ... You tried to do maths with text.
Use
num()to turn the text into a number first.
# This one has a mistake too. Can you fix it?
let snacks = ["cake", "chips", "mango"]
say(snacks[3])
Tip: the line number is your best friend. Go to that line, read it slowly, and look for a missing quote, bracket or spelling mistake. Sometimes the real mistake is on the line just above.
12.Maps: things with names
A list keeps things in order. A map keeps things by name: each key points to a value. Write it with curly brackets, a colon between each key and its value.
let me = {"name": "Tanisha", "city": "Guwahati", "plants": 4}
say(me["name"])
say(me.city)
say(len(me), "keys")
Read a value with me["name"] or, when the key is a simple word, with a dot: me.name.
Set one the same way. Asking for a key that isn't there is a mistake, so use .get(key, default)
when you're not sure.
let tea = {"cups": 2}
tea.cups = tea.cups + 1
tea["sugar"] = false
say(tea)
say(tea.get("milk", "no milk"))
for key in tea {
say(key + " is " + str(tea[key]))
}
Maps are everywhere in TB 2.0: a log line, an alert and a JSON file all come back as maps.
13.Methods: asking a value to do something
Text, lists, maps and numbers come with methods: little built-in functions you call with a dot
after the value. "hi".upper() asks the text "hi" for its capital-letter version.
let name = " Tanisha "
say(name.trim().upper())
say("apple,mango,kiwi".split(","))
say("Hello".contains("ell"), "Hello".starts_with("He"))
say("ha".repeat(3))
Lists have the most useful ones. map, filter and sort_by take a function
and use it on every item.
let cups = [3, 1, 4, 1, 5]
say(cups.sum(), cups.max(), cups.sort())
say(cups.filter(fn(c) { return c > 2 }))
say(cups.map(fn(c) { return c * 2 }))
say(["b", "a", "c"].sort().join(" < "))
See every method in the library reference.
14.Text with holes: f-strings
Joining text with + gets fiddly. Put an f before the quotes and you can drop any
value straight into the text inside { }. TB fills in each hole when the line runs.
let name = "Tanisha"
let cups = 3
say(f"{name} has had {cups} cups of tea.")
say(f"That's {cups * 250} ml, or {round(cups * 0.25, 2)} litres.")
say(f"Shouting: {name.upper()}!")
Plain "..." text never fills in holes, so curly brackets in ordinary text stay as they are. To put a real
curly bracket in an f-string, write it twice: {{ or }}.
15.Libraries: import
TB comes with twenty libraries, extra sets of instructions for jobs like reading files, working
with dates, parsing logs or opening windows. Bring one in with import, then use its functions with a dot.
import math
import text
say(math.sqrt(81))
say(text.pad_left("7", 3, "0"))
say(math.round(math.pi, 2))
You can give a library a shorter name with as, and you can import your own .tb files
too: import "helpers.tb" gives you everything that file made with let and fn.
import encode as enc
say(enc.base64("secret message"))
The library reference lists every library and function. In the browser, the libraries that need your computer (windows, the network) explain that politely instead of running.
16.Catching mistakes: try, catch and throw
Some mistakes aren't in your program but in the world: a file that isn't there, text that isn't a number.
Put the risky lines inside try, and say what to do instead in catch. The error arrives as
a map with a message and a line.
try {
let n = num("twelve")
say(n)
} catch err {
say(f"That didn't work: {err.message}")
}
say("...and the program carries on.")
throw raises your own error, with text or a map. Whoever called your function can catch it.
fn take(cups) {
if cups > 10 {
throw {"message": "That's too much tea", "cups": cups}
}
return cups
}
try {
take(12)
} catch err {
say(err.message, "(" + str(err.cups) + " cups)")
}
An error nobody catches stops the program with the usual friendly "Oops!", like this one:
throw "Something went wrong"
17.More loops: break, continue, in and +=
break leaves a loop straight away. continue skips to the next time round.
for n in range(1, 100) {
if n % 3 != 0 {
continue
}
if n > 15 {
break
}
say(n)
}
in asks whether something is inside a list, text or map, and not in asks the opposite.
+= adds to a box in one step (-=, *= and /= work too).
let seen = []
let total = 0
for word in ["tea", "cake", "tea", "rain"] {
if word not in seen {
seen.push(word)
}
total += 1
}
say(seen, total)
say("cake" in seen, "x" in "TB")
Tutorials
Longer walk-throughs that build something real. The security ones run right here in your browser, on sample logs and emails that come with TB.
- Build your first desktop appA to-do list with a window, a text box, a button and a table, step by step.Desktop, about 20 minutes
- Triage an SSH brute force with TBRead a real-looking auth.log, find password guessing, and find out whether the attacker got in. Runs in your browser.Security, runnable
- Extract and defang IOCs from a phishing emailPull every domain, IP, link and hash out of a suspicious email and make them safe to share. Runs in your browser.Security, runnable
- Write a Sigma rule and run itDescribe an attack once in YAML and run it over web and Windows logs. Runs in your browser.Detection, runnable
- Check your own ports and IP reputationWhich services does your laptop expose, and does an address have a bad name? Desktop code.Network, desktop
Reference: built-in instructions
These eight instructions have been in TB from the start. Version 2.0 adds a few more (type, keys, int, round, min, max, sum, sorted, exit), plus methods on every value and twenty libraries: see the library reference.
say(value, ...)
returns nilShows values on the screen, with a space between each one. say() on its own prints an empty line.
say("Hi", 2)
say()
say([1, 2, 3])
len(list_or_text)
returns numberHow many things are in a list, or how many letters are in some text.
say(len("TB"))
say(len([1, 2, 3]))
str(value)
returns textTurns any value into text, so you can join it to other text.
say(str(5) + " cats")
num(text)
returns numberTurns text like "12" or "2.5" into a number. Says "Oops!" if the text isn't a number.
say(num("12") + 1)
say(num("2.5") * 2)
range(n) / (a, b) / (a, b, step)
returns listMakes a list of whole numbers: from 0 (or a) up to, but not including, n (or b), going up by step.
say(range(3))
say(range(1, 4))
say(range(10, 0, -5))
push(list, value)
returns the listAdds a value to the end of a list, and gives the same list back.
let snacks = ["cake"]
push(snacks, "mango")
say(snacks)
ask(question)
returns textShows the question, waits for an answer, and gives it back as text. Use num() to do maths with it.
let name = ask("What's your name? ")
say("Hello, " + name + "!")
care() / (name)
returns nilPrints ♥ and a kind message, chosen at random. Give it a name to make it personal.
care()
care("Tanisha")
Keywords
The words TB reserves for itself. You can't use them as names for your own boxes.
| Keyword | What it does | Example |
|---|---|---|
let | Makes a new named box | let name = "Tanisha" |
= | Changes a box that already exists | name = "Tani" |
fn | Makes a function (named or not) | fn double(x) { return x * 2 } |
return | Hands a value back from a function | return x * 2 |
if / else if / else | Chooses which lines to run | if x > 3 { ... } else { ... } |
while | Repeats while something is true | while n > 0 { n = n - 1 } |
for ... in | Goes through a list or text | for x in [1, 2, 3] { say(x) } |
true / false / nil | Yes, no and nothing | let done = false |
and / or / not | Combine or flip checks | if sunny and not raining { ... } |
# | A note TB ignores | # this is a comment |
import ... as | Brings in a library or a .tb file | import json as j |
try / catch | Runs risky lines and handles a mistake | try { ... } catch err { ... } |
throw | Raises your own error | throw "Out of tea" |
break / continue | Leave a loop, or skip to the next round | if done { break } |
in / not in | Is it inside a list, text or map? | if "tea" in drinks { ... } |
+= -= *= /= | Change a box in one step | total += 1 |
f"..." | Text with {holes} filled in | f"Hi {name}" |
Little reminders
- Text goes in quotes:
"hello"or'hello'. letmakes a new box;=on its own changes an existing one.=puts a value in a box;==checks if two things are the same.- Lists count from 0.
- Blocks of lines (for
if,while,for,fn) go inside{ }. - You don't need semicolons at the end of lines.
#starts a note that TB ignores.
Running a .tb file on your Windows laptop
The Playground works right here in your browser. To run TB programs on your laptop, without the internet:
- Get the installer,
tb-windows-x64-setup.exe, from the Download page and run it. - Write your program in Notepad and save it with a name ending in
.tb, likehello.tb. (In the save box, choose "All files" so Notepad doesn't add.txt.) - Double-click your
.tbfile, and a window opens to run it. Right-click it and choose Edit to change it.
The Download page has the full step-by-step guide, including what to click if Windows asks whether it's safe.