Skip to main content

Lesson 1.2: Control Flow & Loops

Learning Objectives

By the end of this lesson, you will:

  • Write if/then/else statements to make decisions
  • Use inline conditional expressions for cleaner code
  • Create loops with for, while, and repeat
  • Understand when to use each type of loop
  • Control loop execution with break and continue

Syntax Note: Coming from JavaScript or After Effects?

JavaScript / After Effects Comparison
ConceptLuauJavaScriptAfter Effects
If block startif x thenif (x) {if (x) {
Else ifelseifelse ifelse if
Block endend}}
For loopfor i = 1, 10 dofor (let i = 1; i <= 10; i++)for (var i = 1; i <= 10; i++)
Whilewhile x do ... endwhile (x) { }while (x) { }

Key differences:

  • Luau uses words (then, do, end) instead of braces ({, })
  • elseif is ONE word in Luau (not else if)
  • No parentheses around conditions: if x > 5 then not if (x > 5) then

Where Does Control Flow Go?

Control flow code can go in any lifecycle function:

FunctionWhen to use
init()One-time decisions during setup
advance()Per-frame logic (game rules, state changes)
draw()Conditional rendering
function init(self: MyScript): boolean
-- One-time decision
if self.difficulty == "hard" then
self.enemyHealth = 200
else
self.enemyHealth = 100
end
return true
end

function advance(self: MyScript, elapsed: number): boolean
-- Per-frame decision
if self.health <= 0 then
self.gameOver = true
end
return true
end

Conditionals: Making Decisions

If-Then-Else

The most basic way to make decisions:

if condition then
-- runs if condition is true
end

The full structure:

if condition then
-- runs if condition is true
elseif otherCondition then
-- runs if first was false and this is true
else
-- runs if all conditions were false
end
Don't Forget then and end!

Every if needs then. Every block needs end.

-- WRONG: Missing 'then'
if health > 0
print("Alive")
end

-- WRONG: Missing 'end'
if health > 0 then
print("Alive")

-- CORRECT:
if health > 0 then
print("Alive")
end
JavaScript Comparison
// JavaScript
if (health > 0) {
console.log("Alive");
} else if (health === 0) {
console.log("Just died");
} else {
console.log("Dead");
}
-- Luau
if health > 0 then
print("Alive")
elseif health == 0 then
print("Just died")
else
print("Dead")
end

Differences:

  • No parentheses around condition
  • then instead of {
  • end instead of }
  • elseif (one word) instead of else if (two words)
  • == for equality (same in both)

If-Then-Else Expressions (Inline Conditionals)

Luau has inline conditional expressions — perfect for assigning values based on conditions:

local result = if condition then valueA else valueB

This is like JavaScript's ternary operator (? :):

-- Luau
local status = if health > 0 then "Alive" else "Dead"
// JavaScript equivalent
const status = health > 0 ? "Alive" : "Dead";

Chained expressions:

local grade = if score >= 90 then "A"
elseif score >= 80 then "B"
elseif score >= 70 then "C"
elseif score >= 60 then "D"
else "F"
After Effects Comparison

After Effects uses the ternary operator:

// After Effects
var status = health > 0 ? "Alive" : "Dead";
var grade = score >= 90 ? "A" : score >= 80 ? "B" : "C";

Luau's inline if is more readable for chained conditions.


Loops: Repeating Code

Numeric For Loop

The most common loop — iterate a specific number of times:

for i = start, stop do
-- i goes from start to stop (inclusive)
end

Examples:

-- Count from 1 to 5
for i = 1, 5 do
print(i) -- 1, 2, 3, 4, 5
end

-- Count from 0 to 4 (5 iterations, starting at 0)
for i = 0, 4 do
print(i) -- 0, 1, 2, 3, 4
end

With a step value:

for i = start, stop, step do
-- i changes by 'step' each iteration
end
-- Count down from 5 to 1
for i = 5, 1, -1 do
print(i) -- 5, 4, 3, 2, 1
end

