Monorepo for Aesthetic.Computer aesthetic.computer
at main 299 lines 7.4 kB view raw view rendered
1# Aesthetic Computer Piece Structure 2 3*Understanding how to create and structure AC pieces* 4 5## Overview 6 7Aesthetic Computer pieces are JavaScript modules that define interactive experiences. Each piece exports specific functions that hook into the AC lifecycle, providing entry points for initialization, rendering, input handling, and cleanup. 8 9## Piece Lifecycle 10 11### Core Functions 12 13Every piece can export these optional functions: 14 15#### `boot({ ... })` 16**Purpose**: Initialize the piece when it starts 17**When Called**: Once when the piece loads 18**Common Uses**: Setup variables, load resources, configure settings 19 20```javascript 21export function boot({ wipe, screen, resolution }) { 22 // Initialize piece state 23 resolution(800, 600); 24 console.log(`Piece started with screen: ${screen.width}x${screen.height}`); 25} 26``` 27 28#### `paint({ ... })` 29**Purpose**: Render the visual output 30**When Called**: Every frame at the display refresh rate (typically 60fps) 31**Common Uses**: Draw graphics, update animations, render UI 32 33```javascript 34export function paint({ wipe, ink, box, circle }) { 35 wipe("black"); // Clear screen 36 ink("white"); // Set color 37 box(50, 50, 100, 100); // Draw rectangle 38 circle(200, 200, 50); // Draw circle 39} 40``` 41 42#### `act({ event, ... })` 43**Purpose**: Handle user input and system events 44**When Called**: When input events occur (clicks, key presses, etc.) 45**Common Uses**: Respond to user interaction, state changes 46 47```javascript 48export function act({ event, pointer, keyboard }) { 49 if (event.is("pointer:down")) { 50 console.log(`Clicked at: ${pointer.x}, ${pointer.y}`); 51 } 52 53 if (event.is("keyboard:down:space")) { 54 console.log("Space key pressed"); 55 } 56} 57``` 58 59#### `sim({ ... })` 60**Purpose**: Update simulation logic 61**When Called**: At a fixed rate (typically 120fps), independent of rendering 62**Common Uses**: Physics calculations, game logic, state updates 63 64```javascript 65let position = 0; 66export function sim({ }) { 67 // Update position independent of framerate 68 position += 0.5; 69 if (position > 800) position = 0; 70} 71``` 72 73#### `beat({ ... })` 74**Purpose**: Synchronize with musical timing 75**When Called**: On metronome beats based on BPM setting 76**Common Uses**: Musical synchronization, rhythmic animations 77 78```javascript 79export function beat({ bpm }) { 80 console.log(`Beat at ${bpm} BPM`); 81 // Trigger rhythmic events 82} 83``` 84 85#### `leave({ ... })` 86**Purpose**: Clean up before the piece unloads 87**When Called**: Right before switching to another piece 88**Common Uses**: Save state, clean up resources, send final data 89 90```javascript 91export function leave({ store }) { 92 store["myPiece:lastPosition"] = position; 93 console.log("Piece is leaving"); 94} 95``` 96 97### Optional Functions 98 99#### `meta({ ... })` 100**Purpose**: Define piece metadata 101**When Called**: Once during piece initialization 102**Common Uses**: Set title, description, configuration 103 104```javascript 105export function meta({ store }) { 106 return { 107 title: "My Amazing Piece", 108 description: "An interactive art experience", 109 author: "Artist Name" 110 }; 111} 112``` 113 114#### `preview({ ... })` 115**Purpose**: Create custom thumbnail 116**When Called**: When generating piece previews 117**Common Uses**: Draw representative thumbnail image 118 119```javascript 120export function preview({ wipe, ink, write, slug }) { 121 wipe(64); 122 ink(255); 123 write(slug, { center: "xy", size: 1 }); 124} 125``` 126 127## API Access 128 129Each lifecycle function receives an API object containing all available functions and data: 130 131### Common API Properties 132- **Graphics**: `wipe`, `ink`, `box`, `circle`, `line`, `plot`, `write` 133- **Input**: `pointer`, `keyboard`, `event` 134- **Screen**: `screen` (width, height), `resolution` 135- **Time**: `frame`, `time`, `beat` 136- **Storage**: `store` (persistent data) 137- **Network**: `send`, `receive`, `session` 138- **Audio**: `tone`, `noise`, `sound` 139- **Utilities**: `random`, `map`, `constrain` 140 141### Destructuring Pattern 142```javascript 143export function paint({ wipe, ink, box, screen, pointer }) { 144 // Only destructure what you need 145 wipe("black"); 146 ink("cyan"); 147 box(pointer.x, pointer.y, 50, 50); 148} 149``` 150 151## Piece Types 152 153### Interactive Pieces 154Focus on user input and response: 155```javascript 156let drawing = []; 157 158export function paint({ wipe, ink, line }) { 159 wipe("black"); 160 ink("white"); 161 162 // Draw all lines in drawing array 163 for (let i = 0; i < drawing.length - 1; i++) { 164 line(drawing[i].x, drawing[i].y, 165 drawing[i+1].x, drawing[i+1].y); 166 } 167} 168 169export function act({ event, pointer }) { 170 if (event.is("pointer:down") || event.is("pointer:drag")) { 171 drawing.push({ x: pointer.x, y: pointer.y }); 172 } 173} 174``` 175 176### Generative Pieces 177Focus on procedural generation: 178```javascript 179export function paint({ wipe, ink, circle, screen, random }) { 180 wipe("navy"); 181 ink("cyan"); 182 183 // Generate random circles 184 for (let i = 0; i < 50; i++) { 185 circle( 186 random() * screen.width, 187 random() * screen.height, 188 random() * 20 + 5 189 ); 190 } 191} 192``` 193 194### Animation Pieces 195Focus on time-based changes: 196```javascript 197let rotation = 0; 198 199export function sim() { 200 rotation += 0.02; // Update rotation in sim for smooth animation 201} 202 203export function paint({ wipe, ink, box, screen, sin, cos }) { 204 wipe("black"); 205 ink("red"); 206 207 const centerX = screen.width / 2; 208 const centerY = screen.height / 2; 209 const radius = 100; 210 211 const x = centerX + sin(rotation) * radius; 212 const y = centerY + cos(rotation) * radius; 213 214 box(x, y, 20, 20, "center"); 215} 216``` 217 218## Configuration & Exports 219 220### System Configuration 221```javascript 222// Export configuration for special systems 223export const system = "nopaint"; // Use nopaint system 224export const palette = "custom"; // Custom color palette 225``` 226 227### Piece Settings 228```javascript 229// Piece-specific settings 230export const wrap = true; // Enable coordinate wrapping 231export const autolock = false; // Disable auto-locking 232``` 233 234## Best Practices 235 236### Performance 237- Keep `paint` function efficient (called 60+ times per second) 238- Use `sim` for heavy calculations 239- Minimize object creation in hot paths 240- Cache expensive calculations 241 242### State Management 243- Use module-level variables for piece state 244- Use `store` for persistent data across sessions 245- Initialize state in `boot`, clean up in `leave` 246 247### User Experience 248- Provide visual feedback for interactions 249- Handle edge cases gracefully 250- Consider accessibility (keyboard navigation, etc.) 251 252### Code Organization 253```javascript 254// State at module level 255let gameState = { score: 0, level: 1 }; 256let particles = []; 257 258// Helper functions 259function createParticle(x, y) { 260 return { x, y, vx: random(-2, 2), vy: random(-2, 2) }; 261} 262 263// Lifecycle functions 264export function boot({ screen }) { 265 // Initialize particles 266 for (let i = 0; i < 10; i++) { 267 particles.push(createParticle( 268 random() * screen.width, 269 random() * screen.height 270 )); 271 } 272} 273 274export function sim() { 275 // Update particle positions 276 particles.forEach(p => { 277 p.x += p.vx; 278 p.y += p.vy; 279 }); 280} 281 282export function paint({ wipe, ink, circle }) { 283 wipe("black"); 284 ink("white"); 285 286 // Render particles 287 particles.forEach(p => { 288 circle(p.x, p.y, 3); 289 }); 290} 291``` 292 293## Template Examples 294 295See the `/templates` folder for complete piece templates demonstrating different patterns and use cases. 296 297--- 298 299*For more details on the API functions available in each lifecycle function, see the [JavaScript API Reference](../api/javascript-api.md).*