Tiny Language Reference

Hello World

The classic first program - print a message to the output:

print("Hello, World!")

Click Run to see the output. Add pause() to your code to enter debug mode and inspect variables.

Literals

Literals are fixed values written directly in your code.

Numbers

Tiny has a single number type that handles both integers (whole numbers) and floats (decimal numbers). The interpreter displays them differently in the visualization but they work the same way in expressions.

42        // integer
-5        // negative integer
3.14      // float
0.5       // float
-2.7      // negative float

Math operations work on any numbers:

print(5 / 2)      // 2.5 (float result)
print(4 / 2)      // 2 (integer result)
print(3.5 + 1.5)  // 5 (integer result)

Strings

Text enclosed in double quotes. Single quotes are not supported.

"hello"
"Hello, World!"
""        // empty string

Strings can be indexed like arrays:

let word = "hello"
print(word[0])  // "h"
print(word[4])  // "o"
print(word.length())  // 5

Booleans

Logical values for true/false conditions.

true
false

Arrays

Ordered collections of values. Can hold mixed types.

[1, 2, 3, 4, 5]
[1, "hello", true]    // mixed types
[]                    // empty array
[[1, 2], [3, 4]]      // nested arrays

Accessing Elements

Arrays are zero-indexed. Reading an index that doesn't exist causes an error:

let arr = [10, 20, 30]
print(arr[0])   // 10 (first element)
print(arr[2])   // 30 (third element)
print(arr.length()) // 3
print(arr[5])   // Error: Index 5 out of bounds

Modifying Elements

let arr = [1, 2, 3]
arr[1] = 99
print(arr)  // [1, 99, 3]

Growing Arrays

Arrays can grow by assigning to indices beyond the current length. Gaps are filled with null:

let arr = []
arr[0] = 10
arr[1] = 20
arr[2] = 30
print(arr.length())  // 3

// Gaps are filled with null
let sparse = []
sparse[2] = 99
print(sparse[0])  // null
print(sparse[1])  // null
print(sparse[2])  // 99

Building Arrays in Loops

A common pattern is to build an array using a loop:

let squares = []
let i = 0
while (i < 5) {
  squares[i] = i * i
  i = i + 1
}
print(squares)  // [0, 1, 4, 9, 16]

Nested Arrays

let matrix = [[1, 2], [3, 4]]
print(matrix[0][1])  // 2

Variables

Variables store values and must be declared before use. Tiny is dynamically typed - variables can hold any type and change types during execution.

Naming

  • Variable names must start with a letter or underscore
  • Names can contain letters, numbers, and underscores
  • Names are case-sensitive (foo and Foo are different)
  • Cannot use reserved keywords or built-in function names

Reserved Keywords

and class else equals false function if let new not or return stop this true while

Built-in Functions

circle clear color fast fill fullscreen height input key line looplimit num pause pressed print random rect sleep slow slower stroke text triangle width

Built-in Methods

.length()

Declaration

Use let to declare a variable with an initial value:

let x = 42
let name = "Connor"
let items = [1, 2, 3]

Assignment

After declaration, you can reassign variables to new values (even different types):

let x = 10
x = 20          // reassign to new number
x = "hello"     // dynamic typing - can change type

Scoping

Tiny uses lexical scoping with block-level scope (like Java). Variables are looked up starting from the innermost scope and moving outward to the global scope. Each block (if, while, function body) creates a new scope.

Global Scope

Variables declared at the top level are in the global scope and accessible everywhere:

let globalVar = 100

function foo() {
  print(globalVar)  // can access global
}

foo()  // prints: 100

Function Scope

Variables declared inside a function are local to that function:

function foo() {
  let localVar = 42
  print(localVar)  // works
}

foo()
print(localVar)  // ERROR: Undefined variable

Block Scope

Tiny uses block-level scoping (like Java). Variables declared inside if or while blocks are only accessible within that block:

if (true) {
  let x = 42
  print(x)  // works: 42
}
print(x)  // ERROR: Undefined variable

The same applies to while loops:

let i = 0
while (i < 3) {
  let temp = i * 2
  print(temp)
  i = i + 1
}
print(temp)  // ERROR: Undefined variable

Variables from outer scopes can still be accessed and modified inside blocks:

let result = 0
if (true) {
  result = 42  // modifies outer variable
}
print(result)  // 42

Scope Chain (Multiple Layers)

The scope chain goes up multiple layers, not just one. Inner functions can access variables from all enclosing scopes:

let global = 100

function outer() {
  let outerVar = 50

  function inner() {
    let innerVar = 10
    // Can access all three!
    print(innerVar + outerVar + global)
  }

  inner()
}

outer()  // prints: 160

How the Scope Chain Works

When inner() runs and needs a variable:

  1. First checks inner's scope → finds innerVar
  2. Checks outer's scope (parent) → finds outerVar
  3. Checks global scope (grandparent) → finds global

The chain is: inner → outer → global → (not found = error)

