@@ -38,38 +38,41 @@ TLA+ animations transform behaviors of abstract specifications into visual SVG r
3838
3939### 1. Create Animation File
4040
41- For a specification ` MySystem .tla` , create ` MySystem_anim .tla` :
41+ For a specification ` MySpec .tla` , create ` MySpec_anim .tla` :
4242
4343``` tla
44- ---- MODULE MySystem_anim ----
45- EXTENDS TLC, SVG, IOUtils, MySystem
44+ ---- MODULE MySpec_anim ----
45+ EXTENDS TLC, SVG, IOUtils, MySpec
4646
4747\* Define visual representation
4848AnimView == Circle(50, 50, 20, ("fill" :> "blue"))
4949
50- \* Enable SVG file generation
50+ \* Wrap view in SVG document (single source of truth for viewBox)
51+ AnimDoc == SVGDoc(AnimView, 0, 0, 100, 100, <<>>)
52+
53+ \* Enable SVG file generation (for TLC model checking)
5154AnimAlias ==
5255 [state |-> state] @@ \* Include state variables for debugging
53- [ _anim |-> SVGSerialize(
54- SVGDoc(AnimView, 0, 0, 100, 100, <<>>),
55- "MySystem_anim_",
56- TLCGet("level")
57- )]
56+ [_anim |-> SVGSerialize(AnimDoc, "MySpec_anim_", TLCGet("level"))]
57+
58+ \* Alternative: For interactive debugging in TLA+ Debugger
59+ AnimWatch ==
60+ SVGSerialize(AnimDoc, "MySpec_anim", 0)
5861====
5962```
6063
6164### 2. Validate Animation
6265
6366Use the MCP server to check syntax:
6467```
65- tlaplus_mcp_sany_parse MySystem_anim .tla
68+ tlaplus_mcp_sany_parse MySpec_anim .tla
6669```
6770
6871### 3. Generate Animation SVGs
6972
7073Create configuration file and use MCP to explore states:
7174```
72- tlaplus_mcp_tlc_explore MySystem_anim .tla -depth 10
75+ tlaplus_mcp_tlc_explore MySpec_anim .tla -depth 10
7376```
7477
7578## Animation File Structure
@@ -88,18 +91,88 @@ EXTENDS TLC, \* TLC built-ins (@@, :>, etc.)
8891\* Visual representation logic
8992AnimView == \* Returns SVG elements based on current state
9093
91- \* File generation configuration
92- AnimAlias == \* Configures SVG file output
94+ \* File generation configuration (choose one approach)
95+ AnimAlias == \* For TLC model checking (generates numbered frames)
96+ AnimWatch == \* For TLA+ Debugger (live preview)
9397====
9498```
9599
96100### Key Components Explained
97101
98- 1 . ** AnimView** : The main operator that creates SVG elements based on the current state
99- 2 . ** AnimAlias** : Configures how SVG files are generated and named
100- 3 . ** SVG Module** : Provides constructors for visual elements (Circle, Rect, Text, etc.)
101- 4 . ** IOUtils Module** : Handles file writing operations
102- 5 . ** State Mapping** : Your logic that transforms state variables into visual properties
102+ 1 . ** AnimView** : The main operator that creates SVG elements based on the current state. This is the core of your animation - it defines how state variables map to visual properties.
103+ 2 . ** AnimDoc** : Wraps ` AnimView ` in an SVG document with viewBox parameters. This provides a single source of truth for document dimensions, eliminating duplication between ` AnimAlias ` and ` AnimWatch ` .
104+ 3 . ** AnimAlias** or ** AnimWatch** : Two different approaches for serializing SVG to disk (detailed below)
105+ 4 . ** SVG Module** : Provides constructors for visual elements (Circle, Rect, Text, etc.)
106+ 5 . ** IOUtils Module** : Handles file writing operations
107+ 6 . ** State Mapping** : Your logic that transforms state variables into visual properties
108+
109+ ## Serialization Approaches: AnimAlias vs AnimWatch
110+
111+ While ` AnimView ` defines * what* to visualize, you need a serialization mechanism to * output* the SVG frames. TLA+ animations support two approaches, each suited to different workflows:
112+
113+ ### AnimAlias: For Screencasts and Animation Sequences
114+
115+ ** Use Case** : Creating animation sequences for documentation, presentations, or screencasts.
116+
117+ ** How it works** :
118+ - Add ` ALIAS AnimAlias ` to your TLC configuration file (` .cfg ` )
119+ - TLC automatically evaluates ` AnimAlias ` at each state during counterexample generation
120+ - Each state generates a ** unique numbered SVG file**
121+ - Frame numbering uses ` TLCGet("level") ` to create sequential files
122+
123+ ** Example** :
124+ ``` tla
125+ AnimDoc == SVGDoc(AnimView, 0, 0, 760, 420, <<>>)
126+
127+ AnimAlias ==
128+ [active |-> active, color |-> color, counter |-> counter] @@
129+ [_anim |-> SVGSerialize(
130+ AnimDoc, \* Reusable document
131+ "MySpec_anim_", \* Filename prefix
132+ TLCGet("level"))] \* Unique frame number
133+ ```
134+
135+ ** Generated files** : ` MySpec_anim_0.svg ` , ` MySpec_anim_1.svg ` , ` MySpec_anim_2.svg ` , ...
136+
137+ ** Configuration file** :
138+ ```
139+ INIT Init
140+ NEXT Next
141+ ALIAS AnimAlias
142+ ```
143+
144+ ### AnimWatch: For Interactive Debugging
145+
146+ ** Use Case** : Live visualization during step-by-step debugging sessions.
147+
148+ ** How it works** :
149+ - Use as a Watch expression in the TLA+ Debugger
150+ - Each evaluation ** overwrites the same SVG file** (frame number 0)
151+ - Pair with a live SVG viewer that auto-refreshes (e.g., [ SVG extension] ( https://open-vsx.org/extension/jock/svg ) )
152+ - See animation update in real-time as you step through execution
153+
154+ ** Example** :
155+ ``` tla
156+ AnimDoc == SVGDoc(AnimView, 0, 0, 760, 420, <<>>)
157+
158+ AnimWatch ==
159+ SVGSerialize(
160+ AnimDoc, \* Reusable document
161+ "MySpec_anim", \* Filename prefix
162+ 0) \* Frame 0 (overwrites each time)
163+ ```
164+
165+ ** Generated file** : ` MySpec_anim00.svg ` (single file, continuously updated)
166+
167+ ** Debugger usage** :
168+ When debugging ` MySpec.tla ` , add this watch expression:
169+ ```
170+ LET A == INSTANCE MySpec_anim IN A!AnimWatch
171+ ```
172+
173+ ### Choosing Between AnimAlias and AnimWatch
174+
175+ You don't need to choose—include * both* in your animation file! Use ` AnimAlias ` for documentation and ` AnimWatch ` for development/debugging.
103176
104177## Creating Your First Animation
105178
@@ -146,18 +219,21 @@ AnimView == Group([n \in 1..Len(NodeSeq) |-> NodeElement(NodeSeq[n])],
146219 ("transform" :> "scale(1.2)"))
147220```
148221
149- ### Step 3: Add AnimAlias for TLC
222+ ### Step 3: Add Serialization Operators
150223
151224Enable file generation:
152225
153226``` tla
227+ \* Wrap view in SVG document
228+ AnimDoc == SVGDoc(AnimView, 0, 0, 400, 200, <<>>)
229+
230+ \* For TLC model checking (generates numbered frames)
154231AnimAlias ==
155- [ _anim |-> SVGSerialize(
156- SVGDoc(AnimView, 0, 0, 400, 200, <<>>),
157- "DistributedSystem_anim_",
158- TLCGet("level")
159- )]
232+ [_anim |-> SVGSerialize(AnimDoc, "DistributedSystem_anim_", TLCGet("level"))]
160233
234+ \* For interactive debugging (live preview)
235+ AnimWatch ==
236+ SVGSerialize(AnimDoc, "DistributedSystem_anim", 0)
161237```
162238
163239## Complete Example
@@ -245,18 +321,21 @@ StepNumber ==
245321AnimView ==
246322 Group(<<Legend, StepNumber>> \o [i \in 1..Len(NodeSeq) |-> NodeVis(NodeSeq[i])], <<>>)
247323
248- \* TLC animation export
324+ \* Wrap view in document structure
325+ AnimDoc == SVGDoc(AnimView, 0, 0, 100 + Cardinality(Nodes) * NodeSpacing, 200, <<>>)
326+
327+ \* TLC animation export (for screencasts)
249328AnimAlias ==
250- [ counter |-> counter ] @@
251- [ _anim |-> SVGSerialize(
252- SVGDoc(AnimView, 0, 0, 100 + Cardinality(Nodes) * NodeSpacing, 200, <<>>),
253- "CounterSystem_anim_",
254- TLCGet("level")
255- )]
329+ [counter |-> counter] @@
330+ [_anim |-> SVGSerialize(AnimDoc, "CounterSystem_anim_", TLCGet("level"))]
331+
332+ \* Interactive debugging (for live preview)
333+ AnimWatch ==
334+ SVGSerialize(AnimDoc, "CounterSystem_anim", 0)
256335====
257336```
258337
259- ### 3. Configuration File
338+ ### 3. Configuration File (for AnimAlias)
260339
261340```
262341CONSTANT Nodes = {n1, n2, n3}
@@ -902,13 +981,15 @@ StepNumber ==
902981
903982AnimView == Group(<<Left, Right, Battery, Chargers, Empty, StepNumber>>, [i \in {} |-> {}])
904983
984+ AnimDoc == SVGDoc(AnimView, 0, 0, 230, 200, <<>>)
985+
905986AnimAlias ==
906987 [left |-> left, right |-> right,
907988 batteryLeft |-> batteryLeft, batteryLevel |-> batteryLevel] @@
908- [ _anim |-> SVGSerialize(
909- SVGDoc(AnimView, 0, 0, 230, 200, <<>>),
910- "BatteryRelay_anim_",
911- TLCGet("level"))]
989+ [_anim |-> SVGSerialize(AnimDoc, "BatteryRelay_anim_", TLCGet("level"))]
990+
991+ AnimWatch ==
992+ SVGSerialize(AnimDoc, "BatteryRelay_anim", 0)
912993====
913994```
914995
@@ -1062,12 +1143,14 @@ AnimView ==
10621143 Group(<<ProducerLabel, BufferLabel, ConsumerLabel, BufferStatus, FlowArrows, StepNumber>> \o
10631144 Prod \o Buffer \o Conss, <<>>)
10641145
1146+ AnimDoc == SVGDoc(AnimView, 0, 0, 560, 350, <<>>)
1147+
10651148AnimAlias ==
10661149 [buffer |-> buffer, waitSet |-> waitSet] @@
1067- [ _anim |-> SVGSerialize(
1068- SVGDoc(AnimView, 0, 0, 560, 350, <<>>),
1069- "BlockingQueue_anim_",
1070- TLCGet("level"))]
1150+ [_anim |-> SVGSerialize(AnimDoc, "BlockingQueue_anim_", TLCGet("level"))]
1151+
1152+ AnimWatch ==
1153+ SVGSerialize(AnimDoc, "BlockingQueue_anim", 0)
10711154====
10721155```
10731156
@@ -1142,12 +1225,14 @@ RiverElem ==
11421225AnimView ==
11431226 Group(<<SideElem(1), SideElem(2), SuccessElem(2), RiverElem, BoatElem>>, <<>>)
11441227
1228+ AnimDoc == SVGDoc(AnimView, 0, 0, 530, 420, <<>>)
1229+
11451230AnimAlias ==
11461231 [banks |-> banks, boat |-> boat] @@
1147- [ _anim |-> SVGSerialize(
1148- SVGDoc(AnimView, 0, 0, 530, 420, <<>>),
1149- "CabbageGoatWolf_anim_",
1150- TLCGet("level"))]
1232+ [_anim |-> SVGSerialize(AnimDoc, "CabbageGoatWolf_anim_", TLCGet("level"))]
1233+
1234+ AnimWatch ==
1235+ SVGSerialize(AnimDoc, "CabbageGoatWolf_anim", 0)
11511236====
11521237```
11531238
@@ -1216,12 +1301,14 @@ RingFork ==
12161301AnimView ==
12171302 Group(RingPhil \o RingFork, <<>>)
12181303
1304+ AnimDoc == SVGDoc(AnimView, 0, 0, 270, 270, <<>>)
1305+
12191306AnimAlias ==
12201307 [philosophers |-> philosophers, forks |-> forks] @@
1221- [ _anim |-> SVGSerialize(
1222- SVGDoc(AnimView, 0, 0, 270, 270, <<>>),
1223- "DiningPhilosophers_anim_",
1224- TLCGet("level"))]
1308+ [_anim |-> SVGSerialize(AnimDoc, "DiningPhilosophers_anim_", TLCGet("level"))]
1309+
1310+ AnimWatch ==
1311+ SVGSerialize(AnimDoc, "DiningPhilosophers_anim", 0)
12251312====
12261313```
12271314
@@ -1292,13 +1379,15 @@ TextElems == RMTextElems \o TMTextElems
12921379AnimView ==
12931380 Group(RMElems \o <<TMElem>> \o TextElems, <<>>)
12941381
1382+ AnimDoc == SVGDoc(AnimView, 0, 0, 180, 200, <<>>)
1383+
12951384AnimAlias ==
12961385 [rmState |-> rmState, tmState |-> tmState,
12971386 tmPrepared |-> tmPrepared, msgs |-> msgs] @@
1298- [ _anim |-> SVGSerialize(
1299- SVGDoc(AnimView, 0, 0, 180, 200, <<>>),
1300- "TwoPhase_anim_",
1301- TLCGet("level"))]
1387+ [_anim |-> SVGSerialize(AnimDoc, "TwoPhase_anim_", TLCGet("level"))]
1388+
1389+ AnimWatch ==
1390+ SVGSerialize(AnimDoc, "TwoPhase_anim", 0)
13021391=============================================================================
13031392```
13041393
@@ -1429,14 +1518,16 @@ AnimView ==
14291518 Group(cs \o labels \o termLabels \o logElems \o
14301519 safetyViolationElems \o configVersionTermTitleLabel, <<>>)
14311520
1521+ AnimDoc == SVGDoc(AnimView, 0, 0, 720, 350, <<>>)
1522+
14321523AnimAlias ==
14331524 [currentTerm |-> currentTerm, state |-> state, log |-> log,
14341525 immediatelyCommitted |-> immediatelyCommitted, config |-> config,
14351526 configVersion |-> configVersion, configTerm |-> configTerm] @@
1436- [ _anim |-> SVGSerialize(
1437- SVGDoc(AnimView, 0, 0, 720, 350, <<>>),
1438- "MongoRaftReconfig_anim_",
1439- TLCGet("level"))]
1527+ [_anim |-> SVGSerialize(AnimDoc, "MongoRaftReconfig_anim_", TLCGet("level"))]
1528+
1529+ AnimWatch ==
1530+ SVGSerialize(AnimDoc, "MongoRaftReconfig_anim", 0)
14401531=============================================================================
14411532```
14421533
0 commit comments