Labs · Macro playground

What {{char}} and {{random}}
actually expand to — paste and see.

Card authors write macros from memory and find the typos mid-chat. Paste a greeting or prompt snippet here instead: every macro’s output highlights in place, hover shows which macro produced it, and {{random}} / {{roll}} get a re-roll button. Below the tool: a support table checked line-by-line against the engine.

Freeruns in your browsernothing uploaded

Paste something with {{char}} or {{random}} — the expansion shows up here.

Macro support table

Listed against the actual engine implementation — anything marked supported can be verified in the box above, right now.

SyntaxWhat it doesSupport
{{char}} · {{character}}Character name (V3 nickname takes priority)✓ Supported
{{user}} · {{name}}User (persona) name✓ Supported
{{persona}}User persona description✓ Supported (demo value)empty demo value here
{{scenario}}Scenario✓ Supported (demo value)empty demo value here
{{model}}Current model id✓ Supported (demo value)demo value here
{{lastMessage}} · {{lastUserMessage}} · {{lastCharMessage}}Last message / last user message / last char message✓ Supported (demo value)needs chat state; placeholder here
{{group}} · {{charIfNotGroup}} · {{groupNotMuted}}Group member list (falls back to char in 1-on-1)✓ Supported (demo value)demoed as 1-on-1 here
{{time}} · {{time::UTC+8}}Current time (optional UTC offset)✓ Supportedbrowser clock here; device clock in the app
{{date}} · {{datetime}} · {{isodate}} · {{isotime}} · {{weekday}}Date / datetime / weekday✓ Supported
{{idleDuration}}Time since your last message✓ Supported (demo value)no chat state; always 'just now' here
{{random:a,b,c}} · {{random::a::b::c}} · {{pick}}Pick one at random✓ Supported
{{roll:1d20}} · {{roll 1d100}} · {{roll:20}}Dice roll (NdM; bare number = 1dN)✓ Supported
{{if::条件::甲::乙}}Inline conditional (truthy → first branch)✓ Supported
{{newline::N}} · {{space::N}}Newlines / spaces (with count)✓ Supported
{{noop}} · {{comment}} · {{// 注释}}No-op / comment (expands to nothing)✓ Supported
{{reverse::文本}}Reverse characters✓ Supported
{{setvar::k::v}} · {{getvar::k}} · {{addvar}} · {{incvar}} · {{decvar}} · {{deletevar}} · {{hasvar}}Chat-variable family✓ Supportedscoped to one expansion here; persistent across turns in the app
{{setglobalvar::k::v}} 等 global 族Global-variable family✓ Supportedsame as above
{{$名字}}Variable shorthand read✓ Supported
\{{char}}Escape: output literally✓ Supported
{{outlet::名字}}Pull a worldbook outlet entry— N/A on this pageneeds worldbook context; always empty here (same shape as the runtime's 'inactive = empty')
{{wi::name}} · {{getarg}}STscript closure macros✗ Not supportednot implemented in the app either
{{if}}…{{else}}…{{/if}} 块级Block-level conditional✗ Not supportedthe app ships the inline {{if::…}} form instead

Where this tool comes from

The macro engine is ported from the expander inside the Foreverse app — an implementation aligned with the behavior subset of SillyTavern's macros/definitions: identity macros (char/user/persona), time macros (time/date/weekday with UTC offsets), random macros (every random/pick/roll form), inline {{if}}, the whole variable family (setvar/getvar/addvar/incvar plus global variants and the {{$name}} shorthand), escapes and comments. The expansion order (variable writes → variable reads → everything else) matches the app's three-pass pipeline, and representative cases were carried into this engine's self-test. Ported 2026-07.

The support table above wasn't copied from docs — every row was checked against the actual engine implementation, and anything marked supported can be verified in the input box right now.

What it can't do

Time macros ({{time}}/{{date}}) expand against your browser's clock; in the app it's the device clock. Both mean "local time" — the number you see now just won't be the number at chat time.

Chat-state macros ({{lastMessage}}, {{idleDuration}}) use placeholder values here; {{outlet}} needs worldbook activation context and stays empty. Variables live within a single expansion here — in the app, chat variables persist across turns.

FAQ

Is {{random:a,b,c}} different from {{random::a::b::c}}?

No — both separators are valid. Double-colon wins when present; otherwise commas split. Use the double-colon form when an option itself contains a comma. One option is picked with equal probability per generation — the standard trick for randomized greetings.

What's the full {{roll}} syntax?

NdM rolls N dice with M sides and sums them: {{roll:2d6}} is two six-siders. N defaults to 1 ({{roll:d20}}), a bare number is the side count ({{roll:20}} equals {{roll:1d20}}), and the space form {{roll 1d100}} works too — the common notation in skill-check cards. A malformed roll ({{roll:abc}}) doesn't error; it outputs the raw argument.

Do macros expand recursively?

No. Expansion is single-pass: if the value {{getvar}} pulls out contains {{char}}, it stays literal — both the tavern and the app engine behave this way to stop runaway nesting. The annotated view here shows it directly: macro shapes inside an expansion product stay unexpanded.

What happens to an unrecognized macro?

It's sent to the model as-is — no error, no removal. That's exactly how "the model keeps seeing curly braces" accidents happen when a card typos a macro name. This tool underlines unrecognized macros with a dotted line; scanning a card here before publishing beats discovering it mid-chat.

Related tools & reading

← All free tools

SillyTavern Macro Playground — Expand {{char}} {{user}} {{random}} Online, Full Support Table · Foreverse · Xinmeng