A programming language inspired by HyperTalk — where code reads like English.
Bubble is a dynamically typed, interpreted programming language with English-like syntax inspired by Apple's HyperTalk. It's designed to be approachable, readable, and fun. If you've ever wished your code could sound more like a conversation, Bubble is for you.
put "Hello, World!" into greeting
say greeting
- Installation
- Quick Start
- Language Guide
- Built-in Functions Reference
- Examples
- The REPL
- Monorepo Layout
- Project Structure
- Building from Source
- License
If you already have the compiled binary, simply place it somewhere on your PATH:
chmod +x bubble
sudo mv bubble /usr/local/bin/Requires Go 1.21 or later:
git clone https://github.com/bubblecode/bubble.git
cd bubble
go build -o bubble .Verify the installation:
bubble --version
# Bubble v0.1.0Create a file called hello.bubble:
say "Hello, World!"
Run it:
bubble hello.bubbleThe .bubble extension is optional on the command line — bubble hello works too.
Run bubble with no arguments to enter interactive mode:
bubble ____ _ _ _
| __ ) _ _| |__ | |__ | | ___
| _ \| | | | '_ \| '_ \| |/ _ \
| |_) | |_| | |_) | |_) | | __/
|____/ \__,_|_.__/|_.__/|_|\___|
🫧 Bubble Language v0.1.0
Type "bye" or "quit" to exit.
bubble> say "Hello!"
Hello!
bubble>
Comments begin with -- and continue to the end of the line:
-- This is a comment
say "Hello" -- This is also a comment
Bubble has two ways to assign variables, both reading like natural English.
put "Bubble" into language
put 42 into answer
put true into isReady
set language to "Bubble"
set answer to 42
set isReady to true
Both forms are interchangeable. Use whichever reads better in context.
The add command increments a numeric variable:
put 0 into score
add 10 to score
add 5 to score
say score
-- Output: 15
Variable names can contain letters, digits, and underscores. They are case-insensitive — myName, MyName, and MYNAME all refer to the same variable.
put "Alice" into myName
say MyName
-- Output: Alice
Bubble has five core data types:
| Type | Examples | Notes |
|---|---|---|
| Number | 42, 3.14, -7 |
All numbers are 64-bit floats |
| String | "hello", 'Bubble 🫧' |
Enclosed in double or single quotes |
| Boolean | true, false |
Logical values |
| List | [1, 2, 3], ["a", "b"] |
Ordered collections |
| Null | (no literal) | Absence of a value |
Use say to print to the console:
say "Hello, World!"
say 2 + 2
say "The answer is" && 42
say automatically adds a newline at the end.
Use ask to prompt the user for input:
ask "What is your name?"
say "Hello," && it
By default, the answer is stored in the special variable it. You can specify a different variable:
ask "What is your name?" into name
ask "How old are you?" into age
say name && "is" && age && "years old."
Bubble supports standard arithmetic operators:
say 2 + 3 -- Addition: 5
say 10 - 4 -- Subtraction: 6
say 6 * 7 -- Multiplication: 42
say 20 / 4 -- Division: 5
say 17 mod 5 -- Modulo: 2
say 2 ^ 10 -- Exponentiation: 1024
Parentheses control order of operations:
say (2 + 3) * 4 -- 20
say 2 + 3 * 4 -- 14
Operator precedence (highest to lowest):
^(power)*,/,mod+,-&,&&(string concatenation)<,>,<=,>=,containsis,=,is not,<>notandor
Bubble uses HyperTalk-style concatenation operators:
-- & joins strings directly (no space)
say "Hello" & "World"
-- Output: HelloWorld
-- && joins strings with a space
say "Hello" && "World"
-- Output: Hello World
Building complex strings:
put "Hello" into greeting
put "World" into target
put greeting & ", " & target & "!" into message
say message
-- Output: Hello, World!
Strings can be enclosed in either double quotes or single quotes. Both are equivalent:
say "Hello, World!"
say 'Hello, World!'
This makes it easy to include one type of quote inside the other:
say 'She said "hello"'
say "It's a beautiful day"
Strings support the following escape sequences:
| Escape | Meaning |
|---|---|
\n |
Newline |
\t |
Tab |
\\ |
Literal \ |
\" |
Literal " |
\' |
Literal ' |
say "Line 1\nLine 2"
say "Col1\tCol2"
Check if a string contains a substring (case-insensitive):
put "Hello, Bubble World!" into text
say text contains "bubble" -- true
say text contains "python" -- false
Bubble supports both symbolic and English-style comparisons:
if x = 5 then ...
if x <> 5 then ...
if x < 10 then ...
if x > 3 then ...
if x <= 10 then ...
if x >= 0 then ...
if x is 5 then ...
if x is not 5 then ...
if x is less than 10 then ...
if x is greater than 3 then ...
if x is less than or equal to 10 then ...
if x is greater than or equal to 0 then ...
if x is equal to 5 then ...
if x is not equal to 5 then ...
String comparisons are case-insensitive (following HyperTalk convention):
if "Hello" is "hello" then
say "These are equal!"
end if
-- Output: These are equal!
if x > 0 and x < 100 then
say "x is between 1 and 99"
end if
if name is "Alice" or name is "Bob" then
say "Welcome!"
end if
if not isReady then
say "Not yet..."
end if
put 42 into age
if age is greater than or equal to 18 then
say "You are an adult."
end if
put 15 into temperature
if temperature is less than 0 then
say "It's freezing!"
else
say "It's not freezing."
end if
put 85 into score
if score is greater than or equal to 90 then
say "Grade: A"
else if score is greater than or equal to 80 then
say "Grade: B"
else if score is greater than or equal to 70 then
say "Grade: C"
else if score is greater than or equal to 60 then
say "Grade: D"
else
say "Grade: F"
end if
Bubble offers a rich variety of loop constructs.
repeat 5 times
say "Hello!"
end repeat
repeat with i from 1 to 10
say i
end repeat
-- Count by 5s
repeat with i from 0 to 50 by 5
say i
end repeat
repeat with i from 10 down to 1
say i & "..."
end repeat
say "Blast off!"
put 1 into n
repeat while n is less than or equal to 100
say n
put n * 2 into n
end repeat
put 0 into total
repeat until total is greater than or equal to 100
add 7 to total
end repeat
say "Total:" && total
repeat forever
ask "Say something (or 'quit'):" into input
if input is "quit" then
exit repeat
end if
say "You said:" && input
end repeat
Iterate over lists or characters in a string:
put ["apple", "banana", "cherry"] into fruits
repeat for each fruit in fruits
say "I like" && fruit
end repeat
repeat for each letter in "Bubble"
say letter
end repeat
-- Output: B u b b l e (each on its own line)
-- Skip an iteration with 'next repeat'
repeat with i from 1 to 10
if i mod 2 is 0 then
next repeat
end if
say i
end repeat
-- Output: 1 3 5 7 9
-- Break out of a loop with 'exit repeat'
repeat with i from 1 to 100
if i is greater than 5 then
exit repeat
end if
say i
end repeat
-- Output: 1 2 3 4 5
Bubble has two styles for defining callable routines.
function double(x)
return x * 2
end double
say double(21)
-- Output: 42
Handlers are defined with on and are ideal for procedures that perform side effects rather than returning values:
on greet name
say "Hello," && name & "! Welcome to Bubble! 🫧"
end greet
greet("World")
-- Output: Hello, World! Welcome to Bubble! 🫧
Both function and on support parameters and can call each other. Functions use return to produce a value. If a handler is called as a function, it returns null.
Parameters can be listed with or without parentheses:
-- With parentheses
function add(a, b)
return a + b
end add
-- Without parentheses (handler style)
on greet name, title
say "Hello," && title && name
end greet
Functions can call themselves:
function factorial(n)
if n is less than 2 then
return 1
end if
return n * factorial(n - 1)
end factorial
say factorial(10)
-- Output: 3628800
Functions capture the environment where they are defined. Each function call creates its own local scope:
put "global" into scope
function showScope
put "local" into scope
say "Inside:" && scope
end showScope
showScope()
say "Outside:" && scope
-- Output:
-- Inside: local
-- Outside: global
Lists are ordered collections enclosed in square brackets:
put [1, 2, 3, 4, 5] into numbers
put ["apple", "banana", "cherry"] into fruits
put [] into emptyList
put ["a", "b", "c", "d"] into letters
say itemof(1, letters) -- a
say itemof(3, letters) -- c
say firstitem(letters) -- a
say lastitem(letters) -- d
put [10, 20, 30] into nums
-- Change an item by index
put 99 into item 2 of nums
say nums
-- Output: [10, 99, 30]
-- Append an item
put append(nums, 40) into nums
say nums
-- Output: [10, 99, 30, 40]
-- Delete an item by index
delete item 1 of nums
say nums
-- Output: [99, 30, 40]
-- Insert at a position
put insertitem(nums, 2, 55) into nums
say nums
-- Output: [99, 55, 30, 40]
put [3, 1, 4, 1, 5, 9] into nums
say length(nums) -- 6
say count(nums) -- 6
say sort(nums) -- [1, 1, 3, 4, 5, 9]
say reverse(nums) -- [9, 5, 1, 4, 1, 3]
say shuffle(nums) -- (random order)
say indexof(4, nums) -- 3
say slice(nums, 2, 4) -- [1, 4] (items 2 through 4)
say range(5) -- [1, 2, 3, 4, 5]
say range(3, 7) -- [3, 4, 5, 6, 7]
say range(0, 10, 2) -- [0, 2, 4, 6, 8, 10]
say range(10, 0, -2) -- [10, 8, 6, 4, 2, 0]
put ["red", "green", "blue"] into colors
say join(colors, ", ")
-- Output: red, green, blue
put split("one,two,three", ",") into words
say words
-- Output: [one, two, three]
-- Clear the terminal screen
clear
-- Pause for a duration in seconds
wait 2
wait 0.5 seconds
Bubble provides several built-in constants:
| Constant | Value | Description |
|---|---|---|
true |
true |
Boolean true |
false |
false |
Boolean false |
empty |
"" |
The empty string |
quote |
" |
A literal double-quote character |
put empty into result
say "She said" && quote & "hello" & quote
-- Output: She said "hello"
| Function | Description | Example |
|---|---|---|
length(s) |
Length of a string or list | length("hello") → 5 |
uppercase(s) |
Convert to uppercase | uppercase("hi") → "HI" |
lowercase(s) |
Convert to lowercase | lowercase("HI") → "hi" |
trim(s) |
Strip leading/trailing whitespace | trim(" hi ") → "hi" |
reverse(s) |
Reverse a string or list | reverse("abc") → "cba" |
replace(s, old, new) |
Replace all occurrences | replace("hello", "l", "r") → "herro" |
mid(s, start, len) |
Substring from position (1-based) | mid("hello", 2, 3) → "ell" |
left(s, n) |
First n characters | left("hello", 3) → "hel" |
right(s, n) |
Last n characters | right("hello", 3) → "llo" |
char(n, s) |
Character at position n (1-based) | char(1, "hello") → "h" |
offset(needle, haystack) |
Position of first occurrence (1-based, 0 if not found) | offset("ll", "hello") → 3 |
repeattext(s, n) |
Repeat a string n times | repeattext("*", 5) → "*****" |
padright(s, width) |
Pad with trailing spaces | padright("hi", 6) → "hi " |
padleft(s, width) |
Pad with leading spaces | padleft("42", 5) → " 42" |
startswith(s, prefix) |
Check if string starts with prefix | startswith("hello", "he") → true |
endswith(s, suffix) |
Check if string ends with suffix | endswith("hello", "lo") → true |
split(s, sep) |
Split string into list | split("a,b,c", ",") → ["a","b","c"] |
join(list, sep) |
Join list into string | join(["a","b"], "-") → "a-b" |
chartonum(s) |
ASCII/Unicode value of first character | chartonum("A") → 65 |
numtochar(n) |
Character from ASCII/Unicode value | numtochar(65) → "A" |
tab(n) |
String of n spaces | tab(4) → " " |
newline() |
Newline character | — |
| Function | Description | Example |
|---|---|---|
abs(n) |
Absolute value | abs(-42) → 42 |
round(n) |
Round to nearest integer | round(3.7) → 4 |
floor(n) |
Round down | floor(3.7) → 3 |
ceil(n) |
Round up | ceil(3.2) → 4 |
trunc(n) |
Truncate decimal part | trunc(3.9) → 3 |
sqrt(n) |
Square root | sqrt(144) → 12 |
min(a, b, ...) |
Minimum of values | min(5, 3, 8) → 3 |
max(a, b, ...) |
Maximum of values | max(5, 3, 8) → 8 |
random(n) |
Random integer from 1 to n | random(10) → 7 |
sin(n) |
Sine (radians) | sin(0) → 0 |
cos(n) |
Cosine (radians) | cos(0) → 1 |
tan(n) |
Tangent (radians) | tan(0) → 0 |
log(n) |
Natural logarithm | log(1) → 0 |
exp(n) |
Euler's number raised to n | exp(1) → 2.718... |
| Function | Description | Example |
|---|---|---|
count(list) |
Number of items | count([1,2,3]) → 3 |
append(list, item) |
Add item to end | append([1,2], 3) → [1,2,3] |
list(a, b, ...) |
Create list from arguments | list(1, 2, 3) → [1,2,3] |
itemof(n, list) |
Get item at index (1-based) | itemof(2, [10,20,30]) → 20 |
setitem(list, n, val) |
Return list with item replaced | setitem([1,2,3], 2, 99) → [1,99,3] |
removeitem(list, n) |
Return list with item removed | removeitem([1,2,3], 2) → [1,3] |
insertitem(list, n, val) |
Return list with item inserted | insertitem([1,3], 2, 2) → [1,2,3] |
indexof(item, list) |
Find item position (0 = not found) | indexof("b", ["a","b","c"]) → 2 |
firstitem(list) |
First item | firstitem([10,20,30]) → 10 |
lastitem(list) |
Last item | lastitem([10,20,30]) → 30 |
sort(list) |
Return sorted list | sort([3,1,2]) → [1,2,3] |
shuffle(list) |
Return list in random order | — |
reverse(list) |
Return reversed list | reverse([1,2,3]) → [3,2,1] |
slice(list, start, end) |
Sub-list (1-based start) | slice([10,20,30,40], 2, 3) → [20,30] |
range(n) |
Generate [1..n] |
range(4) → [1,2,3,4] |
range(start, end) |
Generate [start..end] |
range(3,6) → [3,4,5,6] |
range(start, end, step) |
Generate with step | range(0, 10, 3) → [0,3,6,9] |
| Function | Description | Example |
|---|---|---|
typeof(val) |
Return type as string | typeof(42) → "NUMBER" |
isnumber(val) |
Check if value is numeric | isnumber("3.14") → true |
isempty(val) |
Check if value is empty | isempty("") → true |
number(val) |
Convert to number | number("42") → 42 |
text(val) |
Convert to string | text(42) → "42" |
| Function | Description | Example Output |
|---|---|---|
time() |
Current time | "3:04:05 PM" |
date() |
Full date | "Monday, January 2, 2006" |
shortdate() |
Short date | "1/2/06" |
seconds() |
Unix timestamp (seconds) | 1709827200 |
ticks() |
Unix timestamp (milliseconds) | 1709827200000 |
| Function | Description |
|---|---|
newline() |
Returns a newline character \n |
tab(n) |
Returns a string of n spaces |
say "Hello, World!"
say "Welcome to Bubble! 🫧"
repeat with i from 1 to 100
if i mod 15 is 0 then
say "FizzBuzz"
else if i mod 3 is 0 then
say "Fizz"
else if i mod 5 is 0 then
say "Buzz"
else
say i
end if
end repeat
function factorial(n)
if n is less than 2 then
return 1
end if
return n * factorial(n - 1)
end factorial
repeat with i from 1 to 10
say i & "! =" && factorial(i)
end repeat
Output:
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
function fibonacci(n)
if n is less than 2 then
return n
end if
return fibonacci(n - 1) + fibonacci(n - 2)
end fibonacci
repeat with i from 0 to 12
say "fib(" & i & ") =" && fibonacci(i)
end repeat
say "Multiplication table (1-5):"
repeat with row from 1 to 5
put "" into line
repeat with col from 1 to 5
put padleft(text(row * col), 4) into cell
put line & cell into line
end repeat
say line
end repeat
Output:
Multiplication table (1-5):
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
put random(100) into secret
put 0 into guesses
say "🫧 I'm thinking of a number between 1 and 100."
say ""
repeat forever
ask "Your guess:" into guess
put number(guess) into guess
add 1 to guesses
if guess is secret then
say "🎉 Correct! You got it in" && guesses && "guesses!"
exit repeat
else if guess is less than secret then
say "📈 Too low! Try higher."
else
say "📉 Too high! Try lower."
end if
end repeat
The examples/tictactoe.bubble file contains a full two-player Tic Tac Toe game with board rendering, win detection, and input validation. Run it with:
bubble examples/tictactoe.bubbleThe interactive REPL (Read-Eval-Print Loop) starts when you run bubble with no arguments. It supports:
- Multi-line input: blocks like
if,repeat,function, andonautomatically continue to the next line until closed withend. - Persistent state: variables and functions defined in one line carry over to the next.
- Expression evaluation: typing an expression shows its result.
bubble> put 2 + 2 into x
bubble> say x
4
bubble> function cube(n)
...> return n ^ 3
...> end cube
bubble> say cube(5)
125
bubble> say date()
Saturday, March 7, 2026
REPL commands:
| Command | Action |
|---|---|
help |
Show quick reference |
cancel |
Cancel the current unfinished block |
bye |
Exit the REPL |
quit |
Exit the REPL |
exit |
Exit the REPL |
If you forget to close a block like if ... end if or repeat ... end repeat, Bubble now tells you which end ... is missing. Messages include beginner-friendly tips (for spelling mistakes, unclosed blocks, and wrong function inputs).
Bubble now supports a monorepo structure so the language runtime and web app live in one repository.
apps/web: Astro website (React + Tailwind), ready to call Bubble WASM for long script compile tasks.packages/bubble-wasm: shared WASM compile bridge package used by the web app.packages/bubble-notebook: reserved package for notebook/Jupyter-style integration.
# Install all workspace dependencies
npm install
# Build Bubble runtime to WebAssembly (writes into apps/web/public/wasm)
npm run build:wasm
# Start Astro web app
npm run dev:webbubblecode/
├── package.json # npm workspaces for apps/* and packages/*
├── apps/
│ └── web/ # Astro + React + Tailwind frontend
│ └── public/wasm/ # Built WASM artifacts used in browser
├── cmd/
│ └── wasm/ # Go WASM entrypoint exposing bubbleCompile()
├── packages/
│ ├── bubble-wasm/ # Shared Bubble WASM compile API
│ └── bubble-notebook/ # Notebook integration placeholder
├── main.go # Bubble CLI entry point (Go)
├── go.mod # Go module definition
├── token/
│ └── token.go # Token types and keyword lookup
├── lexer/
│ └── lexer.go # Tokenizer (source → tokens)
├── ast/
│ └── ast.go # Abstract syntax tree node definitions
├── parser/
│ └── parser.go # Pratt parser (tokens → AST)
├── interpreter/
│ ├── environment.go # Value types and scoped environments
│ ├── interpreter.go # Tree-walking interpreter (AST → execution)
│ └── builtins.go # Built-in function implementations
├── repl/
│ └── repl.go # Interactive REPL
└── examples/
├── hello.bubble # Hello World
├── variables.bubble # Variable assignment demo
├── math.bubble # Arithmetic and math functions
├── strings.bubble # String operations
├── control_flow.bubble # Conditionals, loops, nested loops
├── functions.bubble # Functions, handlers, recursion
├── interactive.bubble # User input example
└── tictactoe.bubble # Full Tic Tac Toe game
# Clone the repository
git clone https://github.com/bubblecode/bubble.git
cd bubble
# Install JS workspace dependencies (Astro app + shared packages)
npm install
# Build Bubble WASM runtime used by the web app
npm run build:wasm
# Build
go build -o bubble .
# Run tests
go test ./...
# Run an example
./bubble examples/hello.bubble
# Build web app
npm run build:webBubble is open-source software. See the LICENSE file for details.