-- Even numbers from 2 to 10
for i = 2, 10, 2 do
print(i) -- 2, 4, 6, 8, 10
end

-- Every third number
for i = 0, 12, 3 do
print(i) -- 0, 3, 6, 9, 12
end
Common Mistake: Counting Down Without Step
-- WRONG: This won't run at all!
for i = 10, 1 do
print(i) -- Nothing prints!
end

-- CORRECT: Include the -1 step
for i = 10, 1, -1 do
print(i) -- 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
end
JavaScript Comparison
// JavaScript: Count 1 to 5
for (let i = 1; i <= 5; i++) {
console.log(i);
}

// JavaScript: Count down 5 to 1
for (let i = 5; i >= 1; i--) {
console.log(i);
}
-- Luau: Count 1 to 5
for i = 1, 5 do
print(i)
end

-- Luau: Count down 5 to 1
for i = 5, 1, -1 do
print(i)
end

Key differences:

  • Luau: for i = start, stop, step do
  • JS: for (let i = start; i <= stop; i += step)
  • Luau's stop is inclusive, JS uses a condition

While Loop

Repeats while a condition is true:

while condition do
-- runs while condition is true
end

Example: Countdown

local count = 5
while count > 0 do
print(count)
count -= 1
end
print("Blast off!")

Output:

5
4
3
2
1
Blast off!
JavaScript Comparison
// JavaScript
let count = 5;
while (count > 0) {
console.log(count);
count--;
}
-- Luau
local count = 5
while count > 0 do
print(count)
count -= 1
end

Repeat Until

Repeats until a condition becomes true. Always runs at least once!

repeat
-- this code runs at least once
until condition -- stops when condition is true

Example: Retry until success

local attempts = 0
repeat
attempts += 1
print(`Attempt {attempts}`)
until attempts >= 3

print("Done!")

Output:

Attempt 1
Attempt 2
Attempt 3
Done!
JavaScript Comparison

JavaScript has do...while which is similar:

// JavaScript do-while
let attempts = 0;
do {
attempts++;
console.log(`Attempt ${attempts}`);
} while (attempts < 3); // Note: condition is OPPOSITE

// Luau repeat-until
local attempts = 0
repeat
attempts += 1
print(`Attempt {attempts}`)
until attempts >= 3 -- Stops when TRUE

Key difference:

  • JS while continues while condition is TRUE
  • Luau until continues until condition is TRUE (opposite logic!)

Break and Continue

break — Exit the loop immediately:

for i = 1, 100 do
if i == 5 then
break -- Stop at 5
end
print(i)
end
-- Output: 1, 2, 3, 4

continue — Skip to the next iteration:

for i = 1, 5 do
if i == 3 then
continue -- Skip 3
end
print(i)
end
-- Output: 1, 2, 4, 5
JavaScript Comparison
// JavaScript - break and continue work the same
for (let i = 1; i <= 5; i++) {
if (i === 3) continue; // Skip 3
console.log(i);
}
// Output: 1, 2, 4, 5

Exercises

Exercise 1: Health Tier ⭐

Premise

Control flow is how your script makes decisions. In Rive, those decisions drive which animation state or visual response should appear.

Goal

By the end of this exercise, you will use an if/elseif chain to categorize a health value.

Use Case

A health bar needs a label like Critical, Low, Medium, or Full. That label can drive color changes or trigger warning animations. If your conditions are out of order, the wrong state will show even when the number is correct.

Example scenarios:

  • Health warning logic
  • Difficulty gating based on thresholds

Setup

In Rive Editor:

  1. Create the script:

    • Assets panel → + → Script → Node Script
    • Name it Exercise1_HealthTier
  2. Attach and run:

    • Attach to any shape and press Play

Starter Code

--!strict

export type Exercise1 = {}

