Player Input Guide
Learn how to collect text and image input from players to create interactive, personalized story experiences.
Overview
Player input allows you to:
- Collect custom text - Names, locations, answers, descriptions
- Request image selection - Photos from the player's gallery
- Personalize the story - Use player's responses in dialogue
- Create interactive investigations - Gather witness statements, evidence
- Validate and process input - Check responses and react accordingly
- Integrate with AI tools - Analyze images with object detection
Types of Player Input
Text Input
Collect free-form text from the player using the #text_input: tag.
Use cases:
- Character names
- Location descriptions
- Witness statements
- Answers to questions
- Passwords or codes
- Custom responses
Image Attachment
Request the player to select an image from their gallery using the #attach_image: tag.
Use cases:
- Identity verification
- Evidence submission
- Location photos
- Proof of completion
- Visual clues
Text Input
Basic Text Input
Structure:
* [Continue]
# text_input:Prompt text here
~ variable_name = GetPlayerInput()
-> next_knotExample - Asking for a Name:
EXTERNAL GetPlayerInput()
VAR player_name = ""
-> start
== start ==
Can you tell me your name? #typing:2s
-> ask_name
== ask_name ==
* [Continue]
# text_input:Enter your name
~ player_name = GetPlayerInput()
-> confirm_name
== confirm_name ==
Thank you, {player_name}. Nice to meet you! #delay:2s
-> ENDHow It Works
- Display the prompt - The message before the choice asks the question
- Show input field - The
#text_input:tag displays a text input field - Player types - Player enters their response
- Retrieve input -
GetPlayerInput()gets the text they entered - Store in variable - Save to a variable for later use
- Use in dialogue - Reference the variable with
{variable_name}
Important Requirements
Must use with a single-option choice:
* [Continue] // Single choice only
# text_input:Your promptCannot use with multiple choices:
// ❌ WRONG - Don't do this
* [Option 1]
# text_input:Enter something
* [Option 2]
# text_input:Enter something elseDeclare external function:
EXTERNAL GetPlayerInput()Declare variable to store input:
VAR player_name = ""Complete Example - Detective Interview
Based on the detective.ink example from the sample story:
EXTERNAL GetPlayerInput()
EXTERNAL GetPlayerName()
EXTERNAL GetPlayerFirstName()
VAR player_name = ""
VAR witness_location = ""
-> start
== start ==
This is Detective Morgan from the Metropolitan Police. #initial
I'm investigating a missing person case. #initial
I need to ask you a few questions. First, can you tell me your name? #initial
-> ask_name
== ask_name ==
* [Continue]
# text_input:Enter your name
~ player_name = GetPlayerInput()
-> confirm_name
== confirm_name ==
Thank you, {player_name}. I appreciate your cooperation. #delay:2s #typing:3s
// Optional: Cross-check with actual player name
{player_name != GetPlayerName():
Wait... I have your details here. Your real name is {GetPlayerFirstName()}, isn't it? #delay:2s
No worries, let's continue. #delay:2s
}
Now, you mentioned you found Sarah's phone. Can you tell me exactly where you found it? #delay:3s
-> ask_location
== ask_location ==
* [Continue]
# text_input:Where did you find the phone?
~ witness_location = GetPlayerInput()
-> confirm_location
== confirm_location ==
I see. You found it at {witness_location}. That's very helpful information. #delay:2s #typing:4s
We'll need to send a forensics team to that location immediately. #delay:3s
-> ENDUsing Player Input in Dialogue
Reference the variable:
Thank you, {player_name}.
You found it at {witness_location}.
So, {player_name}, what do you think about {witness_location}?Use in conditionals:
{player_name == "Sarah":
Wait, you're Sarah? But you're supposed to be missing!
- else:
Okay, {player_name}, let's continue.
}Combine with string functions:
~ temp name_upper = StringToUpper(player_name)
~ temp name_length = StringLength(player_name)
{name_length < 2:
That name seems too short. Are you sure?
- else:
{name_upper}, got it.
}Input Validation
Check if empty:
~ temp input = GetPlayerInput()
~ temp cleaned = StringTrim(input)
{StringIsEmpty(cleaned):
You didn't enter anything. Please try again.
-> ask_name
- else:
~ player_name = cleaned
-> confirm_name
}Check length:
~ temp input = GetPlayerInput()
{StringLength(input) < 3:
That's too short. Please enter at least 3 characters.
-> ask_name
- StringLength(input) > 50:
That's too long. Please keep it under 50 characters.
-> ask_name
- else:
~ player_name = input
-> confirm_name
}Pattern matching:
~ temp email = GetPlayerInput()
{StringMatchesPattern(email, "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$"):
Email saved: {email}
-> END
- else:
That doesn't look like a valid email address. Please try again.
-> ask_email
}Image Attachment
Basic Image Attachment
Structure:
* [Continue]
# attach_image:Prompt text here
~ variable_name = GetAttachedGalleryItem()
-> next_knotExample - Identity Verification:
EXTERNAL GetAttachedGalleryItem()
VAR attached_item = ""
-> start
== start ==
Can you send me a photo for identity verification? #typing:2s
-> request_photo
== request_photo ==
* [Continue]
# attach_image:Select a photo for identity verification
~ attached_item = GetAttachedGalleryItem()
-> verify_photo
== verify_photo ==
Thank you. I'll review this photo. #delay:2s
-> ENDHow It Works
- Display the prompt - The message before the choice asks for the image
- Show gallery picker - The
#attach_image:tag opens the gallery - Player selects - Player chooses an image from their gallery
- Retrieve ID -
GetAttachedGalleryItem()gets the gallery item ID - Store in variable - Save the ID for later use
- Process or validate - Check which image was selected
Important Requirements
Must use with a single-option choice:
* [Continue] // Single choice only
# attach_image:Your promptDeclare external function:
EXTERNAL GetAttachedGalleryItem()Declare variable to store item ID:
VAR attached_item = ""Complete Example - Photo Evidence Submission
Based on the detective.ink example:
EXTERNAL GetAttachedGalleryItem()
EXTERNAL GetStoryToolsStatus()
EXTERNAL DetectObjectInGalleryImage(galleryItemId, objectName)
VAR attached_item = ""
VAR tools_enabled = 0
VAR face_confidence = 0
-> start
== start ==
Before we continue, I need to verify your identity for security purposes. #delay:3s
Can you send me a photo that clearly shows your face? #delay:2s
-> request_face_photo
== request_face_photo ==
* [Continue]
# attach_image:Select a photo for identity verification
~ attached_item = GetAttachedGalleryItem()
-> verify_face_photo
== verify_face_photo ==
~ tools_enabled = GetStoryToolsStatus()
{tools_enabled == 1:
Let me run this through our facial recognition system... #delay:2s #typing:3s
~ face_confidence = DetectObjectInGalleryImage(attached_item, "face")
{face_confidence > 0.7:
Perfect. I can clearly see a face in this photo. Identity verified. #delay:2s
- face_confidence > 0.3:
Hmm, the image quality isn't great, but I can make out a face. That'll do. #delay:2s
- else:
I'm having trouble detecting a face in this photo. #delay:2s
But we'll proceed anyway - time is of the essence. #delay:2s
}
- else:
Thank you. I'll review this manually later. #delay:2s
}
-> ENDChecking Which Image Was Selected
Match specific gallery items:
~ attached_item = GetAttachedGalleryItem()
{attached_item == "crime-scene-photo":
This is the crime scene photo. Excellent. #delay:2s
- attached_item == "witness-photo":
Ah, the witness photo. This could be useful. #delay:2s
- attached_item == "sarah-selfie":
This is Sarah's selfie from that night. #delay:2s
- else:
Thank you for sending this. We'll analyze it. #delay:2s
}Example - Multiple Photo Options:
EXTERNAL GetAttachedGalleryItem()
VAR attached_item = ""
== ask_for_photo ==
I need you to send me a photo of where you found the phone. #delay:2s
-> receive_photo
== receive_photo ==
* [Continue]
# attach_image:Select a photo from your gallery
~ attached_item = GetAttachedGalleryItem()
-> process_photo
== process_photo ==
{attached_item == "found-phone":
This is excellent. This photo shows the exact location. #delay:2s #typing:4s
I can see some important details in the background. #delay:3s
We'll send a forensics team there right away. #delay:2s
- attached_item == "sample-photo":
Interesting choice. This might be relevant. #delay:2s
- attached_item == "sample-video":
Actually, this appears to be a video file, not a photo. #delay:2s #typing:3s
But it might still be useful. Let me review it. #delay:2s
- else:
Thank you for sending this. We'll analyze it for any clues. #delay:2s #typing:3s
}
-> ENDAI-Powered Image Analysis
Object Detection
Use the DetectObjectInGalleryImage() function to detect objects in player-submitted images.
Setup:
EXTERNAL GetStoryToolsStatus()
EXTERNAL DetectObjectInGalleryImage(galleryItemId, objectName)
VAR tools_enabled = 0
VAR confidence = 0Check if tools are available:
~ tools_enabled = GetStoryToolsStatus()
{tools_enabled == 1:
Our AI tools are online. #delay:2s
- else:
Our systems are offline. We'll proceed manually. #delay:2s
}Detect objects:
~ confidence = DetectObjectInGalleryImage(attached_item, "face")
{confidence > 0.7:
I can clearly see a face in this photo. #delay:2s
- confidence > 0.3:
There might be a face, but the quality is poor. #delay:2s
- else:
I don't see a face in this photo. #delay:2s
}Common Objects to Detect
People:
"face"- Human faces"person"- Full person
Vehicles:
"car"- Cars"truck"- Trucks"motorcycle"- Motorcycles
Objects:
"phone"- Mobile phones"weapon"- Weapons"bag"- Bags or backpacks
Example - Multi-Object Detection:
EXTERNAL GetAttachedGalleryItem()
EXTERNAL DetectObjectInGalleryImage(galleryItemId, objectName)
VAR attached_item = ""
VAR has_face = 0
VAR has_car = 0
VAR has_weapon = 0
== analyze_evidence_photo ==
* [Continue]
# attach_image:Send the evidence photo
~ attached_item = GetAttachedGalleryItem()
-> process_evidence
== process_evidence ==
Let me analyze this photo... #delay:2s #typing:3s
~ has_face = DetectObjectInGalleryImage(attached_item, "face")
~ has_car = DetectObjectInGalleryImage(attached_item, "car")
~ has_weapon = DetectObjectInGalleryImage(attached_item, "weapon")
{has_face > 0.5:
I can see a person in this photo. #delay:2s
}
{has_car > 0.5:
There's a vehicle visible. #delay:2s
}
{has_weapon > 0.5:
Wait... is that a weapon? This is serious. #delay:2s
}
{has_face < 0.3 && has_car < 0.3 && has_weapon < 0.3:
I'm not detecting anything significant in this photo. #delay:2s
}
-> ENDCombining Text and Image Input
Sequential Input
Example - Complete Witness Statement:
EXTERNAL GetPlayerInput()
EXTERNAL GetAttachedGalleryItem()
VAR witness_name = ""
VAR witness_location = ""
VAR evidence_photo = ""
-> start
== start ==
I need to take your statement. First, what's your name? #typing:2s
-> ask_name
== ask_name ==
* [Continue]
# text_input:Enter your name
~ witness_name = GetPlayerInput()
-> ask_location
== ask_location ==
Thank you, {witness_name}. Where exactly did you see the suspect? #delay:2s #typing:3s
* [Continue]
# text_input:Describe the location
~ witness_location = GetPlayerInput()
-> ask_photo
== ask_photo ==
You said you saw them at {witness_location}. #delay:2s
Did you take any photos? #typing:2s
* [Yes, I have a photo]
-> request_photo
* [No, I didn't take any photos]
-> no_photo
-> END
== request_photo ==
Please send me the photo. #delay:1s
* [Continue]
# attach_image:Select the photo you took
~ evidence_photo = GetAttachedGalleryItem()
-> process_statement
== no_photo ==
That's okay, {witness_name}. Your description is still helpful. #delay:2s
-> process_statement
== process_statement ==
{evidence_photo != "":
Thank you, {witness_name}. We have your statement and the photo from {witness_location}. #delay:2s #typing:4s
- else:
Thank you, {witness_name}. We have your statement about {witness_location}. #delay:2s #typing:3s
}
This information will be very helpful to the investigation. #delay:2s
-> ENDBest Practices
Text Input
Clear prompts:
# text_input:Enter your full name
# text_input:Where did you find the phone?
# text_input:Describe what you sawAvoid vague prompts:
# text_input:Enter something // ❌ Too vague
# text_input:Type here // ❌ Not helpfulValidate input:
~ temp input = GetPlayerInput()
~ temp cleaned = StringTrim(input)
{StringIsEmpty(cleaned):
Please enter something.
-> ask_again
}Provide context:
I need your full legal name for the official report. #typing:2s
* [Continue]
# text_input:Enter your full name (First and Last)Image Attachment
Specific prompts:
# attach_image:Select a photo for identity verification
# attach_image:Send the crime scene photo
# attach_image:Choose the photo you took at the locationExplain why:
I need to verify your identity for security purposes. #delay:2s
Can you send me a photo that clearly shows your face? #typing:2s
* [Continue]
# attach_image:Select a photo for identity verificationHandle different selections:
{attached_item == "expected-photo":
Perfect, this is exactly what I needed. #delay:2s
- else:
Hmm, this isn't quite what I was expecting, but it might still be useful. #delay:2s
}User Experience
Don't overwhelm:
// ❌ Too many inputs at once
-> ask_name
-> ask_age
-> ask_location
-> ask_photo
-> ask_email
// ✅ Space them out
-> ask_name
// ... story progression ...
-> ask_location
// ... more story ...
-> ask_photoAcknowledge input:
~ player_name = GetPlayerInput()
Thank you, {player_name}. #delay:1s
// Always acknowledge what they enteredMake it feel natural:
// ❌ Robotic
Enter your name.
* [Continue]
# text_input:Name
// ✅ Natural
Before we continue, I need to know who I'm speaking with. #typing:2s
What's your name? #delay:1s
* [Continue]
# text_input:Enter your nameTroubleshooting
Input not working:
- Ensure you have a single-option choice:
* [Continue] - Check external function is declared:
EXTERNAL GetPlayerInput() - Verify variable is declared:
VAR player_name = ""
Empty input:
- Use
StringTrim()to remove whitespace - Check with
StringIsEmpty()before proceeding - Provide validation and ask again if needed
Image not recognized:
- Verify the gallery item ID matches exactly
- Check the item exists in
src/gallery/ - Remember: ID is the filename without
.json
AI detection not working:
- Check
GetStoryToolsStatus()returns1 - Ensure API is configured and running
- Provide fallback for when tools are offline
Complete Example - Police Interview
Here's a complete, realistic example combining everything:
EXTERNAL GetPlayerInput()
EXTERNAL GetAttachedGalleryItem()
EXTERNAL GetPlayerName()
EXTERNAL GetPlayerFirstName()
EXTERNAL GetStoryToolsStatus()
EXTERNAL DetectObjectInGalleryImage(galleryItemId, objectName)
VAR player_name = ""
VAR witness_location = ""
VAR attached_item = ""
VAR tools_enabled = 0
VAR face_confidence = 0
-> start
== start ==
This is Detective Morgan from the Metropolitan Police. #initial
I'm investigating a missing person case. #initial
I need to ask you a few questions. #initial
First, can you tell me your name? #delay:2s
-> ask_name
== ask_name ==
* [Continue]
# text_input:Enter your name
~ player_name = GetPlayerInput()
-> confirm_name
== confirm_name ==
Thank you, {player_name}. I appreciate your cooperation. #delay:2s #typing:3s
{player_name != GetPlayerName():
Wait... I have your details here. Your real name is {GetPlayerFirstName()}, isn't it? #delay:2s
No worries, let's continue. #delay:2s
}
Let me check our system status... #delay:2s
~ tools_enabled = GetStoryToolsStatus()
{tools_enabled == 1:
Our advanced investigation tools are online. #delay:2s
- else:
Our systems are currently offline, but we'll manage. #delay:2s
}
Before we continue, I need to verify your identity for security purposes. #delay:3s
Can you send me a photo that clearly shows your face? #delay:2s
-> request_face_photo
== request_face_photo ==
* [Continue]
# attach_image:Select a photo for identity verification
~ attached_item = GetAttachedGalleryItem()
-> verify_face_photo
== verify_face_photo ==
{tools_enabled == 1:
Let me run this through our facial recognition system... #delay:2s #typing:3s
~ face_confidence = DetectObjectInGalleryImage(attached_item, "face")
{face_confidence > 0.7:
Perfect. I can clearly see a face in this photo. Identity verified. #delay:2s
- face_confidence > 0.3:
Hmm, the image quality isn't great, but I can make out a face. That'll do. #delay:2s
- else:
I'm having trouble detecting a face in this photo. #delay:2s
But we'll proceed anyway - time is of the essence. #delay:2s
}
- else:
Thank you. I'll review this manually later. #delay:2s
}
Now, you mentioned you found Sarah's phone. #delay:3s
Can you tell me exactly where you found it? #typing:2s
-> ask_location
== ask_location ==
* [Continue]
# text_input:Where did you find the phone?
~ witness_location = GetPlayerInput()
-> confirm_location
== confirm_location ==
I see. You found it at {witness_location}. That's very helpful information. #delay:2s #typing:4s
We'll need to send a forensics team to that location immediately. #delay:3s
Thank you for your cooperation, {player_name}. #delay:2s
If you remember anything else, please contact me immediately. #typing:3s
-> ENDNext Steps
- Learn about External Functions for processing input
- See Tag Reference for all available tags
- Explore String Functions for validation
- Review Gallery System for image management