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 (
fooandFooare 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:
- First checks inner's scope → finds
innerVar - Checks outer's scope (parent) → finds
outerVar - 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
| Operator | Description | Example |
|---|---|---|
+ | Addition | 5 + 3 → 8 |
- | Subtraction | 5 - 3 → 2 |
* | Multiplication | 5 * 3 → 15 |
/ | Division | 15 / 3 → 5 |
% | Modulo (remainder) | 7 % 3 → 1 |
-x | Negation | -5 |
Comparison
| Operator | Description | Example |
|---|---|---|
equals | Equal to | 5 equals 5 → true |
not equals | Not equal to | 5 not equals 3 → true |
< | Less than | 3 < 5 → true |
> | Greater than | 5 > 3 → true |
<= | Less than or equal | 3 <= 3 → true |
>= | Greater than or equal | 5 >= 5 → true |
Logical
| Operator | Description | Example |
|---|---|---|
and | Logical AND | true and false → false |
or | Logical OR | true or false → true |
not | Logical NOT | not true → false |
String
The + operator concatenates strings:
let greeting = "Hello, " + "World!" // "Hello, World!"
let msg = "Age: " + 25 // "Age: 25"
Precedence
From highest to lowest:
not,-(unary)*,/,%+,-<,>,<=,>=equals,not equalsandor
Use parentheses to override: (2 + 3) * 4 → 20
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
| Function | Description | Example |
|---|---|---|
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.
| Function | Description |
|---|---|
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
| Function | Description | Example |
|---|---|---|
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. Anyprint()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
| Function | Description |
|---|---|
clear() | Clear canvas to black |
fullscreen() | Enter fullscreen mode (ESC to exit) |
width() | Get current canvas width |
height() | Get current canvas height |
Drawing
| Function | Description |
|---|---|
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
| Function | Description |
|---|---|
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