Closures

Functions capture their surrounding environment when created. This captured environment persists even after the outer function returns:

function makeCounter() {
  let count = 0

  function increment() {
    count = count + 1
    return count
  }

  return increment
}

let counter = makeCounter()
print(counter())  // 1
print(counter())  // 2
print(counter())  // 3

Each call to makeCounter() creates a new closure with its own count:

let counterA = makeCounter()
let counterB = makeCounter()

print(counterA())  // 1
print(counterA())  // 2
print(counterB())  // 1 (independent count)

Variable Shadowing

A local variable can have the same name as an outer variable, "shadowing" it:

let x = 10

function foo() {
  let x = 99  // shadows the outer x
  print(x)    // 99
}

foo()
print(x)  // 10 (outer x unchanged)

Assignment vs Declaration

Using let creates a new variable in the current scope. Assignment without let modifies an existing variable in the scope chain:

let x = 10

function foo() {
  x = 99  // modifies outer x (no let)
}

foo()
print(x)  // 99

Operators

Operators perform computations and comparisons on values.

Arithmetic

OperatorDescriptionExample
+Addition5 + 38
-Subtraction5 - 32
*Multiplication5 * 315
/Division15 / 35
%Modulo (remainder)7 % 31
-xNegation-5

Comparison

OperatorDescriptionExample
equalsEqual to5 equals 5true
not equalsNot equal to5 not equals 3true
<Less than3 < 5true
>Greater than5 > 3true
<=Less than or equal3 <= 3true
>=Greater than or equal5 >= 5true

Logical

OperatorDescriptionExample
andLogical ANDtrue and falsefalse
orLogical ORtrue or falsetrue
notLogical NOTnot truefalse

String

The + operator concatenates strings:

let greeting = "Hello, " + "World!"  // "Hello, World!"
let msg = "Age: " + 25               // "Age: 25"

Precedence

From highest to lowest:

  1. not, - (unary)
  2. *, /, %
  3. +, -
  4. <, >, <=, >=
  5. equals, not equals
  6. and
  7. or

Use parentheses to override: (2 + 3) * 420

Control Flow

Control flow statements determine which code runs and in what order.

If

if (condition) {
  // executed if condition is true
}

If-Else

if (x > 0) {
  print("positive")
} else {
  print("not positive")
}

Else If

Use else if for multiple conditions:

if (grade >= 90) {
  print("A")
} else if (grade >= 80) {
  print("B")
} else if (grade >= 70) {
  print("C")
} else {
  print("D or F")
}

The first matching condition runs, then the rest are skipped.

Loops

The while loop repeats code as long as a condition is true:

let i = 0
while (i < 5) {
  print(i)
  i = i + 1
}

Loop Limit

To prevent infinite loops from freezing your browser, loops are limited to 10,000 iterations by default. If exceeded, the program stops with an error. Use looplimit(n) to change the limit.

Stop

The stop keyword immediately terminates program execution:

while (true) {
  let input = key()
  if (input equals "q") {
    stop  // exit the program
  }
}

Functions

Functions let you organize code into reusable blocks. Tiny has built-in functions and you can define your own.

Built-in

FunctionDescriptionExample
print(x) Output a value to the console print("Hello")
x.length() Length of array or string [1,2,3].length()3
num(x) Convert string to number num("42")42
random(min, max) Random integer in range [min, max] random(1, 6)

Speed Control

These functions control execution and enable debugging. Use pause() to enter debug mode where you can inspect variables and step through code.

FunctionDescription
pause() Enter debug mode - shows variables and step buttons
sleep(ms) Pause execution for specified milliseconds
looplimit(n) Set max loop iterations (default: 10000)
slow() Set step delay to 1 second (for animation)
slower() Set step delay to 2 seconds (for animation)
fast() Remove step delay (instant execution)

Debug Mode

When pause() is called, the code pane splits to show the call stack with all variables. Three buttons appear:

  • Step Into - Execute ONE statement, entering function calls to step through them line by line
  • Step Over - Execute ONE statement, running entire functions without stopping inside them
  • Resume - Continue running until the next pause() or end of program

Stack frames show the function name, parameters, local variables, and which line called the function.

Example: Debugging a Loop

let total = 0
let i = 0
while (i < 5) {
  total = total + i
  i = i + 1
  pause()  // inspect variables each iteration
}
print(total)

Example: Debugging a Function

function add(a, b) {
  let sum = a + b
  pause()  // see function's local variables
  return sum
}

let result = add(3, 4)
print(result)

Keyboard

FunctionDescriptionExample
input() Read a line of text input let name = input()
key() Wait for and return single keypress let k = key()
pressed(key) Check if key is currently held down pressed("ArrowUp")

Common Key Names

For key() and pressed():

  • "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"
  • "Enter", "Space", "Escape"
  • "a" - "z" (lowercase letters)
  • "0" - "9" (digits)

Example: Arrow Key Movement