function init(self: Exercise1): boolean
local health = 45
-- TODO: Set status based on health ranges
-- <= 0: "Dead"
-- <= 20: "Critical"
-- <= 40: "Low"
-- <= 70: "Medium"
-- <= 90: "High"
-- else: "Full"
local status = "TODO"

print(`ANSWER: {status}`)
return true
end

function draw(self: Exercise1, renderer: Renderer)
end

return function(): Node<Exercise1>
return {
init = init,
draw = draw,
}
end

Assignment

Complete these tasks:

  1. Use an if/elseif chain to compute status
  2. Keep the order from lowest to highest ranges

Expected Output

Your console output should display the health tier status based on where the health value (45) falls in the ordered range checks. The status will be one of: Dead, Critical, Low, Medium, High, or Full.


Verify Your Answer

Verify Your Answer

Checklist

  • Conditions are ordered from lowest to highest
  • status equals "Medium" for health 45
  • Output matches the expected line

Exercise 2: Inline Labels ⭐

Premise

Inline conditionals are compact and make assignments readable when you only need a value, not a full block.

Goal

By the end of this exercise, you will use inline conditionals to compute three labels.

Use Case

You are assigning labels for score grade, premium discount, and rank title. These are values you might bind to a text run in a HUD or debug panel.

Example scenarios:

  • Rank badges and labels
  • Feature gating for premium users

Setup

In Rive Editor:

  1. Create the script:

    • Assets panel → + → Script → Node Script
    • Name it Exercise2_InlineLabels
  2. Attach and run:

    • Attach to any shape and press Play

Starter Code

--!strict

export type Exercise2 = {}

function init(self: Exercise2): boolean
local score = 75
local isPremium = true
local level = 5

-- TODO 1: grade should be "A/B/C/D/F" based on score
local grade = "?"
-- TODO 2: discount should be 20 if premium else 0
local discount = 0
-- TODO 3: title should be "Master" (>=10), "Expert" (>=5), else "Novice"
local title = "?"

print(`ANSWER: {grade},{discount},{title}`)
return true
end

function draw(self: Exercise2, renderer: Renderer)
end

return function(): Node<Exercise2>
return {
init = init,
draw = draw,
}
end

Assignment

Complete these tasks:

  1. Use inline if/elseif expressions for grade
  2. Use inline if for discount
  3. Use inline if/elseif for title

Expected Output

Your console output should display three values separated by commas:

  • The letter grade based on score thresholds (90+, 80+, 70+, 60+, else)
  • The discount percentage (premium users get a discount)
  • The title based on level (10+, 5+, else)

Verify Your Answer

Verify Your Answer

Checklist

  • Inline conditionals use if ... then ... else ...
  • grade is "C" for score 75
  • Output matches the expected line

Exercise 3: Loop Totals ⭐

Premise

Loops let you repeat logic without copy-pasting. They are essential for summing values or iterating over ranges.

Goal

By the end of this exercise, you will use a numeric for loop to compute a sum and an even count.

Use Case

You are preparing a debug readout for a chart. You need the total of a range and the count of even markers. These loop patterns show up everywhere in data-driven animations.

Example scenarios:

  • Summing data points for a bar chart
  • Counting markers for grid lines

Setup

In Rive Editor:

  1. Create the script:

    • Assets panel → + → Script → Node Script
    • Name it Exercise3_LoopTotals
  2. Attach and run:

    • Attach to any shape and press Play

Starter Code

--!strict

export type Exercise3 = {}

function init(self: Exercise3): boolean
local sum = 0
local evenCount = 0

-- TODO 1: for i = 1, 10 do sum += i end
-- TODO 2: inside the same loop, increment evenCount for even i

print(`ANSWER: sum={sum},evens={evenCount}`)
return true
end

function draw(self: Exercise3, renderer: Renderer)
end

return function(): Node<Exercise3>
return {
init = init,
draw = draw,
}
end

Assignment

