Examples
Copy-paste patterns for the most common structures. Swap in the character names, asset filenames and variable names you’ve registered in your project.
A full chapter#
Backgrounds, dialogue, choices, variables, conditionals, jumps and endings working together.
## The Message
// Register 'stranger' in Studio → Characters, 'city_night' in Assets → Backgrounds,
// and '$met_stranger' (boolean) in Studio → Variables before running this script.
@bg city_night
// Narration — no speaker, shown as prose overlay
"It was 2 AM when the phone buzzed."
// Dialogue — speaker must be registered in Studio → Characters
player: "Who texts at this hour?"
stranger: "Turn around."
// Choice gate — player picks one branch; the engine waits here
? "What do you do?"
- "Turn around slowly"
player: "My hands are shaking."
stranger: "Smart choice."
// Session variable assignment — saved with save slots
$met_stranger = true
-> The Reveal
- "Ignore it"
player: "Probably spam."
"She put the phone down. Big mistake."
-> Bad End
## The Reveal
@bg alley_night
"The figure stepped into the light."
// Conditional — branches on a session variable value
? if $met_stranger:
stranger: "I knew you'd come."
? else:
stranger: "You shouldn't be here."
stranger: "We need to talk. Not here."
// @end — chapter complete, engine loads the next published chapter
@end
## Bad End
@bg city_night
"Three hours later, she was gone."
// @fin ends the entire story, not just the chapter
@finPhone-story syntax tour#
A longer reference for chats, choices, variables and notifications. Try this in a practice phone story with its starter Messages app and Alex conversation.
// ═══════════════════════════════════════════════════════════
// Phone-story syntax tour — copy this into a practice chapter.
// Lines starting with // are comments — never shown to players.
//
// Quick orientation:
// ## Scene Name → starts a new scene
// @app <id> → switches which app is open on screen
// @chat <chatId> → opens a chat thread (defined in Apps panel)
// speaker: "text" → a chat bubble from that character
// ~ "text" → a system message (e.g. "Alex has left")
// @bg <asset_name> → sets a background image (upload in Assets)
// ? reply: → pauses for the player to choose
// - "Option A" → a choice branch
// @end → ends this chapter
// ═══════════════════════════════════════════════════════════
## Opening
// ── Step 1: Set the active app ────────────────────────────
// Use @app to open an app on the simulated phone.
// The app ID must match one defined in the Apps panel (left).
// New phone stories include "messages", "social", and "phone".
@app messages
// ── Step 2: Start a chat thread ──────────────────────────
// @chat references a chatId defined inside the "messages" app.
// This example uses the starter conversation with Alex.
// Characters are declared in the Characters panel (left).
// The reserved ID "player" is always available.
@chat chat_alex
~ "Monday, 9:14 AM"
player: "Hey — this is my first Storyomi game."
player: "Let me show you how choices work."
// ── Step 3: Player choices ────────────────────────────────
// ? pauses the game and shows options to the player.
// Each - branch runs its lines, then continues below.
? reply:
- "Sounds exciting!"
player: "I love it already."
- "How does this work?"
player: "I'll figure it out as I go."
player: "Both choices lead here — that's linear branching."
player: "For paths that stay split, use -> to jump to scenes."
## Variables
// ── Step 4: Variables ─────────────────────────────────────
// Declare variables in the Variables panel (left).
// Write to them with $var = value. Read them in conditions.
// Example (uncomment to use — declare 'mood' in the panel first):
//
// $mood = "curious"
//
// ? if $mood == "curious":
// player: "I'm feeling curious today."
// ? else:
// player: "I'm not sure how I feel."
@chat chat_alex
player: "Variables let your story remember choices."
player: "Declare one in the Variables panel, then use it here."
-> Notifications
## Notifications
// ── Step 5: Notifications ─────────────────────────────────
// @notify sends a system notification banner on the phone.
// Syntax: @notify <appId> "<title>" "<body>"
@notify messages "Messages" "You have a new message"
@app messages
@chat chat_alex
player: "That was a notification. Tap it to come back here."
player: "Use @notify any time — even mid-scene."
-> Finish
## Finish
@app messages
@chat chat_alex
player: "That's the core of OmiScript."
player: "Delete all of this and start your real story."
player: "Check the docs at storyomi.com/docs for the full reference."
~ "End of demo"
// ── Done ──────────────────────────────────────────────────
// @end closes the chapter. @fin marks the story as complete.
@end
Branching choices#
// Choices can be conditional — an option only appears when its [$condition] is true.
// Conditions read session variables ($var) declared in Studio → Variables.
? "What do you say?"
- "I believe you" [$trust >= 1]
// This option only shows if $trust is at least 1
player: "I believe you."
sarah: "Thank you. Really."
$route = "friendship"
-> Good Path
- "I don't trust you"
// No condition — always visible
player: "I don't trust you."
sarah: "That's fair. But you're wrong."
$route = "tense"
-> Tense Path
- "Stay quiet" [$met_sarah == false]
// Only available if the player hasn't met sarah yet
"You said nothing. Safer that way."
-> Quiet PathMessenger scene#
// Switch to a messenger-type app registered in Studio → Apps.
// All @chat content is deposited into that chat's message log.
@app chatter
// Open a specific chat — chatId must be registered under this app in Studio.
@chat chat_sarah
// Messages drip in one at a time as the engine runs
sarah: "hey, you there?"
sarah: "hello??"
// Reply gate — the player picks one option to send as their message
? reply:
- "sorry, was asleep"
player: "sorry just woke up lol"
sarah: "okay good, we need to talk"
// Track that the player replied — useful for branching later
$replied_sarah = true
-> Replied
- "leave on read"
// No player message — sarah gets no response
-> Left On Read
## Replied
sarah: "meet me at the usual spot. 20 mins."
@end
## Left On Read
// Push a notification into the player's notification shade
@notify chatter "Sarah" "is waiting for your reply..."
@endConditional branching#
// ? if / ? elif / ? else — conditional blocks.
// Evaluates against session ($var) or persistent ($$var) variables.
// Only the first matching branch executes.
? if $route == "friendship":
sarah: "I'm glad we talked."
player: "Me too."
? elif $route == "tense":
sarah: "I hope you change your mind."
player: "Maybe."
? else:
// Fallback — runs when no other branch matched
"They stood in silence."
// Comparison operators: == != > < >= <=
// Logical: and or not
// Example multi-condition: ? if $score >= 10 and $met_sarah:Phone call (SimOS)#
## The Call
// Switch to a phone-type app registered in Studio → Apps
@app phone
// Incoming call gate — the player picks Answer, Decline, or another branch.
// Dialogue inside a branch becomes the in-call conversation in the Phone app.
? call from sarah:
- "answer"
sarah: "Hey. Are you alone?"
player: "Yeah, why?"
sarah: "I need you to listen carefully."
sarah: "Don't come to the office tomorrow."
-> After Call
- "decline"
// No in-call dialogue — goes straight to the branch body
-> Missed Call
// 'hang up' is a special branch — runs when the player taps End Call mid-conversation
- "hang up"
sarah: "...hello?"
-> Hung Up
## After Call
"She hung up before you could ask anything."
@end
## Missed Call
// Voicemail — deposited into the phone app's voicemail inbox
@voicemail vm_sarah_001
from: sarah
duration: 0:12
transcript: "It's me. Call me back. Please."
@end
## Hung Up
"You stared at the phone. Had that been a mistake?"
@endOutgoing phone call (SimOS)#
## The Call
@app phone
// Outgoing call gate — the story waits until the player calls Sarah from the Phone app.
// The branches are tried top to bottom; the first whose [condition] is true wins.
? call to sarah:
hint: "You should call Sarah."
- "answered" [$relationship_sarah >= 3]
sarah: "Hey, I was hoping you'd call."
-> Made Up
- "answered"
sarah: "What do you want?"
-> Cold Call
- "unanswered"
// Nobody picks up — no dialogue here
-> No Answer
## Made Up
@end
## Cold Call
@end
## No Answer
@end
Social feed (SimOS)#
## The Post
// Switch to a social_feed-type app registered in Studio → Apps
@app social_feed
// Posts deposit into the feed as the engine runs — displayed newest-first in the UI.
// 'image' is a logical name from Studio → Assets → CGs (no file extension).
@post post_morning
by: sarah
text: "Another late night at the office. Someone send help ☕"
image: sarah_office
likes: 12
likeable: true
// Comment gate — the player picks a reply to a specific post.
// appId and postId must reference what's already been deposited above.
? comment on social_feed post_morning:
- "Hang in there!"
player: "Hang in there, you've got this!"
sarah: "Aw, thanks 🥺"
-> After Comment
- "Say nothing"
-> After Comment
## After Comment
// Push a banner notification into the player's notification shade
@notify social_feed "Sarah" "liked your comment"
"Your notification lit up a moment later."
@endPremium feed with subscription (SimOS)#
## The Exclusive
// Premium feed — requires a premium_feed-type app in Studio → Apps
@app vip_feed
// free: true — always visible regardless of subscription status
@post post_free_001
by: creator
text: "Hey everyone, big news coming soon 👀"
likes: 204
likeable: true
free: true
// locked_until — the post is blurred with a lock overlay until the condition is true.
// player_subscribed_to(appId, userId) is a built-in engine expression.
@post post_locked_001
by: creator
text: "Here's the full behind-the-scenes video from last night."
image: bts_video_thumb
locked_until: player_subscribed_to(vip_feed, creator)
teaser: "Subscribe to unlock exclusive content 🔒"
// ? subscribe to — narrative-driven subscription gate.
? subscribe to vip_feed creator:
- "Subscribe"
creator: "Thank you so much! 🥺"
// Persistent — survives across playthroughs, so future chapters know
$$subscribed_vip = true
-> Subscribed
- "Maybe later"
-> Skipped
## Subscribed
"The locked post shimmered and revealed itself."
@end
## Skipped
"The blurred post remained. For now."
@endCharacter sprites#
## The Confrontation
// 'rooftop_night' is a logical name from Studio → Assets → Backgrounds
@bg rooftop_night
// Auto-stage: speaking with () triggers automatic sprite placement.
// The first speaker goes center; the second speaker pushes the first aside.
sarah (): "You followed me up here."
player (): "You left me no choice."
// Named expression — registered in the character's sprite map in Studio.
// Use () for the default expression, (name) for a registered variant.
sarah (angry): "Don't make this harder than it has to be."
// Explicit placement — overrides auto-staging, puts marcus in the left slot.
@sprite left marcus
marcus: "Nobody's going anywhere."
// Clear a slot — removes the sprite visually when a character exits.
@sprite left clear
// sarah and player remain staged in their auto-assigned slots.
sarah: "It's just us now."
// @end — chapter complete. Use @fin to end the entire story.
@end