let x = 100
let y = 100

while (true) {
  if (pressed("ArrowUp")) { y = y - 5 }
  if (pressed("ArrowDown")) { y = y + 5 }
  if (pressed("ArrowLeft")) { x = x - 5 }
  if (pressed("ArrowRight")) { x = x + 5 }

  clear()
  color("white")
  circle(x, y, 10)
  sleep(16)
}

Graphics

Graphics functions draw to a canvas. The canvas appears automatically when any graphics function is called.

Output Modes

Tiny has two output modes:

  • Text Mode: When you only use print(), output appears as scrollable text.
  • Mixed Mode: When you use graphics functions like clear(), a canvas appears. Any print() statements will appear in a text area below the canvas, similar to the Apple II's split-screen mode.
// Mixed mode example
clear()
color("red")
circle(100, 100, 50)
print("Score: 100")  // appears below canvas

Canvas Setup

FunctionDescription
clear()Clear canvas to black
fullscreen()Enter fullscreen mode (ESC to exit)
width()Get current canvas width
height()Get current canvas height

Drawing

FunctionDescription
color(name)Set drawing color (see Colors below)
rect(x, y, w, h)Draw rectangle at (x,y) with width w, height h
circle(x, y, r)Draw circle at (x,y) with radius r
line(x1, y1, x2, y2)Draw line from (x1,y1) to (x2,y2)
triangle(x1,y1,x2,y2,x3,y3)Draw triangle with three vertices
text(x, y, str)Draw text at position (x,y)

Drawing Modes

FunctionDescription
fill()Shapes are filled solid (default)
stroke()Shapes are outlined only

Example: Bouncing Ball

let x = 100
let y = 100
let dx = 3
let dy = 2

while (true) {
  // Move
  x = x + dx
  y = y + dy

  // Bounce off walls
  if (x < 10 or x > width() - 10) { dx = -dx }
  if (y < 10 or y > height() - 10) { dy = -dy }

  // Draw
  clear()
  color("red")
  circle(x, y, 10)

  sleep(16)
}

Colors

Available color names for the color() function:

User-defined

Declaration

function greet(name) {
  print("Hello, " + name)
}

greet("Connor")  // prints: Hello, Connor

Return Values

function add(a, b) {
  return a + b
}

let sum = add(3, 4)  // sum = 7

Multiple Parameters

function volume(width, height, depth) {
  return width * height * depth
}

print(volume(2, 3, 4))  // prints: 24

Recursion

Functions can call themselves:

function factorial(n) {
  if (n <= 1) {
    return 1
  }
  return n * factorial(n - 1)
}

print(factorial(5))  // prints: 120

Classes

Objects let you work with complex things by building them from simpler things, and those from even simpler things—all the way down to primitives.

  • Compose: Bundle variables into concepts (Player = name + x + y + health)
  • Nest: Build bigger concepts from smaller ones (Team has Players, Game has Teams)
  • Pass around: One reference handles the whole bundle
  • Organize: Code lives with the data it operates on

Without Classes vs With Classes

Without classes, you might track a player like this:

// Without classes - messy!
let player1X = 100
let player1Y = 200
let player1Health = 10
let player1Name = "Connor"

let player2X = 300
let player2Y = 200
let player2Health = 10
let player2Name = "Alex"

// Functions need lots of parameters
function movePlayer(x, y, dx, dy) { ... }

With classes, each player is a self-contained object:

// With classes - clean!
class Player {
  name, x, y, health,

  move(dx, dy) {
    this.x = this.x + dx
    this.y = this.y + dy
  }
}

let player1 = new Player("Connor", 100, 200, 10)
let player2 = new Player("Alex", 300, 200, 10)

player1.move(5, 0)  // player1 moves itself

Definition

Define a class with fields (data) and methods (behavior):

class Player {
  name,
  x,
  y,
  health,

  takeDamage(amount) {
    this.health = this.health - amount
  }
}

Fields are listed first (separated by commas), followed by methods.

Instances

Create an instance with new, passing values for each field in order:

let p = new Player("Connor", 100, 200, 10)
print(p.name)    // "Connor"
print(p.x)       // 100
print(p.health)  // 10

Modifying Fields

Access and modify fields with dot notation:

p.x = 150
p.health = p.health - 1
print(p.x)       // 150
print(p.health)  // 9

Methods

Methods are functions that operate on instance data. Use this to access fields:

class Point {
  x,
  y,

  move(dx, dy) {
    this.x = this.x + dx
    this.y = this.y + dy
  }

  distanceFromOrigin() {
    return this.x + this.y
  }
}

let pt = new Point(10, 20)
pt.move(5, -3)
print(pt.x)  // 15
print(pt.y)  // 17
print(pt.distanceFromOrigin())  // 32

Methods with Return Values

class Rectangle {
  width,
  height,

  area() {
    return this.width * this.height
  }
}

let r = new Rectangle(10, 5)
print(r.area())  // 50