Monorepo for Aesthetic.Computer
aesthetic.computer
1# Robo System Plan
2
3**Date:** 2025.09.16
4**Purpose:** Create an automated brush system that can robotically execute brushes in non-interactive mode
5
6## Overview
7
8The `robo` command will create an automated drawing system that can import and execute existing brush files (like `box.mjs`) in a programmatic, non-interactive mode. This creates a "plotter-like" system that can generate procedural art using the existing brush ecosystem.
9
10## Core Concept
11
12```
13robo box # Load box.mjs brush and run automatically
14robo box fade:red-blue # Load box.mjs with color parameters
15robo circle count:50 # Load circle.mjs and draw 50 circles
16```
17
18## Architecture
19
20### 1. Robo Piece Structure (`robo.mjs`)
21
22```javascript
23// robo.mjs - Main robo system piece
24function boot({ params, colon }) {
25 const brushName = params[0]; // e.g., "box"
26 const brushParams = params.slice(1); // e.g., ["fade:red-blue"]
27 const robotSettings = parseColon(colon); // e.g., speed:2, count:10
28}
29
30async function paint({ frame }) {
31 // Execute frame-by-frame robotic drawing
32 if (loadedBrush && robotState.active) {
33 executeRoboticFrame(frame);
34 }
35}
36```
37
38### 2. Dynamic Brush Loading
39
40Based on research of `disk.mjs` loading mechanisms:
41
42```javascript
43// Inside robo.mjs boot function
44async function loadBrush(brushName) {
45 try {
46 const brushModule = await import(`../disks/${brushName}.mjs`);
47 return {
48 overlay: brushModule.overlay,
49 lift: brushModule.lift,
50 brush: brushModule.brush, // Legacy support
51 boot: brushModule.boot
52 };
53 } catch (err) {
54 console.error(`Failed to load brush: ${brushName}`, err);
55 return null;
56 }
57}
58```
59
60### 3. Path Generation System
61
62The robo system needs to generate synthetic path data that mimics user interaction:
63
64```javascript
65class RoboPathGenerator {
66 constructor() {
67 this.currentPath = [];
68 this.pathQueue = [];
69 }
70
71 // Generate geometric patterns
72 generateGrid(rows, cols, spacing) {
73 for (let row = 0; row < rows; row++) {
74 for (let col = 0; col < cols; col++) {
75 this.pathQueue.push(this.createBoxPath(
76 col * spacing,
77 row * spacing,
78 spacing * 0.8,
79 spacing * 0.8
80 ));
81 }
82 }
83 }
84
85 generateSpiral(centerX, centerY, radius, steps) {
86 // Create spiral path
87 }
88
89 generateRandom(count, bounds) {
90 // Create random placement paths
91 }
92
93 createBoxPath(x, y, w, h) {
94 return {
95 type: 'drag',
96 startPoint: { x, y },
97 endPoint: { x: x + w, y: y + h },
98 duration: 60 // frames
99 };
100 }
101}
102```
103
104### 4. Brush Execution Engine
105
106Mock the nopaint system's brush execution:
107
108```javascript
109class RoboBrushExecutor {
110 constructor(brushModule, screen) {
111 this.brush = brushModule;
112 this.screen = screen;
113 this.mockSystem = this.createMockNopaintSystem();
114 }
115
116 createMockNopaintSystem() {
117 return {
118 nopaint: {
119 color: [255, 0, 0, 255], // Default red
120 brush: null,
121 finalDragBox: null,
122 buffer: null // Will be created
123 }
124 };
125 }
126
127 executePath(path, frame) {
128 const progress = frame / path.duration;
129 const currentBox = this.interpolateDragBox(path, progress);
130
131 // Mock the overlay call during drawing
132 if (progress < 1.0) {
133 this.mockSystem.nopaint.brush = { dragBox: currentBox };
134 this.callBrushFunction('overlay', {
135 mark: currentBox,
136 color: this.mockSystem.nopaint.color,
137 ink: this.createMockInk()
138 });
139 }
140
141 // Mock the lift call at completion
142 if (progress >= 1.0) {
143 this.mockSystem.nopaint.finalDragBox = currentBox;
144 this.callBrushFunction('lift', {
145 mark: currentBox,
146 color: this.mockSystem.nopaint.color,
147 ink: this.createMockInk()
148 });
149 return true; // Path complete
150 }
151
152 return false; // Path in progress
153 }
154
155 interpolateDragBox(path, progress) {
156 const { startPoint, endPoint } = path;
157 const currentEnd = {
158 x: startPoint.x + (endPoint.x - startPoint.x) * progress,
159 y: startPoint.y + (endPoint.y - startPoint.y) * progress
160 };
161
162 return {
163 x: startPoint.x,
164 y: startPoint.y,
165 w: currentEnd.x - startPoint.x,
166 h: currentEnd.y - startPoint.y
167 };
168 }
169
170 callBrushFunction(functionName, api) {
171 if (this.brush[functionName]) {
172 this.brush[functionName](api);
173 }
174 }
175
176 createMockInk() {
177 // Return ink function that interfaces with the robo rendering system
178 return (color) => ({
179 box: (mark, mode = 'fill') => {
180 this.renderBox(mark, color, mode);
181 return this; // Chainable
182 }
183 });
184 }
185}
186```
187
188### 5. Robo Timing System with `sim` Function
189
190Based on AC disk API research, use the `sim` function for precise timing control:
191
192```javascript
193// In robo.mjs - Main timing controller
194let robotState = {
195 active: false,
196 currentPath: null,
197 pathQueue: [],
198 completedPaths: [],
199 frameCounter: 0,
200 speed: 1.0,
201 pattern: 'grid',
202 brushName: null
203};
204
205// 🧮 Sim (Runs once per logic frame at 120fps locked)
206function sim({ simCount, needsPaint }) {
207 if (!robotState.active) return;
208
209 // Speed control: advance robot logic every N sim frames
210 const speedFrames = Math.max(1, Math.round(120 / (60 * robotState.speed)));
211
212 if (simCount % BigInt(speedFrames) === 0n) {
213 advanceRobotLogic();
214 needsPaint(); // Request paint update when robot state changes
215 }
216
217 // Update debug panel state
218 debugPanel.update(robotState);
219}
220
221function advanceRobotLogic() {
222 if (!robotState.currentPath && robotState.pathQueue.length > 0) {
223 // Start next path
224 robotState.currentPath = robotState.pathQueue.shift();
225 robotState.frameCounter = 0;
226 }
227
228 if (robotState.currentPath) {
229 robotState.frameCounter++;
230
231 // Calculate progress through current path
232 const progress = robotState.frameCounter / robotState.currentPath.duration;
233
234 if (progress >= 1.0) {
235 // Path complete - trigger lift
236 executePathCompletion(robotState.currentPath);
237 robotState.completedPaths.push(robotState.currentPath);
238 robotState.currentPath = null;
239 robotState.frameCounter = 0;
240 } else {
241 // Path in progress - trigger overlay
242 executePathProgress(robotState.currentPath, progress);
243 }
244 }
245
246 // Check if all paths complete
247 if (!robotState.currentPath && robotState.pathQueue.length === 0) {
248 robotState.active = false;
249 console.log("🤖 Robot drawing complete!");
250 }
251}
252```
253
254**Key `sim` Function Benefits:**
255- **120fps locked timing** - Consistent logic updates regardless of display refresh rate
256- **`simCount`** - BigInt counter for precise frame tracking
257- **`needsPaint()`** - Request paint updates only when robot state changes (performance)
258- **Speed control** - Use modulo with simCount to control robot execution speed
259- **Frame precision** - Perfect for smooth path interpolation and timing control
260
261Support various robotic drawing modes:
262
263```javascript
264const ROBO_MODES = {
265 // Grid patterns
266 'grid': { rows: 10, cols: 10, spacing: 50 },
267 'grid:5x3': { rows: 5, cols: 3, spacing: 80 },
268
269 // Organic patterns
270 'random': { count: 20, bounds: 'screen' },
271 'spiral': { center: 'screen', radius: 200, steps: 50 },
272
273 // Animation patterns
274 'wave': { amplitude: 100, frequency: 0.1 },
275 'orbit': { center: 'screen', radius: 150 }
276};
277
278// Example: robo box grid:5x3 fade:red-blue speed:2
279```
280
281### 6. Time Control
282
283```javascript
284class RoboTimeController {
285 constructor() {
286 this.speed = 1.0; // 1.0 = normal speed
287 this.pauseRequested = false;
288 this.stepMode = false;
289 }
290
291 // Speed control: robo box speed:0.5 (half speed)
292 setSpeed(multiplier) {
293 this.speed = Math.max(0.1, Math.min(10.0, multiplier));
294 }
295
296 // Step through frame by frame: robo box step
297 enableStepMode() {
298 this.stepMode = true;
299 this.speed = 0;
300 }
301
302 shouldAdvanceFrame() {
303 return !this.pauseRequested && (this.speed > 0 || this.stepMode);
304 }
305}
306```
307
308### 7. Virtual Orange Robot Cursor
309
310Add a CSS virtual cursor that shows where the robot is currently drawing:
311
312```javascript
313// Virtual cursor management
314function updateVirtualCursor(x, y, isDrawing = false) {
315 let cursor = document.getElementById('robo-virtual-cursor');
316
317 if (!cursor) {
318 cursor = document.createElement('div');
319 cursor.id = 'robo-virtual-cursor';
320 cursor.style.cssText = `
321 position: absolute;
322 width: 12px;
323 height: 12px;
324 background: orange;
325 border: 2px solid white;
326 border-radius: 50%;
327 pointer-events: none;
328 z-index: 10000;
329 box-shadow: 0 0 4px rgba(255, 165, 0, 0.6);
330 transition: all 0.1s ease;
331 ${isDrawing ? 'transform: scale(1.2);' : ''}
332 `;
333 document.body.appendChild(cursor);
334 }
335
336 // Position cursor at robot drawing location
337 cursor.style.left = `${x - 6}px`;
338 cursor.style.top = `${y - 6}px`;
339 cursor.style.opacity = robotState.active ? '1' : '0';
340
341 // Scale effect when drawing
342 cursor.style.transform = isDrawing ? 'scale(1.2)' : 'scale(1.0)';
343}
344
345function removeVirtualCursor() {
346 const cursor = document.getElementById('robo-virtual-cursor');
347 if (cursor) cursor.remove();
348}
349```
350
351**Virtual Cursor Features:**
352- **Orange robot dot** - Distinct from regular UI cursors
353- **Real-time position** - Shows exact robot drawing location
354- **Drawing state feedback** - Scales up when actively drawing
355- **Always visible** - Appears even when browser cursor is elsewhere
356- **Smooth movement** - CSS transitions for fluid robot motion
357
358Similar to the nopaint performance overlay, create a visual debug panel:
359
360```javascript
361class RoboDebugPanel {
362 constructor() {
363 this.visible = true;
364 this.position = { x: 10, y: 10 };
365 this.size = { w: 300, h: 200 };
366 }
367
368 render({ ink, write, screen }) {
369 if (!this.visible) return;
370
371 const { x, y } = this.position;
372 const { w, h } = this.size;
373
374 // Semi-transparent background panel
375 ink(0, 0, 0, 180).box(x, y, w, h);
376 ink(255, 255, 255, 80).box(x, y, w, h, "outline");
377
378 // Title
379 ink("cyan").write("🤖 ROBO DEBUG", x + 8, y + 16);
380
381 // Current state info
382 let lineY = y + 32;
383 const lineHeight = 12;
384
385 ink("white").write(`Brush: ${this.currentBrush || 'none'}`, x + 8, lineY);
386 lineY += lineHeight;
387
388 ink("yellow").write(`Pattern: ${this.currentPattern || 'none'}`, x + 8, lineY);
389 lineY += lineHeight;
390
391 ink("lime").write(`Speed: ${this.speed.toFixed(1)}x`, x + 8, lineY);
392 lineY += lineHeight;
393
394 ink("orange").write(`Frame: ${this.currentFrame}/${this.totalFrames}`, x + 8, lineY);
395 lineY += lineHeight;
396
397 // Progress bar
398 const progressW = w - 16;
399 const progress = this.totalFrames > 0 ? this.currentFrame / this.totalFrames : 0;
400 ink(50, 50, 50).box(x + 8, lineY, progressW, 8);
401 ink("cyan").box(x + 8, lineY, progressW * progress, 8);
402 lineY += 16;
403
404 // Current path info
405 if (this.currentPath) {
406 ink("magenta").write(`Path: ${this.currentPath.type}`, x + 8, lineY);
407 lineY += lineHeight;
408
409 ink("gray").write(`Start: ${this.currentPath.startPoint.x}, ${this.currentPath.startPoint.y}`, x + 8, lineY);
410 lineY += lineHeight;
411
412 ink("gray").write(`End: ${this.currentPath.endPoint.x}, ${this.currentPath.endPoint.y}`, x + 8, lineY);
413 lineY += lineHeight;
414 }
415
416 // Queue status
417 ink("white").write(`Queue: ${this.pathQueue.length} paths`, x + 8, lineY);
418 lineY += lineHeight;
419
420 // Mini grid visualization
421 this.renderMiniGrid(ink, x + 8, lineY, 100, 60);
422 }
423
424 renderMiniGrid(ink, x, y, w, h) {
425 // Background
426 ink(30, 30, 30).box(x, y, w, h);
427 ink(80, 80, 80).box(x, y, w, h, "outline");
428
429 // Grid lines
430 const gridSize = 10;
431 ink(60, 60, 60);
432 for (let gx = gridSize; gx < w; gx += gridSize) {
433 ink().line(x + gx, y, x + gx, y + h);
434 }
435 for (let gy = gridSize; gy < h; gy += gridSize) {
436 ink().line(x, y + gy, x + w, y + gy);
437 }
438
439 // Show completed paths as green dots
440 this.completedPaths?.forEach(path => {
441 const miniX = x + (path.startPoint.x / this.screenW) * w;
442 const miniY = y + (path.startPoint.y / this.screenH) * h;
443 ink("lime").circle(miniX, miniY, 2, true);
444 });
445
446 // Show current path as yellow
447 if (this.currentPath) {
448 const miniX = x + (this.currentPath.startPoint.x / this.screenW) * w;
449 const miniY = y + (this.currentPath.startPoint.y / this.screenH) * h;
450 ink("yellow").circle(miniX, miniY, 3, true);
451 }
452
453 // Show queued paths as gray dots
454 this.pathQueue?.forEach(path => {
455 const miniX = x + (path.startPoint.x / this.screenW) * w;
456 const miniY = y + (path.startPoint.y / this.screenH) * h;
457 ink("gray").circle(miniX, miniY, 1, true);
458 });
459 }
460
461 update(roboState) {
462 this.currentBrush = roboState.brushName;
463 this.currentPattern = roboState.pattern;
464 this.speed = roboState.speed;
465 this.currentFrame = roboState.frame;
466 this.totalFrames = roboState.totalFrames;
467 this.currentPath = roboState.currentPath;
468 this.pathQueue = roboState.pathQueue;
469 this.completedPaths = roboState.completedPaths;
470 this.screenW = roboState.screenWidth;
471 this.screenH = roboState.screenHeight;
472 }
473
474 toggle() {
475 this.visible = !this.visible;
476 }
477}
478```
479
480Integration in main robo piece:
481
482```javascript
483// In robo.mjs
484let debugPanel = new RoboDebugPanel();
485
486function paint({ ink, write, screen, keyboard }) {
487 // Toggle debug panel with 'd' key
488 if (keyboard.pressed.d) {
489 debugPanel.toggle();
490 }
491
492 // Execute robotic drawing
493 if (robotState.active) {
494 executeRoboticFrame();
495 }
496
497 // Update and render debug panel
498 debugPanel.update(robotState);
499 debugPanel.render({ ink, write, screen });
500}
501```
502
503### 8. Visual Pattern Preview
504
505Show upcoming pattern visually:
506
507```javascript
508function renderPatternPreview(ink, x, y, w, h) {
509 ink(0, 0, 0, 100).box(x, y, w, h);
510
511 // Show the complete pattern as wireframe
512 pathGenerator.pathQueue.forEach((path, index) => {
513 const alpha = Math.max(50, 255 - index * 10); // Fade distant paths
514 const previewX = x + (path.startPoint.x / screen.width) * w;
515 const previewY = y + (path.startPoint.y / screen.height) * h;
516 const previewW = (path.endPoint.x - path.startPoint.x) / screen.width * w;
517 const previewH = (path.endPoint.y - path.startPoint.y) / screen.height * h;
518
519 ink(100, 100, 255, alpha).box(previewX, previewY, previewW, previewH, "outline");
520 });
521}
522
523## Implementation TODO
524
5251. **Create basic robo.mjs piece** (Start here!)
526 - Core structure with sim() function for 120fps logic timing
527 - Simple grid pattern generator (3x3 box grid)
528 - Robot state management with speed control
529 - Command parsing: `robo grid 3 box 0.5` (pattern, size, brush, speed)
530 - needsPaint() integration for performance
531 - Basic console logging for debug feedback
532
533### Phase 2: Path Generation
5341. Implement `RoboPathGenerator` class
5352. Add basic patterns: grid, random, spiral
5363. Create path interpolation system
5374. **Add `RoboDebugPanel` with mini-grid visualization**
5385. Test automated box drawing in patterns
539
540### Phase 3: Advanced Features
5411. Add time controls (speed, pause, step mode)
5422. Implement complex patterns (waves, orbits)
5433. Add parameter parsing for brush configuration
5444. Support fade colors and brush-specific parameters
5455. **Enhance debug panel with pattern preview and state visualization**
546
547### Phase 4: Integration & Polish
5481. Integrate with existing color system
5492. Add recording/playback capabilities
5503. Create export functionality for generated art
5514. Add real-time control interface
552
553## Technical Research Findings
554
555### Brush Loading Pattern
556- Use `await import()` for dynamic loading (found in `disk.mjs:4776`)
557- Brushes export `overlay`, `lift`, and optionally `brush` functions
558- Need to mock the nopaint system structure for brush execution
559
560### Nopaint System Structure
561- Brushes expect `mark` (dragBox coordinates) and `color` parameters
562- `overlay` function shows preview during drawing
563- `lift` function executes final drawing when stroke completes
564- System provides `ink()` function for actual rendering
565
566### Drawing Data Flow
567- User interaction creates `dragBox` coordinates
568- `dragBox` contains `{x, y, w, h}` rectangle data
569- Brushes use this data to determine what/where to draw
570- For robo mode, we synthesize this data programmatically
571
572## Example Usage
573
574```bash
575# Basic automated box drawing
576robo box
577
578# Grid of red boxes with debug panel
579robo box grid fade:red debug
580
581# Spiral pattern with debug visualization
582robo box spiral fade:blue-green speed:0.5 debug
583
584# Random placement with custom count and mini-grid view
585robo box random count:50 fade:rainbow debug
586
587# Step-through mode for debugging (press 'd' to toggle panel)
588robo box grid:3x3 step
589```
590
591**Debug Panel Features:**
592- 🤖 Real-time robo state display
593- 📊 Progress bar and frame counter
594- 🗺️ Mini-grid showing completed/current/queued paths
595- ⏱️ Speed and timing information
596- 🎯 Current path coordinates and type
597- 👀 Pattern preview wireframe overlay
598
599## Future Extensions
600
6011. **Brush Composition**: Combine multiple brushes in sequence
6022. **Physics Simulation**: Add gravity, collision for realistic movement
6033. **AI Integration**: Use ML to generate interesting patterns
6044. **Live Coding**: Real-time pattern modification while drawing
6055. **Network Sync**: Coordinate multiple robo instances across devices
606
607## Benefits
608
6091. **Automated Art Generation**: Create complex artworks without manual drawing
6102. **Brush Testing**: Systematically test brushes across different scenarios
6113. **Pattern Libraries**: Build reusable pattern generation systems
6124. **Educational Tool**: Demonstrate brush behavior and geometric concepts
6135. **Performance Art**: Live automated drawing performances
614
615This system would essentially create a "turtle graphics" style plotter that can use any existing brush in the Aesthetic Computer ecosystem, opening up powerful possibilities for procedural art generation and automated drawing systems.