Skip to content

Helpers reference

Squiffy uses Handlebars as its text processing engine, providing a powerful set of helpers for dynamic content. This page is a quick reference for all available helpers.

Squiffy processes all text through Handlebars, which means you can use:

  • Double curly braces {{}} to output values and call helpers
  • Block helpers with {{#helper}}...{{/helper}} syntax
  • Comments with {{! comment text }}
  • Markdown formatting (converted to HTML automatically)

{{#if}}, {{#unless}} and {{else if}} accept conditions written with familiar operators:

{{#if gender = "male"}}You are male.{{/if}}
{{#if score > 100 and has_key}}You win!{{else}}Keep trying.{{/if}}
{{#unless (seen "intro") or count >= 3}}Welcome!{{/unless}}
Operator Meaning Equivalent helper
= or == equal to eq
!= not equal to ne
> greater than gt
>= greater than or equal to gte
< less than lt
<= less than or equal to lte
and or && both are true and
or or || either is true or
not or ! is not true not

Values can be attribute names, numbers, quoted strings, true/false, or helper calls in parentheses such as (seen "intro") or (length "inventory"). For attribute names containing spaces, use square brackets: {{#if [player name] = "Bob"}}.

Comparisons are worked out first, then not, then and, then or. So not gender = "male" means not (gender = "male"), and a or b and c means a or (b and c). Use parentheses to group things differently, e.g. (a or b) and c.

Each condition is converted to the equivalent helpers before it runs, so {{#if score > 100 and has_key}} is the same as {{#if (and (gt score 100) has_key)}}. You can still write conditions in that form if you prefer, and you’ll need it when using the comparison or logic helpers anywhere other than a condition, as described below.

Understanding parentheses vs double brackets

Section titled “Understanding parentheses vs double brackets”

When working with helpers, you’ll encounter two different bracket types:

Double brackets {{}} - Output values or call helpers:

{{score}} - Display an attribute
{{set "score" 100}} - Call a helper
{{#if has_key}}...{{/if}} - Block helper

Parentheses () - Subexpressions (helper arguments):

When you need to pass the result of one helper as an argument to another helper, use parentheses:

{{#if (seen "intro")}}Welcome back{{/if}}
{{random (array "red" "green" "blue")}}
{{#if (length "inventory") > 3}}Your bag is getting heavy{{/if}}

In these examples:

  • (seen "intro") - The seen helper returns true/false, which becomes the condition for if
  • (array "red" "green" "blue") - The array helper creates an array, which is passed to random
  • (length "inventory") - The length helper returns a number, which is compared with 3

Why not {{#if {{seen ...}}}}?

You cannot nest {{}} inside {{}} - that’s invalid Handlebars syntax. Use parentheses for subexpressions:

❌ Wrong: {{#if {{seen "intro"}}}}...{{/if}}
✅ Right: {{#if (seen "intro")}}...{{/if}}
❌ Wrong: {{random {{array "a" "b" "c"}}}}
✅ Right: {{random (array "a" "b" "c")}}

Nesting subexpressions:

You can nest parentheses to combine multiple helpers:

{{set "next_round" (random (array "music" "sport" "history"))}}

Here, (array "music" "sport" "history") is evaluated first, then its result is passed to random, and finally the result of random is passed to set.

See Attributes for detailed documentation and examples.

Helper Description Example
{{attribute}} Display attribute value (single-word names, always gets latest value) {{score}}
{{get "name"}} Get attribute value (for multi-word names or dynamic attribute names from expressions) {{get "player name"}}
Helper Parameters Description Example
{{set}} "attribute" value Set attribute to a value {{set "score" 100}}
{{unset}} "attribute" Set attribute to false {{unset "has_key"}}
{{inc}} "attribute" [amount] Increment by 1 or specified amount {{inc "score"}} or {{inc "score" 10}}
{{dec}} "attribute" [amount] Decrement by 1 or specified amount {{dec "health"}} or {{dec "health" 5}}

See Attributes for examples. In {{#if}} and {{#unless}}, you can use the operator instead (see Conditions).

Helper Description Operator Example
{{eq a b}} Returns true if a equals b a = b {{#if status = "complete"}}Done{{/if}}
{{ne a b}} Returns true if a not equals b a != b {{#if health != 0}}Still alive{{/if}}
{{gt a b}} Returns true if a > b a > b {{#if score > 100}}High score!{{/if}}
{{gte a b}} Returns true if a >= b a >= b {{#if level >= 5}}Advanced{{/if}}
{{lt a b}} Returns true if a < b a < b {{#if health < 20}}Low health{{/if}}
{{lte a b}} Returns true if a <= b a <= b {{#if age <= 18}}Minor{{/if}}

See Attributes for examples. In {{#if}} and {{#unless}}, you can use the operator instead (see Conditions).

Helper Description Operator Example
{{and ...}} Returns true if all arguments are truthy a and b {{#if has_key and door_locked}}Can unlock{{/if}}
{{or ...}} Returns true if any argument is truthy a or b {{#if is_admin or is_moderator}}Has powers{{/if}}
{{not value}} Negates a boolean value not a {{#if not game_over}}Continue{{/if}}

See Attributes for examples.

Helper Parameters Description Example
{{seen}} "name" Check if section/passage has been visited {{#if (seen "intro")}}Welcome back{{/if}}
{{at}} "name" or (array ...) Check if currently at a specific section {{#if (at "ending")}}Game over{{/if}}

See Attributes for examples.

Helper Parameters Description
{{section}} "name" [text="..."] [set="..."] Create a section link with optional custom text and attribute changes
{{passage}} "name" [text="..."] [set="..."] Create a passage link with optional custom text and attribute changes

Example:

{{section "next_room" text="Enter" set="visited=true, count+=1"}}
Helper Parameters Description Example
{{embed}} "name" Embed text from another section or passage {{embed "description"}}
{{array}} item1 item2 ... Create an array for use with other helpers {{array "red" "green" "blue"}}

See Embed text for an embed example.

These helpers let you manage arrays stored in attributes - useful for tracking items, available options, or game state.

Helper Parameters Description Example
{{array}} item1 item2 ... Create an array {{set "items" (array "a" "b" "c")}}
{{append}} "attribute" value Append value to array stored in attribute {{append "inventory" "sword"}}
{{prepend}} "attribute" value Prepend value to array stored in attribute {{prepend "queue" "urgent"}}
{{pop}} "attribute" Remove and return first element from array {{set "next" (pop "queue")}}
{{remove}} "attribute" value Remove first matching value from array {{remove "inventory" "key"}}
Helper Parameters Description Example
{{contains}} "attribute" value or (array ...) value Check if array contains value {{#if (contains "inventory" "key")}}...{{/if}}
{{length}} "attribute" or (array ...) Get number of elements in array {{length "inventory"}}

Track available game rounds:

{{!-- Initialize available rounds --}}
{{set "rounds" (array "Quiz" "Challenge" "Bonus")}}
{{!-- Pick a random round --}}
{{set "current" (random rounds)}}
You're playing: {{current}}
{{!-- Remove it so it can't be picked again --}}
{{remove "rounds" current}}
{{!-- Check if more rounds remain --}}
{{#if (length "rounds") > 0}}
[[Play next round]]
{{else}}
[[Final round]]
{{/if}}

Manage an inventory:

{{set "inventory" (array)}}
[pick up sword]:
{{append "inventory" "sword"}}
You picked up the sword.
[use key]:
{{#if (contains "inventory" "key")}}
{{remove "inventory" "key"}}
You used the key.
{{else}}
You don't have a key.
{{/if}}

Process a queue:

{{set "tasks" (array "Task A" "Task B" "Task C")}}
[next task]:
{{#if (length "tasks") > 0}}
Now doing: {{pop "tasks"}}
{{else}}
All tasks complete!
{{/if}}

See Dynamic text for detailed documentation and examples.

Helper Parameters Description
{{random}} (array ...) [set="attribute"] Select a random item from an array, optionally storing the result

Example:

The weather is {{random (array "sunny" "cloudy" "rainy") set="weather"}}.
Helper Parameters Description
{{#animate}}...{{/animate}} "effect" [trigger="link"] [style="..."] [loop=true] Animate text with visual effects

Available effects:

  • typewriter - Text appears character by character
  • toast - Text slides in from bottom
  • fadeIn - Text fades in
  • continue - Clickable link that pauses animation queue

Example:

{{#animate "typewriter"}}This text appears gradually.{{/animate}}
Helper Parameters Description
{{#label}}...{{/label}} "name" Create a labeled span that can be replaced later
{{#replace}}...{{/replace}} "label" Replace the content of a labeled span

Example:

I bought {{#label "item"}}milk{{/label}}.
[change]:
{{#replace "item"}}bread{{/replace}}
Helper Parameters Description
{{rotate}} (array ...) [set="attribute"] [show="next"] Create a cycling link that rotates through options infinitely
{{sequence}} (array ...) [set="attribute"] [show="next"] Create a sequence link that disables after the last option

Example:

Size: {{rotate (array "small" "medium" "large") set="size"}}
Helper Parameters Description
{{live}} "attribute" [section="name"] [passage="name"] Display attribute value that updates automatically when the attribute changes

Example:

Score: {{live "score"}}

The section and passage parameters let you display processed content from another section/passage that updates when the attribute changes:

{{live "category" section="categoryDescription"}}

In addition to Squiffy’s custom helpers, you have access to standard Handlebars block helpers:

Helper Description Example
{{#if}}...{{/if}} Conditional content {{#if has_key}}You have a key{{/if}}
{{#if}}...{{else}}...{{/if}} Conditional with fallback {{#if alive}}Living{{else}}Dead{{/if}}
{{#if}}...{{else if}}...{{/if}} Multiple conditions {{#if score > 100}}High{{else if score > 50}}Medium{{else}}Low{{/if}}
{{#unless}}...{{/unless}} Inverted conditional {{#unless game_over}}Keep playing{{/unless}}
{{#each}}...{{/each}} Iterate over arrays (advanced usage) Not commonly used in Squiffy

Squiffy has two ways to modify attributes:

Execute when a section or passage loads, before any rendering:

@set score = 100
@inc counter
@dec health
@unset flag
  • Always execute, regardless of any {{#if}} conditions in the text
  • Run once when the section/passage loads
  • Cannot be used inside conditionals

Execute during rendering, at the point where they appear in the text:

{{set "score" 100}}
{{inc "counter"}}
{{dec "health"}}
{{unset "flag"}}
  • Respect {{#if}} conditions - only execute if the condition is true
  • Run at render time, when that part of the text is processed
  • Can be used inside conditionals and helpers

When to use each:

  • Use directives for unconditional changes when a section/passage loads
  • Use helpers for conditional changes that depend on {{#if}} or other conditions

You can add your own helpers by calling squiffy.registerHelper from JavaScript in a @ui block. A @ui block goes at the top of your story, and the JavaScript in it runs once when the story starts, before any text is displayed. End the block with ---:

@ui
squiffy.registerHelper("shout", (text) => text.toUpperCase() + "!");
---
Welcome to the story. {{shout "hello"}}

This displays “Welcome to the story. HELLO!”.

registerHelper takes the helper’s name and a function. Arguments written after the helper name are passed to the function in order. Named key=value arguments are passed in options.hash (options is always the last argument).

Text returned from a helper is escaped, so any < or > characters are displayed as-is rather than treated as HTML. To return HTML, wrap it in squiffy.SafeString. For example, this helper embeds a YouTube video:

@ui
squiffy.registerHelper("youtube", (id, options) => {
const width = options.hash.width || 560;
const height = options.hash.height || 315;
return new squiffy.SafeString(
`<iframe width="${width}" height="${height}" src="https://www.youtube-nocookie.com/embed/${encodeURIComponent(id)}" ` +
`title="YouTube video" frameborder="0" allowfullscreen></iframe>`);
});
---
Here's a video:
{{youtube "dQw4w9WgXcQ" width=480 height=270}}

Only use SafeString for HTML you’ve built yourself. If part of the HTML comes from a player’s input, escape it first.

Inside a helper you can use squiffy.get and squiffy.set to read and change attributes, just as you can in a section’s JavaScript. For example, this helper shows different text each time it’s displayed, and then keeps showing the last one. It uses an attribute to count how many times it has been shown:

@ui
squiffy.registerHelper("visit", (attribute, ...args) => {
const items = args.slice(0, -1); // the last argument is "options"
const count = squiffy.get(attribute) || 0;
squiffy.set(attribute, count + 1);
return items[Math.min(count, items.length - 1)];
});
---
[[tutor]]:
{{visit "tutorvisits" "“And you are?…”" "“Ahh, here you are…”" "“You again!”"}}
[[Visit the tutor again]](tutor)

You can also put the JavaScript in a separate file with @ui filename.js. See the text processor example for both of these helpers, and the animate example for custom animations, which are registered the same way using squiffy.registerAnimation.