Complete these tasks:

  1. Add a numeric for loop from 1 to 10
  2. Sum the values into sum
  3. Count even numbers into evenCount

Expected Output

Your console output should display:

  • sum= — the total of all numbers from 1 to 10
  • evens= — the count of even numbers in that range

Verify Your Answer

Verify Your Answer

Checklist

  • Sum is 55
  • Even count is 5
  • Output matches the expected line

Exercise 4: While Countdown ⭐

Premise

While loops are best when you do not know the exact number of iterations ahead of time. You loop until a condition changes.

Goal

By the end of this exercise, you will use a while loop to count down and track steps.

Use Case

A cooldown timer counts down to zero and you want to know how many ticks happened. This mirrors simple timer loops used in scripts.

Example scenarios:

  • Cooldowns and timers
  • Resource depletion

Setup

In Rive Editor:

  1. Create the script:

    • Assets panel → + → Script → Node Script
    • Name it Exercise4_WhileCountdown
  2. Attach and run:

    • Attach to any shape and press Play

Starter Code

--!strict

export type Exercise4 = {}

function init(self: Exercise4): boolean
local energy = 3
local steps = 0

-- TODO: Use a while loop to decrement energy to 0
-- Increment steps each iteration

print(`ANSWER: steps={steps},energy={energy}`)
return true
end

function draw(self: Exercise4, renderer: Renderer)
end

return function(): Node<Exercise4>
return {
init = init,
draw = draw,
}
end

Assignment

Complete these tasks:

  1. Loop while energy > 0
  2. Decrement energy by 1 each iteration
  3. Increment steps each iteration

Expected Output

Your console output should display:

  • steps= — how many iterations the while loop executed
  • energy= — the final energy value (should be depleted to zero)

Verify Your Answer

Verify Your Answer

Checklist

  • While loop runs until energy is 0
  • steps ends at 3
  • Output matches the expected line

Exercise 5: Repeat Until ⭐⭐

Premise

repeat-until always runs at least once. This is useful for retry logic or initialization loops.

Goal

By the end of this exercise, you will use repeat-until to simulate a retry sequence.

Use Case

You are polling for a resource that becomes available on the third attempt. You want to track attempts and stop once it succeeds.

Example scenarios:

  • Retrying a load or setup step
  • Validating Input until it passes

Setup

In Rive Editor:

  1. Create the script:

    • Assets panel → + → Script → Node Script
    • Name it Exercise5_RepeatUntil
  2. Attach and run:

    • Attach to any shape and press Play

Starter Code

--!strict

export type Exercise5 = {}

function init(self: Exercise5): boolean
local attempts = 0
local success = false

-- TODO: Repeat until success is true
-- - Increment attempts
-- - Set success to true when attempts >= 3

print(`ANSWER: attempts={attempts},success={success}`)
return true
end

function draw(self: Exercise5, renderer: Renderer)
end

return function(): Node<Exercise5>
return {
init = init,
draw = draw,
}
end

Assignment

Complete these tasks:

  1. Use repeat-until so the loop runs at least once
  2. Increment attempts each loop
  3. Set success to true when attempts >= 3

Expected Output

Your console output should display:

  • attempts= — how many times the loop ran before success
  • success= — the final boolean state (true when threshold reached)

Verify Your Answer

Verify Your Answer

Checklist

  • Loop runs at least once
  • Attempts stop at 3
  • Output matches the expected line

Exercise 6: Break and Continue ⭐⭐

Premise

break and continue let you control loop flow. You can skip unwanted items or stop early when a condition is met.

Goal

By the end of this exercise, you will use both break and continue in one loop.

Use Case

You are scanning items and want to skip every third entry, but stop once you reach a certain point. This pattern is common in search logic or filtering.

Example scenarios:

  • Skipping invalid entries
  • Stopping once a target is found

Setup

In Rive Editor:

  1. Create the script:

    • Assets panel → + → Script → Node Script
    • Name it Exercise6_BreakContinue
  2. Attach and run:

    • Attach to any shape and press Play

