Help: Quick Start · Stories · Characters · Lore Books · Multiplayer · Discover · Advanced · Templates · FAQ
Dream Scrolls uses Mustache as its prompt templating language. Every text field that feeds the AI — the system prompt, the summarization prompt, character descriptions, the story scenario, lore entries — is rendered through this engine before being sent to the model. You can use variables and logic blocks directly in those fields.
| Field | Available context |
|---|---|
| System Prompt (story / preset) | Full template context — all variables listed below |
| Summarization System Prompt (story / preset) | Full template context |
| Mood System Prompt (story / preset) | Full template context, POV character is the one being evaluated |
| State System Prompt (story / preset) | Full template context, POV character is the one being evaluated |
| Scenario (story) | Story-level context (char, presentChars, nonPresentChars) |
| Author Note (story) | Story-level context (char, presentChars, nonPresentChars) |
| Character description, personality, short description, author note | Character context (name only) |
| Lore entries | Story-level context (char, presentChars, nonPresentChars) |
| Variable | Description |
|---|---|
{{scenario}} | The story scenario text, after its own template pass |
{{storyAuthorNote}} | The story author note, after its own template pass |
char)These are only populated when the AI is generating a response for a specific character (the POV character). They are empty when building the summarization prompt.
| Variable | Description |
|---|---|
{{char.name}} | The character's display name |
{{char.id}} | The character's internal UUID |
{{char.shortDescription}} | The character's one-line description |
{{char.description}} | The character's full description |
{{char.personality}} | The character's personality field |
{{char.authorNote}} | The per-story author note for this character |
{{char.mood}} | The character's current AI-maintained (or manually pinned) mood for this story |
{{char.state}} | The character's current AI-maintained (or manually pinned) state for this story |
In the Mood/State System Prompt specifically, {{char.mood}}/{{char.state}} resolve to the value from before this re-evaluation — include it in your prompt (e.g. "Current mood: {{char.mood}}. Update it based on what just happened.") to get an incremental update instead of the model re-deriving the value from scratch every time.
presentChars contains all characters currently present in the story. nonPresentChars is the same list minus the POV character — useful for listing "other people in the scene."
Each item in both lists exposes the same fields as the char object above: name, id, shortDescription, description, personality, authorNote, mood, state.
{{#presentChars}}
- {{name}}: {{shortDescription}}
{{/presentChars}}
{{#nonPresentChars}}
- {{name}}
{{/nonPresentChars}}
The lore array contains only the entries that passed vector search and fit within the lore context budget. Use {{hasLore}} to conditionally wrap the block so no heading appears when there is nothing to show.
| Variable | Description |
|---|---|
{{hasLore}} | true if at least one lore entry was injected |
{{lore[*].label}} | The lore entry's title |
{{lore[*].content}} | The lore entry's text |
{{#hasLore}}
# World Knowledge
{{#lore}}
**{{label}}**: {{content}}
{{/lore}}
{{/hasLore}}
The summaries array contains past event summaries that fit within the context budget. Like lore entries, only summaries that did not overlap with the raw message history are included.
| Variable | Description |
|---|---|
{{hasSummaries}} | true if at least one summary was injected |
{{summaries[*].content}} | The summary text |
{{#hasSummaries}}
# Summary of past events
{{#summaries}}
{{content}}
{{/summaries}}
{{/hasSummaries}}
The messages array is the budget-filtered conversation history passed to the model.
| Variable | Description |
|---|---|
{{messages[*].role}} | user, assistant, or system |
{{messages[*].authorName}} | The author's display name |
{{messages[*].content}} | The message body |
Rendering messages as role-tagged turns (see the next section) is the recommended pattern:
{{#messages}}
<{{role}}{{#authorName}} name="{{authorName}}"{{/authorName}}>{{content}}</{{role}}>
{{/messages}}
By default, the entire rendered template is sent as a single system message. You can split it into multiple turns — system, user, and assistant — using role tags.
<system>Your instructions here.</system>
<user name="Alice">What she said.</user>
<assistant name="Bob">What he replied.</assistant>
name attribute is optional on all tags.After the Mustache template is rendered, the engine scans the output for role tags and produces an ordered list of prompt messages. Each message has a role (SYSTEM, USER, ASSISTANT) and optionally a name. That list is then sent to the LLM as a structured multi-turn conversation.
If the rendered string contains no role tags at all, the entire content is sent as a single system message — backward-compatible with simple prompts.
<system>
You are playing {{char.name}} in a collaborative story.
{{char.name}}: {{char.description}}
</system>
{{#scenario}}
<system>
{{scenario}}
</system>
{{/scenario}}
{{#hasLore}}
<system>
# Relevant world knowledge
{{#lore}}
{{label}}: {{content}}
{{/lore}}
</system>
{{/hasLore}}
{{#hasSummaries}}
<system>
# Summary of past events
{{#summaries}}
{{content}}
{{/summaries}}
</system>
{{/hasSummaries}}
{{#messages}}
<{{role}}{{#authorName}} name="{{authorName}}"{{/authorName}}>{{content}}</{{role}}>
{{/messages}}
<system>
[Continue the story as {{char.name}}. Two paragraphs maximum.]
</system>
Character descriptions, personality, short description, and author notes are also passed through Mustache before being injected into the prompt. However, only the character's own name is available in these fields:
| Variable | Available in character fields |
|---|---|
{{name}} | The character's own name |
In the story scenario and story author note, the story-level context is available: {{char}}, {{presentChars}}, {{nonPresentChars}}.
Lore entry text is similarly rendered with the story-level context before injection.
| Syntax | Effect |
|---|---|
{{variable}} | Output the value of a variable (no HTML escaping) |
{{object.property}} | Access a nested property |
{{#block}}...{{/block}} | Render the block if the value is truthy; iterate if it is a list |
{{^block}}...{{/block}} | Render the block if the value is falsy (inverse section) |
{{! comment }} | Comment — not included in the output |
Dream Scrolls disables Mustache's default HTML entity encoding. All values are output as-is.
"Invalid system prompt template" error in the story feed
The template failed to render, usually due to a syntax error (mismatched {{# / {{/ tags, or an unclosed {{). Check your system prompt for typos. The error message includes a brief description of what went wrong.
A variable renders as empty
{{char.*}} variables are empty when no POV character is set (e.g. in the summarization prompt when no character context is passed). Use {{#char}}...{{/char}} to guard the block.{{scenario}} and {{storyAuthorNote}} are empty strings when the story fields are blank.The lore / summaries block appears even when empty
Wrap it in {{#hasLore}}...{{/hasLore}} or {{#hasSummaries}}...{{/hasSummaries}} to suppress it conditionally.
Role tags are not being split
Make sure the tag names are lowercase (<system>, <user>, <assistant>). Tag names are case-insensitive in the parser, but the name attribute value is used verbatim.