Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: CL piece dispatch + 4 test pieces (spirals, pixels, waves, grid)

CL runtime now loads .lisp piece files and calls their paint function
in the main loop with DRM graphics, audio, and input. Each piece
defines a package (piece.<name>) with a paint function.

- spirals: rotating colorful spiral arms with tones
- pixels: accumulating color noise with sonification
- waves: layered sine waves with chord audio
- grid: cellular automata with frequency sonification

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+243 -2
+58 -2
fedac/native/cl/main.lisp
··· 322 322 323 323 (defvar *js-fallback* nil "Set to T when falling back from JS to prevent re-entry loop.") 324 324 325 + (defun run-cl-piece (paint-fn piece-name) 326 + "Run a CL piece with DRM graphics, audio, and input. PAINT-FN is called each frame." 327 + (format *error-output* "~%════════════════════════════════════~%") 328 + (format *error-output* " ~A (Common Lisp)~%" piece-name) 329 + (format *error-output* "════════════════════════════════════~%~%") 330 + (force-output *error-output*) 331 + (let ((display (ac-native.drm:drm-init))) 332 + (unless display (return-from run-cl-piece)) 333 + (let* ((dw (ac-native.drm:display-width display)) 334 + (dh (ac-native.drm:display-height display)) 335 + (scale (cond ((>= (min dw dh) 1440) 6) 336 + ((>= (min dw dh) 1080) 4) 337 + ((>= (min dw dh) 720) 3) 338 + (t 2))) 339 + (sw (floor dw scale)) 340 + (sh (floor dh scale)) 341 + (screen (ac-native.framebuffer:fb-create sw sh)) 342 + (graph (ac-native.graph:graph-create screen)) 343 + (input (ac-native.input:input-init dw dh scale)) 344 + (audio (ac-native.audio:audio-init))) 345 + (unwind-protect 346 + (loop 347 + ;; Input 348 + (ac-native.input:input-poll input) 349 + (dotimes (i (ac-native.input:input-event-count input)) 350 + (let ((ev (ac-native.input:input-event input i))) 351 + (when (and (= (ac-native.input:event-type ev) 1) 352 + (= (ac-native.input:event-code ev) 1)) 353 + (return)))) ; ESC 354 + ;; Paint 355 + (handler-case 356 + (funcall paint-fn graph sw sh audio) 357 + (error (e) 358 + (format *error-output* "[piece] error: ~A~%" e) 359 + (force-output *error-output*))) 360 + ;; Present 361 + (ac-native.drm:drm-present display screen scale) 362 + (sleep 1/60)) 363 + ;; Cleanup 364 + (when audio (ac-native.audio:audio-destroy audio)) 365 + (ac-native.input:input-destroy input) 366 + (ac-native.framebuffer:fb-destroy screen) 367 + (ac-native.drm:drm-destroy display))))) 368 + 325 369 (defun main () 326 370 "AC Native OS entry point. Runs .mjs pieces via QuickJS or native CL notepat. 327 371 When called with --swank-only, starts Swank server and blocks (no graphics)." ··· 347 391 (when piece-arg 348 392 (format *error-output* "[cl] Running CL piece: ~A~%" piece-arg) 349 393 (force-output *error-output*) 350 - ;; Go straight to native CL notepat (the only CL piece for now) 351 - ;; Future: dispatch to different CL pieces based on piece-arg 394 + ;; Try to load the piece file and run its paint function 395 + (let ((piece-path (format nil "/pieces/~A.lisp" piece-arg))) 396 + (when (probe-file piece-path) 397 + (handler-case 398 + (progn 399 + (load piece-path) 400 + (let ((paint-fn (find-symbol "PAINT" (find-package 401 + (intern (string-upcase (format nil "PIECE.~A" piece-arg)) :keyword))))) 402 + (when (and paint-fn (fboundp paint-fn)) 403 + (return-from main (run-cl-piece paint-fn piece-arg))))) 404 + (error (e) 405 + (format *error-output* "[cl] Error loading ~A: ~A~%" piece-path e) 406 + (force-output *error-output*))))) 407 + ;; Fallback to native CL notepat 352 408 (setf *js-fallback* t))) 353 409 354 410 ;; Determine piece: config.json > command-line arg > default
+68
fedac/native/pieces/grid.lisp
··· 1 + ;;; grid.lisp — Cellular automata grid with sonification 2 + 3 + (defpackage :piece.grid 4 + (:use :cl)) 5 + (in-package :piece.grid) 6 + 7 + (defvar *frame* 0) 8 + (defvar *rng* (make-random-state t)) 9 + (defvar *cells* nil) 10 + (defvar *cols* 0) 11 + (defvar *rows* 0) 12 + 13 + (defun init-grid (cols rows) 14 + (setf *cols* cols *rows* rows) 15 + (setf *cells* (make-array (* cols rows) :initial-element 0)) 16 + ;; Random seed 17 + (dotimes (i (* cols rows)) 18 + (when (< (random 100 *rng*) 30) 19 + (setf (aref *cells* i) (1+ (random 5 *rng*)))))) 20 + 21 + (defun cell-at (x y) 22 + (if (and (>= x 0) (< x *cols*) (>= y 0) (< y *rows*)) 23 + (aref *cells* (+ (* y *cols*) x)) 24 + 0)) 25 + 26 + (defun step-grid () 27 + "Simple rule: each cell averages neighbors, with random mutation." 28 + (let ((new (make-array (* *cols* *rows*) :initial-element 0))) 29 + (dotimes (y *rows*) 30 + (dotimes (x *cols*) 31 + (let ((sum (+ (cell-at (1- x) y) (cell-at (1+ x) y) 32 + (cell-at x (1- y)) (cell-at x (1+ y))))) 33 + (setf (aref new (+ (* y *cols*) x)) 34 + (mod (+ (floor sum 2) 35 + (if (< (random 100 *rng*) 3) (random 6 *rng*) 0)) 36 + 6))))) 37 + (setf *cells* new))) 38 + 39 + (defun paint (graph screen-w screen-h audio) 40 + "Paint the cellular automata grid." 41 + (incf *frame*) 42 + (when (null *cells*) 43 + (init-grid (floor screen-w 4) (floor screen-h 4))) 44 + ;; Step every 4 frames 45 + (when (zerop (mod *frame* 4)) 46 + (step-grid)) 47 + ;; Draw 48 + (ac-native.graph:graph-wipe graph 49 + (ac-native.color:make-color :r 0 :g 0 :b 0 :a 255)) 50 + (let ((cell-w 4) (cell-h 4) 51 + (active-count 0)) 52 + (dotimes (y *rows*) 53 + (dotimes (x *cols*) 54 + (let ((v (cell-at x y))) 55 + (when (> v 0) 56 + (incf active-count) 57 + (let ((colors #((40 200 80) (200 60 60) (60 60 220) 58 + (200 200 40) (180 60 200)))) 59 + (let ((c (aref colors (min (1- (length colors)) (1- v))))) 60 + (ac-native.graph:graph-ink graph 61 + (ac-native.color:make-color 62 + :r (first c) :g (second c) :b (third c) :a 220)) 63 + (ac-native.graph:graph-box graph 64 + (* x cell-w) (* y cell-h) (1- cell-w) (1- cell-h) 1))))))) 65 + ;; Sonify: tone based on active cell count 66 + (when (and audio (zerop (mod *frame* 20))) 67 + (let ((freq (+ 100 (* 2 active-count)))) 68 + (ac-native.audio:audio-synth audio 0 freq 0.05 0.3 0.003 0.06 0.0)))))
+29
fedac/native/pieces/pixels.lisp
··· 1 + ;;; pixels.lisp — Animated pixel noise with color drift 2 + 3 + (defpackage :piece.pixels 4 + (:use :cl)) 5 + (in-package :piece.pixels) 6 + 7 + (defvar *frame* 0) 8 + (defvar *rng* (make-random-state t)) 9 + 10 + (defun paint (graph screen-w screen-h audio) 11 + "Paint one frame of pixel noise." 12 + (incf *frame*) 13 + ;; Don't wipe — let pixels accumulate and drift 14 + (let* ((density (+ 50 (round (* 200 (abs (sin (/ *frame* 120.0))))))) 15 + (hue-base (mod (* *frame* 2) 360))) 16 + (dotimes (i density) 17 + (let* ((x (random screen-w *rng*)) 18 + (y (random screen-h *rng*)) 19 + (hue (mod (+ hue-base (random 60 *rng*)) 360)) 20 + (r (min 255 (max 0 (round (* 255 (max 0 (sin (* hue 0.0174)))))))) 21 + (g (min 255 (max 0 (round (* 255 (max 0 (sin (* (+ hue 120) 0.0174)))))))) 22 + (b (min 255 (max 0 (round (* 255 (max 0 (sin (* (+ hue 240) 0.0174))))))))) 23 + (ac-native.graph:graph-ink graph 24 + (ac-native.color:make-color :r r :g g :b b :a 255)) 25 + (ac-native.graph:graph-plot graph x y))) 26 + ;; Sonify: tone based on average hue 27 + (when (and audio (zerop (mod *frame* 15))) 28 + (let ((freq (+ 100 (* 3 hue-base)))) 29 + (ac-native.audio:audio-synth audio 0 freq 0.04 0.2 0.002 0.04 0.0)))))
+45
fedac/native/pieces/spirals.lisp
··· 1 + ;;; spirals.lisp — Rotating spiral pattern 2 + ;;; Uses the ac-native CL graphics API directly. 3 + 4 + (defpackage :piece.spirals 5 + (:use :cl)) 6 + (in-package :piece.spirals) 7 + 8 + (defvar *frame* 0) 9 + 10 + (defun paint (graph screen-w screen-h audio) 11 + "Paint one frame of the spiral pattern." 12 + (incf *frame*) 13 + (let* ((cx (floor screen-w 2)) 14 + (cy (floor screen-h 2)) 15 + (t-sec (/ *frame* 60.0)) 16 + (arms 5) 17 + (points-per-arm 200)) 18 + ;; Dark background with subtle fade 19 + (ac-native.graph:graph-wipe graph 20 + (ac-native.color:make-color :r 5 :g 5 :b 15 :a 255)) 21 + ;; Draw spiral arms 22 + (dotimes (arm arms) 23 + (let ((arm-offset (* arm (/ (* 2 pi) arms)))) 24 + (dotimes (i points-per-arm) 25 + (let* ((r (* (/ (float i) points-per-arm) (min cx cy) 0.9)) 26 + (angle (+ arm-offset 27 + (* (/ (float i) points-per-arm) 4 pi) 28 + (* t-sec 0.5))) 29 + (x (+ cx (round (* r (sin (+ angle 1.5708)))))) 30 + (y (+ cy (round (* r (sin angle))))) 31 + ;; Color cycles with position and time 32 + (hue (mod (+ (* i 2) (* *frame* 3) (* arm 50)) 360)) 33 + (bright (max 80 (- 255 (round (* i 0.8)))))) 34 + (when (and (>= x 0) (< x screen-w) (>= y 0) (< y screen-h)) 35 + (ac-native.graph:graph-ink graph 36 + (ac-native.color:make-color 37 + :r (min 255 (+ 60 (round (* bright (max 0 (sin (* hue 0.0174))))))) 38 + :g (min 255 (+ 40 (round (* bright (max 0 (sin (* (+ hue 120) 0.0174))))))) 39 + :b (min 255 (+ 80 (round (* bright (max 0 (sin (* (+ hue 240) 0.0174))))))) 40 + :a 255)) 41 + (ac-native.graph:graph-plot graph x y)))))) 42 + ;; Occasional tone based on frame 43 + (when (and audio (zerop (mod *frame* 30))) 44 + (let ((freq (+ 200 (* 400 (sin (* t-sec 0.3)))))) 45 + (ac-native.audio:audio-synth audio 0 freq 0.08 0.3 0.005 0.07 0.0)))))
+43
fedac/native/pieces/waves.lisp
··· 1 + ;;; waves.lisp — Sine wave visualization with sound 2 + 3 + (defpackage :piece.waves 4 + (:use :cl)) 5 + (in-package :piece.waves) 6 + 7 + (defvar *frame* 0) 8 + 9 + (defun paint (graph screen-w screen-h audio) 10 + "Paint layered sine waves with matching audio." 11 + (incf *frame*) 12 + (let ((t-sec (/ *frame* 60.0))) 13 + ;; Dark blue-black background 14 + (ac-native.graph:graph-wipe graph 15 + (ac-native.color:make-color :r 2 :g 2 :b 12 :a 255)) 16 + ;; Draw 5 layered sine waves 17 + (dotimes (layer 5) 18 + (let* ((freq-mult (+ 1.0 (* layer 0.7))) 19 + (amplitude (* (- screen-h 20) (/ 0.15 (1+ (* layer 0.3))))) 20 + (y-offset (+ (floor screen-h 2) (* (- layer 2) 20))) 21 + (phase (* t-sec freq-mult 0.8)) 22 + (r (min 255 (round (* 50 (1+ layer))))) 23 + (g (min 255 (round (* 30 (- 5 layer))))) 24 + (b (min 255 (+ 100 (* 30 layer))))) 25 + (ac-native.graph:graph-ink graph 26 + (ac-native.color:make-color :r r :g g :b b :a 200)) 27 + ;; Draw wave as connected line segments 28 + (let ((prev-y y-offset)) 29 + (dotimes (x screen-w) 30 + (let* ((nx (/ (float x) screen-w)) 31 + (y (+ y-offset 32 + (round (* amplitude 33 + (sin (+ phase (* nx freq-mult 2 pi))))))) 34 + (cy (max 0 (min (1- screen-h) y)))) 35 + (when (> x 0) 36 + (ac-native.graph:graph-line graph (1- x) prev-y x cy)) 37 + (setf prev-y cy)))))) 38 + ;; Audio: play a chord based on the wave frequencies 39 + (when (and audio (zerop (mod *frame* 60))) 40 + (dotimes (layer 3) 41 + (let ((freq (* 220 (+ 1.0 (* layer 0.5))))) 42 + (ac-native.audio:audio-synth audio 0 freq 0.06 0.8 0.01 0.3 43 + (- (* layer 0.3) 0.3)))))))