Starter Code

--!strict

export type Exercise6 = {}

function init(self: Exercise6): boolean
local sum = 0
local count = 0

for i = 1, 10 do
-- TODO 1: Skip multiples of 3 with continue
-- TODO 2: Break when i == 6
-- TODO 3: Add i to sum and increment count
end

print(`ANSWER: sum={sum},count={count}`)
return true
end

function draw(self: Exercise6, renderer: Renderer)
end

return function(): Node<Exercise6>
return {
init = init,
draw = draw,
}
end

Assignment

Complete these tasks:

  1. Use continue when i % 3 == 0
  2. Use break when i == 6
  3. Sum the processed values and count them

Expected Output

Your console output should display:

  • sum= — the total of processed values (after skipping and breaking)
  • count= — how many numbers were actually added to the sum

The loop processes values 1-5, skipping multiples of 3 and breaking at 6.


Verify Your Answer

Verify Your Answer

Checklist

  • continue skips 3 and 6
  • break stops at 6
  • Output matches the expected line

Exercise 7: State Switch ⭐⭐

Premise

Real scripts combine multiple conditions to determine a state. This is the basis of state machines and animation transitions.

Goal

By the end of this exercise, you will use an ordered if/elseif chain to set a state and update stamina.

Use Case

A character can be dead, exhausted, wounded, or normal. That state determines which animation should play. You also regenerate stamina when not dead.

Example scenarios:

  • Player state machines
  • UI state transitions

Setup

In Rive Editor:

  1. Create the script:

    • Assets panel → + → Script → Node Script
    • Name it Exercise7_StateSwitch
  2. Attach and run:

    • Attach to any shape and press Play

Starter Code

--!strict

export type Exercise7 = {}

function init(self: Exercise7): boolean
local health = 25
local stamina = 10
local state = "idle"

-- TODO 1: Set state based on health/stamina
-- <=0: "dead"
-- stamina <=0: "exhausted"
-- health <=30: "wounded"
-- else: "ok"

-- TODO 2: If not dead and stamina < 20, add 5 stamina

print(`ANSWER: state={state},stamina={stamina}`)
return true
end

function draw(self: Exercise7, renderer: Renderer)
end

return function(): Node<Exercise7>
return {
init = init,
draw = draw,
}
end

Assignment

Complete these tasks:

  1. Use ordered if/elseif to set state
  2. Regenerate stamina if the state is not dead

Expected Output

Your console output should display:

  • state= — the character's state based on health/stamina conditions
  • stamina= — the final stamina value after regeneration (if applicable)

Verify Your Answer

Verify Your Answer

Checklist

  • Ordered conditions produce "wounded" for health 25
  • Stamina increases by 5
  • Output matches the expected line

Knowledge Check

Q:What keyword ends an if-then-else block in Luau?
Q:What is the syntax for a numeric for loop that counts from 10 down to 1?
Q:Which loop always executes at least once?
Q:What is the Luau keyword for 'else if'?
Q:What does 'continue' do in a loop?

Common Mistakes

Top Mistakes
  1. Forgetting thenif x > 5 then not if x > 5
  2. Forgetting do in loopsfor i = 1, 10 do not for i = 1, 10
  3. Forgetting end — Every if, for, while, function needs end
  4. Wrong step in countdownfor i = 10, 1, -1 do not for i = 10, 1 do
  5. Using else if instead of elseif — It's one word in Luau!

Quick Reference

-- If-Then-Else
if condition then
-- code
elseif other then
-- code
else
-- code
end

-- Inline conditional
local x = if condition then valueA else valueB

-- For loop
for i = start, stop, step do
-- code
end

-- While loop
while condition do
-- code
end

-- Repeat until
repeat
-- code
until condition

-- Loop control
break -- exit loop
continue -- skip to next iteration

Next Steps