Monorepo for Aesthetic.Computer
aesthetic.computer
1# KidLisp Live Parameter Editing
2
3## 🎯 Goal
4Enable live editing of KidLisp variables (like framerate, line coordinates, colors) while the program is running, without restarting from frame 0.
5
6## 💡 Concept
7When you change a `def` value in the editor while the code is playing, update only that variable in the running instance instead of reloading the entire program.
8
9## 🎬 User Experience
10
11### Before (Current)
12```lisp
13(def speed 5)
14(def color "red")
15(line 0 0 100 100)
16```
17- User edits `speed 5` → `speed 10`
18- Presses Enter/Play
19- **Program restarts from frame 0** (frameCount resets, all state lost)
20
21### After (Proposed)
22```lisp
23(def speed 5)
24(def color "red")
25(line 0 0 100 100)
26```
27- User edits `speed 5` → `speed 10`
28- **Variable updates live**, program keeps running
29- Frame count continues, timing state preserved
30- Only the changed variable updates
31
32## 🔧 Implementation Strategy
33
34### Phase 1: Variable Hot-Swapping (Simplest)
35
36Detect when only `def` statements have changed:
37
38```javascript
39// In kidlisp.mjs
40updateVariable(name, value) {
41 // Update the variable without restarting
42 this.globalDef[name] = value;
43 // Keep frameCount, timing state, etc.
44}
45
46// In kidlisp.com or boot.mjs
47function handleCodeUpdate(newCode, oldCode) {
48 const changedDefs = detectChangedDefs(newCode, oldCode);
49
50 if (changedDefs.length > 0 && onlyDefsChanged(newCode, oldCode)) {
51 // Hot-swap the variables
52 changedDefs.forEach(({ name, value }) => {
53 kidlispInstance.updateVariable(name, value);
54 });
55 // Don't reload, keep running!
56 } else {
57 // Structure changed, do full reload
58 kidlispInstance.reload(newCode);
59 }
60}
61```
62
63### Phase 2: Smart Diff Detection
64
65Compare AST to detect what changed:
66
67```javascript
68function detectChanges(oldAST, newAST) {
69 return {
70 defsChanged: [...], // Variable definitions that changed
71 codeChanged: false, // Whether non-def code changed
72 timingChanged: false, // Whether timing expressions changed
73 };
74}
75```
76
77### Phase 3: Partial Recompilation (Advanced)
78
79For more complex changes:
80- Changed `def` → hot-swap variable
81- Changed function body → recompile function, keep state
82- Changed timing expression → update timing map, keep counters
83- Changed drawing code → reparse that section
84
85## 📊 What to Preserve
86
87### Must Keep Running
88- `frameCount` - Current frame number
89- `lastSecondExecutions` - Timing state for `1s...`, `2s...` etc
90- `sequenceCounters` - Sequence state for timing
91- `onceExecuted` - Track of `once` blocks
92- Embedded layer buffers
93- `bakes` - Baked background layers
94
95### Can Update
96- `globalDef` - Variables defined with `(def ...)`
97- Parsed AST if code structure changed
98- Function definitions (`later` functions)
99
100## 🎨 UI Considerations
101
102### Visual Feedback
103```
104🔴 PLAYING (Frame 234) ← Show it's running
105✏️ Variable 'speed' updated (5 → 10) ← Confirm change
106```
107
108### Keyboard Shortcuts
109- `Cmd+Enter` - Smart reload (hot-swap if possible, full reload if needed)
110- `Cmd+Shift+Enter` - Force full reload (restart from frame 0)
111
112## 🏗️ Architecture
113
114### kidlisp.mjs Changes
115```javascript
116class KidLisp {
117 // New method
118 hotUpdateVariable(name, value) {
119 this.globalDef[name] = value;
120 // Invalidate any cached evaluations that use this variable
121 this.invalidateVariableCache(name);
122 }
123
124 // New method
125 canHotUpdate(newSource) {
126 const newAST = this.parse(newSource);
127 const diff = this.diffAST(this.ast, newAST);
128 return diff.onlyDefsChanged;
129 }
130
131 // Enhanced reload
132 reload(newSource, options = {}) {
133 if (options.preserveState && this.canHotUpdate(newSource)) {
134 // Hot update path
135 this.hotUpdate(newSource);
136 } else {
137 // Full reload path (current behavior)
138 this.fullReload(newSource);
139 }
140 }
141}
142```
143
144### boot.mjs Changes
145```javascript
146// Enhance existing kidlisp-reload handler
147if (event.data?.type === "kidlisp-reload") {
148 const code = event.data.code;
149 const preserveState = event.data.preserveState !== false;
150
151 window.acSEND({
152 type: "piece-reload",
153 content: {
154 source: code,
155 preserveState: preserveState // NEW
156 }
157 });
158}
159```
160
161### kidlisp.com Changes
162```javascript
163// Add option to preserve state
164function sendCode(preserveState = true) {
165 previewIframe.contentWindow.postMessage({
166 type: 'kidlisp-reload',
167 code: editor.getValue(),
168 preserveState: preserveState // NEW
169 }, aestheticUrl);
170}
171
172// Keyboard shortcuts
173editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => {
174 sendCode(true); // Smart reload
175});
176
177editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.Enter, () => {
178 sendCode(false); // Force full reload
179});
180```
181
182## 🧪 Test Cases
183
184### Should Hot-Update
185```lisp
186; Change value
187(def speed 5) → (def speed 10)
188
189; Change color
190(def color "red") → (def color "blue")
191
192; Change multiple defs
193(def x 10)
194(def y 20)
195```
196
197### Should Full-Reload
198```lisp
199; Add new code
200(line 0 0 100 100) → (line 0 0 100 100) (circle 50 50 25)
201
202; Change timing
203(1s (ink red)) → (2s (ink red))
204
205; Change structure
206(repeat 5 i (box i)) → (repeat 10 i (box i))
207```
208
209## 🚀 Benefits
210
2111. **Faster iteration** - Tweak values without losing animation progress
2122. **Better debugging** - See parameter effects immediately
2133. **Live performance** - Adjust values during live coding performances
2144. **Teaching tool** - Show immediate cause/effect of parameter changes
215
216## 📝 Notes
217
218- Start simple: only hot-swap `def` changes
219- Can expand to timing expressions, function bodies later
220- Keep full reload as fallback for complex changes
221- Consider auto-detection vs manual mode selection
222
223## 🎯 Success Criteria
224
225- ✅ Can change `(def speed 5)` to `(def speed 10)` while running
226- ✅ Frame count continues from current value
227- ✅ Timing state (1s..., 2s...) preserved
228- ✅ Visual feedback shows what was updated
229- ✅ Fallback to full reload for structural changes