Ink Scripting Guide
Ink is a powerful scripting language for writing interactive narratives. This guide covers how to use Ink for Encounters stories.
Ink Basics
What is Ink?
Ink is a narrative scripting language created by Inkle Studios. It allows you to write branching stories with:
- Linear narrative sequences
- Player choices
- Variables and state tracking
- Conditional logic
- Functions and reusable content
For comprehensive Ink documentation, see the official Ink guide.
Encounters-Specific Features
Conversation State Management
Track where the player is in the conversation:
VAR conversation_state = "initial"
-> start
== start ==
Initial messages here
~ conversation_state = "waiting_for_response"
-> conversation_choices
== conversation_choices ==
* [Choice 1]
~ conversation_state = "responded_to_choice_1"
{{ ... }}
-> response_1Cross-Conversation Variables
Variables can be shared between conversations for complex stories:
// In mum.ink
== on_police_called ==
I'm calling the police now!
~ police_contacted = true
-> END
// In alex.ink
== check_police_status ==
{police_contacted:
I heard mum called the police.
- else:
Should we call the police?
}
-> ENDTriggered Events
Create knots that other conversations can trigger:
== on_mum_police_contacted ==
Mum just called the police. #typing:3s #delay:4s
They said they're opening a missing person case now.
~ conversation_state = "reacted_to_police"
-> ENDThis knot can be triggered by events in other conversations.
Sequences
Show different content on repeated visits:
== repeated_question ==
{
- First time asking
- Second time asking
- Third time asking
- They keep asking...
}
-> ENDConditional Text
Inline conditions within messages:
{police_contacted: The police are already involved. | Maybe we should call the police.}Example: Complete Conversation
Here's a complete example showing best practices:
// alex.ink - Conversation with Alex about missing Sarah
// State variables
VAR police_contacted = false
VAR alex_knows_player = false
VAR club_info_shared = false
VAR conversation_state = "initial"
-> start
== start ==
// Initial messages that appear when conversation opens
Like my new avatar? #initial #read #image:alex-avatar.jpg
Have you seen this funny video? #link:www.youtube.com/watch?v=dQw4w9WgXcQ #initial #read
Looks great! And lol that video 😂 #me #initial #read
Hey Sarah, have you seen my keys? #initial #read
I think they're on the kitchen counter #me #initial #read
Sarah? You were supposed to meet me an hour ago. Where are you? #initial
This is really weird... you always text back immediately. #initial
// Transition to interactive mode
~ conversation_state = "waiting_for_response"
-> conversation_choices
== conversation_choices ==
// Player's available choices
* [Hi, I found Sarah's phone and saw your messages]
-> found_phone_response
* [Where was Sarah last seen?]
-> last_seen_response
* {alex_knows_player and not club_info_shared} [Tell me more about the club]
-> club_details_response
* {not police_contacted} [We should call the police]
-> police_response
-> END
== found_phone_response ==
Wait... who is this? #typing:2s
You're texting from Sarah's phone but you're not Sarah. #typing:1s
Did you find her phone? Oh god, something's happened to her hasn't it? #typing:3s
~ alex_knows_player = true
~ conversation_state = "phone_explained"
-> END
== last_seen_response ==
She was at Club Neon last night. #typing:2s
Said she was meeting some new friends from work. #typing:2s
I offered to pick her up but she said she had a ride. #typing:1s
That was around 11 PM.
~ conversation_state = "shared_info"
-> END
== club_details_response ==
Club Neon is on King Street in the city centre. #typing:2s
It's pretty new, opened about a month ago. #typing:2s
Sarah said her colleagues Emma and Jake were going to meet her there.
~ club_info_shared = true
~ conversation_state = "shared_club_info"
-> END
== police_response ==
Yes, you're right. I should have called them already. #typing:2s
But they always say to wait 24 hours... #typing:1s
It's been about 18 hours now.
~ conversation_state = "police_discussion"
-> END
// Event triggered from another conversation
== on_mum_police_contacted ==
Mum just called the police. #typing:3s #delay:4s
They said they're opening a missing person case now.
~ police_contacted = true
~ conversation_state = "reacted_to_police"
-> ENDBest Practices
Writing Style
Natural Dialogue
Write messages as real people would text - use contractions, casual language, and emojis where appropriate.
Good:
Hey! Where are you? 😊
I'm getting worried...Avoid:
Hello. I am inquiring about your current location.
I am experiencing feelings of concern.Pacing
- Use timing tags to create realistic conversation flow
- Don't overwhelm the player with too many messages at once
- Build tension with delays and typing indicators
- Vary timing - quick responses for urgent moments, longer delays for thinking
Let me check something... #typing:3s #delay:2s
Oh no. #typing:1s #delay:3s
You need to see this. #image:evidence.jpg #typing:2sTiming Guidelines:
- Short messages: 1-2 seconds typing
- Medium messages: 2-4 seconds typing
- Long messages: 4-6 seconds typing
- Very long messages: 1-2 minutes typing
- Quick pause: 1-3 seconds delay
- Emotional pauses: 3-10 seconds delay
- Short activity: 1-5 minutes delay (driving, searching, etc.)
- Longer activity: 10-60 minutes delay (meetings, investigations)
- Major time jumps: 1+ hours delay (next day, later that evening)
Choice Design
- Offer 2-4 choices at a time (avoid overwhelming)
- Make choices meaningful - they should affect the story
- Use clear language - player should understand what each choice means
- Vary choice types - questions, actions, emotional responses
State Management
- Track important decisions with variables
- Use descriptive state names:
"police_contacted"not"state1" - Update state consistently when story progresses
Testing
- Test all branches - make sure every choice works
- Check timing - ensure delays feel natural
- Verify conditions - conditional choices should appear correctly
- Test cross-conversation events if you use them
Common Patterns
Delayed Revelation
I need to tell you something... #typing:3s #delay:2s
It's about Sarah. #typing:2s #delay:3s
She's been lying to us. #typing:2sInformation Gathering
* [What time did she leave?]
~ time_asked = true
Around 11 PM, I think.
-> more_questions
* [Who was she with?]
~ companions_asked = true
Some people from work.
-> more_questions
== more_questions ==
* {time_asked and companions_asked} [That's all I need to know]
-> END
* {not time_asked} [What time did she leave?]
-> time_responseEmotional Progression
VAR trust_level = 0
* [I want to help find Sarah]
~ trust_level = trust_level + 1
Thank you... I really appreciate that.
* [Tell me everything you know]
{trust_level > 2:
Okay, I trust you. Here's what happened...
- else:
I don't know if I should tell you everything yet.
}Debugging Tips
Common Errors
Missing -> END:
== my_knot ==
Some text here
// ERROR: No ending!Fix:
== my_knot ==
Some text here
-> ENDUndefined knot:
-> non_existent_knot // ERROR: Knot doesn't existSyntax errors in conditions:
{variable = true} // ERROR: Use == for comparison
{variable == true} // CORRECTTesting Your Ink
Build your story to check for errors:
npm run build:storyThe build script will show compilation errors with line numbers.
Quick Reference: All Encounters Tags
Here's a complete list of all available tags for quick reference:
| Tag | Description | Example |
|---|---|---|
#initial | Message exists before player joins | Hey! #initial |
#read | Mark message as already read | Hi there #initial #read |
#me | Message from the player | Thanks! #me #initial #read |
#system | System message (no sender) | User joined #system |
#typing:X | Show typing indicator (s/m/h) | Let me check... #typing:3s |
#delay:X | Wait before showing message (s/m/h) | I found it! #delay:5s |
#image:filename | Display an image | Look at this! #image:photo.jpg |
#audio:filename | Display audio player | Listen #audio:voice.wav |
#video:filename | Display video player | Watch this #video:clip.mp4 |
#gallery:id | Unlock and display gallery item | Found this #gallery:photo-001 |
#link:url | Create a clickable link | Check this out #link:example.com |
#contact:id | Specify sender in group chat | Hello everyone #contact:alex |
#text_input:prompt | Request text input from player | # text_input:Enter name |
#attach_image:prompt | Request image selection | # attach_image:Select photo |
#notify:conv:knot | Trigger knot in another conversation | Done! #notify:alex:on_event |
#unlockConversation:id[:delay] | Unlock a conversation | #unlockConversation:group:10s |
#unlockContact:id[:delay] | Unlock a contact | #unlockContact:detective:5s |
#unlockNews:id[:delay] | Unlock a news article | #unlockNews:breaking:2m |
#unlockNote:id[:delay] | Unlock a note | #unlockNote:clue:30s |
Tag Compatibility
Can be combined:
#initial+#read+#me- Show player's past messages#typing:Xs+#delay:Xs- Realistic message timing#image:file+#typing:Xs- Image with typing indicator#delay:Xs+#unlock:id- Unlock conversation after delay#notify:conv:knot+#typing:Xs- Trigger with timing
Group chat specific:
#contact:id- Only use in group conversations
Cross-conversation:
#notify:conv:knot- Trigger events in other conversations#unlock:conv-id- Make new conversations available
Recommended Tools
Visual Studio Code with Ink Extension
The best way to write Ink scripts is using Visual Studio Code with the Ink extension:
Why VS Code + Ink Extension?
- Syntax highlighting for
.inkfiles - Auto-completion for Ink keywords
- Real-time error detection
- Jump to knot definitions
- Outline view of your story structure
Installation:
- Download Visual Studio Code (free)
- Install VS Code
- Open VS Code
- Click Extensions icon (left sidebar) or press
Ctrl+Shift+X/Cmd+Shift+X - Search for "Ink" by inkle
- Click Install
Using the Extension:
- Open any
.inkfile and it will automatically highlight syntax - Hover over knots to see previews
- Use
Ctrl+Click/Cmd+Clickon knot names to jump to them - View story structure in the Outline panel
Other Resources
- Official Ink Documentation
- Ink Language Reference
- Inky Editor - Standalone visual Ink editor for testing
Next Steps
- Explore dynamic behavior: External Functions Reference
- Master all tags: Tag Reference Card
- Learn about Building and Publishing your story
- Understand the Project Structure in detail