Variables and logic

Expressions, conditionals and live values — prototypes that keep score.

A prototype can remember things. Variables (the Variables panel) hold numbers, text and on/off flags; actions change them; layers read them live. That is enough to build a working settings screen, a shopping cart that adds up, or a game HUD whose health bar drains when you take a hit — without a screen per state.

The official Game UI templates are built entirely from what this page describes. Open one and press Present.

Play one

Play a game menu: keys, a variable and a conditional · 0:04
Description

The EMBERFALL game UI with its main menu selected. Present fills the screen with the menu. The down arrow moves the highlight from item to item (each press sets the menu’s focus variable to an expression), and Enter runs a conditional that opens the screen the focused item names.

  1. Open a Game UI template (New → File, search game).
  2. Select a screen — the Main menu — and click Present.
  3. Press ↓ to move through the menu, and Enter to open the item. Each press runs a Set variable with an expression; Enter runs a Conditional.

Set variable to an expression

Set variable sets a variable to a value, flips an on/off one, or — pick Expression — works the new value out when the action runs:

ExpressionWhat it does
health - 15Takes 15 off health
min(maxHealth, health + 25)Heals, but never past the maximum
(focus + 1) % 5Moves a menu's focus down, wrapping round
score + 150Adds to a score
upper($key)The key that was pressed (on a Key down trigger), as a capital

The field says what the expression comes to now, or what is wrong with it.

A number set with a Glide eases there in present mode: the variable holds its new value at once — the next action and every condition see it — while bars, knobs and readouts bound to it slide over the glide's length. A score counts up; a health bar drains.

Conditional

Conditional runs different actions depending on the variables. Each branch has a condition and a list of actions; the first branch whose condition holds runs, in order. Add Else for when none does.

  • If health <= 0 → Navigate to Game over
  • Else if health < 30 → Set warning to 1
  • Else → nothing

Branch actions are ordinary actions — Set variable, Navigate, Open overlay, Change to, even another Conditional — and they run one after another, so a later one sees what an earlier one changed.

See what a layer reads

See the live values a layer reads · 0:04
Description

The EMBERFALL game UI in Prototype mode. A menu item’s label on the Main menu screen is selected, and the Prototype panel shows its Live values: its opacity and its sideways position are expressions of the menu’s focus variable, so the focused item lights up and slides out.

  1. Switch to Prototype in the top bar.
  2. Select a layer — a menu item's label here.
  3. Live values in the Prototype panel lists the properties bound to expressions: this label's opacity and position follow the menu's focus variable, so the focused item lights up. Its Interactions are listed below.

Live values

Select a layer and open Live values in the Prototype panel to bind one of its properties to an expression:

PropertyExample
Widthhealth / maxHealth * 320 — a bar that follows health
Textpad(score, 6) — a score as 000420
Visiblehealth < 30 — a warning that shows only when it should
Opacity, X, Y, rotation, scalespeed / 300 * 240 — a speedometer needle

Live values are worked out wherever the layer is drawn — on the canvas, in present mode as variables change, and in exports — from the variables' current values. A text bound straight to a number shows it as words.

The expression language

  • Numbers, "text", true / false; + - * / % (+ joins text), comparisons == != < <= > >=, && / and, || / or, ! / not, and condition ? this : that.
  • Variables by name: player/health, or just health when no other variable ends in that name; a name with spaces in braces, {player/max health}.
  • $key: the key a Key down trigger fired with (set Key down to no key to hear every key, then branch on it).
  • Functions: min max clamp(v, lo, hi) abs sign floor ceil round(v, places) sqrt pow mix(a, b, t) random(lo, hi) now() · text: text pad(n, digits) fixed(n, places) group(n) (thousands) clock(ms) len upper today() (today's date as words).
  • Records of a collection, by its name (on a published site, the signed-in visitor's own): count("Trips"), count("Trips", "status", "Booked") (each pair is a field and the value it must have), sum("Sessions", "minutes"), best(…) (the largest), distinct("Dishes", "by") (how many different values), and by the day — countSince("Sessions", 6) (the last seven days; 0 is today), sumSince("Sessions", "minutes", 6), countOn("Checks", 1) (yesterday's), streak("Checks", "habit", item_name) (days in a row up to today, or up to yesterday while today has none yet). Inside a list's copy, item_<field> is that copy's record, item_id its id, item_age how many days ago it was made, and in a shared collection item_by who made it and item_mine whether that was the visitor.
  • An action fired from a list's copy reads that copy's record too: Set variable editing to item_id opens that one copy's edit form (show the form where editing == item_id).

Nothing is evaluated as code: an expression can only read variables and call these functions. Dividing by zero gives 0, and an expression that does not work out leaves the variable (or the property) as it was.

Keys in a prototype

A Key down reaches every layer listening for it on the screen — or, while an overlay is open, in that overlay — in layer order, so one key can drain a bar, flash a hit marker and tick a counter, each layer listening for itself. It stops once an action goes to another screen or opens or closes an overlay. A screen that listens for keys keeps all its keys: W, the arrows and Space play the prototype instead of moving the slideshow; Escape still ends it unless the screen uses Escape itself.

Change to onto the variant an instance already shows plays that variant again from its start — a damage flash or a pickup pop replays on every hit.