CLAUDE.md#
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview#
Aesthetic Computer (AC) is a mobile-first runtime and social network for creative computing. It's designed as a musical instrument-like interface where users discover memorizable paths through commands and published "pieces" (interactive programs). The system supports both JavaScript (.mjs) and KidLisp (.lisp) pieces.
Agent Memory (Local-First)#
When @jeffrey is working, Claude hook events are written to a local encrypted memory store first.
- Hook:
.claude/settings.json→UserPromptSubmit→node memory/hook.mjs - Git hook:
.githooks/post-commit→ commit log + Codex import + remote flush - Local store:
~/.ac-agent-memory(overridable viaAGENT_MEMORY_HOME) - Encryption: AES-256-GCM (
AGENT_MEMORY_KEYoptional; local key file otherwise) - Redaction: metadata and summaries are redacted before indexing/sync
- CLI:
node memory/cli.mjs(list,remember,checkpoint,doctor,profile,flush-remote) - Codex import:
node memory/codex-sync.mjs
Remote writes are optional and disabled by default:
AGENT_MEMORY_REMOTE_ENABLED=true+AGENT_MEMORY_REMOTE_URL=...enables queued remote sync
remember continuity is represented as lineage (remembered_from) instead of taking over a live mutable session.
AestheticAnts & Score.md#
This repository uses an automated maintenance system called "AestheticAnts" (AA). The main score lives at SCORE.md, and ant-specific mindset/rules live in ants/mindset-and-rules.md. Read both before contributing.
Important: Do not modify ants/mindset-and-rules.md unless you are the queen (@jeffrey).
Development Commands#
Running the Development Environment#
The system consists of multiple servers that run concurrently:
# Run all servers (site, session, edge, stripe) - primary dev command
npm run aesthetic
# or shorthand:
npm run ac
# Run individual servers:
npm run site # Main dev server (port 8888)
npm run server:session # Session backend (port 8889)
npm run stripe # Stripe webhook listener
Testing#
# Run all tests
npm test
# KidLisp tests (with auto-reload on changes)
npm run test:kidlisp
# KidLisp tests (direct, no watch)
npm run test:kidlisp:direct
# Performance tests
npm run test:perf
npm run test:perf:chrome
npm run test:perf:lighthouse
Creating New Pieces#
# Generate a new piece from the blank.mjs template
npm run new piece-name "Description of the piece"
Working with Sessions#
# List active session backends
npm run session:alive
# View logs for a specific session
npm run server:session:logs SESSION_ID
# Reset/terminate a session
npm run session:reset SESSION_ID
# Deploy session server
npm run session:publish
Assets Management#
# Sync assets down from Digital Ocean Spaces
npm run assets:sync:down
# Sync assets up to Digital Ocean Spaces
npm run assets:sync:up
AC Native OS (fedac/native/)#
# Full build pipeline: binary → initramfs → kernel
ac-os build
# Build + flash USB
ac-os flash
# Build + upload OTA release (ALWAYS rebuilds first)
ac-os upload
# Build + flash + upload
ac-os flash+upload
Critical: ac-os upload always does a full rebuild before uploading. The kernel embeds the git hash and build name at compile time (AC_GIT_HASH, AC_BUILD_NAME in the Makefile). Uploading without rebuilding would serve a stale kernel with the wrong version string.
Notation#
- compush - commit & push
Architecture#
Core Components#
-
Boot System (
system/public/aesthetic.computer/boot.mjs)- Entry point that loads the BIOS and initializes the runtime
- Manages service worker registration for module caching
- Handles WebSocket module loader for hot reloading
- Boot telemetry system logs to
/api/boot-log
-
BIOS (
system/public/aesthetic.computer/bios.mjs)- Main runtime coordinator
- Manages piece lifecycle (boot, act, paint, sim, etc.)
- Provides the API surface for pieces
- Handles routing and navigation
-
Disk System (
system/public/aesthetic.computer/lib/disk.mjs)- Large module (~572KB) that provides the core API for pieces
- Graphics primitives, audio, input handling, UI components
- All pieces interact with AC through the Disk API
-
Module Loader (
system/public/aesthetic.computer/module-loader.mjs)- WebSocket-based dynamic module loading
- Enables hot reloading during development
- Prefetches commonly used modules
Pieces#
Pieces are the fundamental unit of content in AC. Each piece is a single .mjs or .lisp file located in system/public/aesthetic.computer/disks/.
Piece Structure (JavaScript)#
Pieces export lifecycle functions that receive an API object:
// boot: runs once when the piece loads
function boot({ wipe, screen, params, colon, api }) {
// Initialize state
}
// paint: runs every frame
function paint({ wipe, ink, line, circle, screen }) {
// Render graphics
}
// act: handles user input and events
function act({ event: e }) {
if (e.is("keyboard:down:space")) {
// Handle spacebar press
}
}
// sim: simulation/game logic that runs every frame
function sim() {
// Update game state
}
export { boot, paint, act, sim };
Common lifecycle functions:
boot- Initialization, runs oncepaint- Rendering, runs every frameact- Event handling (input, network, etc.)sim- Simulation/logic, runs every frameleave- Cleanup when exiting the piecepreview- Static preview image generation
Piece API Surface#
The API is provided through function parameters. Common APIs:
- Graphics:
wipe,ink,line,box,circle,plot,paste, etc. - Text:
write,type,paste,help - Input:
event,pen,hand,gamepad - Audio:
sound,speaker,microphone - UI:
ui.Button,ui.TextInput,cursor - System:
screen,params,colon,store,net,jump,send
Servers and Services#
-
System Server (
system/+lith/)- Production: lith monolith (Express + Caddy on DigitalOcean VPS)
- Dev:
npm run site(port 8888) - Backend functions in
system/netlify/functions/ - Handles piece serving, authentication, storage APIs
-
Session Server (
session-server/)- Per-session backend using Jamsocket for ephemeral deployments
- WebSocket server using Geckos.io for real-time communication
- Manages chat, multiplayer state, and real-time features
- Uses Redis for state synchronization
-
Feed Server (
dp1-feed/)- Cloudflare Worker for activity feeds
- Deployed separately to Cloudflare Workers
KidLisp#
KidLisp is a minimal Lisp dialect for generative art, with 118 built-in functions across 12 categories. See kidlisp/README.md for comprehensive documentation.
Key files:
system/public/aesthetic.computer/lib/kidlisp.mjs- Main evaluatorsystem/netlify/functions/store-kidlisp.mjs- Storage APIkidlisp/tools/- Analysis and debugging tools
Running KidLisp tools:
# Analyze piece structure (requires dev server running)
./kidlisp/tools/source-tree.mjs $cow
# Get source code
./kidlisp/tools/get-source.mjs $piece-code
Data Storage#
- MongoDB: User data, handles, chat messages, moods
- Redis: Session state, real-time coordination
- Firebase: Authentication, cloud messaging
- Digital Ocean Spaces: Asset storage (CDN)
Routing and URLs#
- Pieces are URL-addressable:
https://aesthetic.computer/piece-name - Parameters:
piece-name:param1:param2 - User pieces:
@handle/piece-name - QR code sharing:
share piece-name
Development Workflow#
Local Development in Codespaces#
When running in GitHub Codespaces, the server is accessible at:
https://{CODESPACE_NAME}-8888.app.github.dev
Get your Codespace name: echo $CODESPACE_NAME
Hot Reloading#
The module loader provides hot reloading during development:
- Piece changes are reflected immediately when saved
- WebSocket connection shown in boot canvas
- Use
channel custom-namefor multi-device testing
Publishing#
# In the AC prompt:
publish # Publish current piece
publish piece-name # Publish with custom name
source # Download blank template
source piece-name # Fork existing piece
Important Directories#
system/public/aesthetic.computer/disks/- All pieces (.mjs and .lisp files)system/public/aesthetic.computer/lib/- Shared libraries and utilitiessystem/netlify/functions/- Serverless backend functionssession-server/- Real-time session backendshared/- Code shared between system and session serverskidlisp/- KidLisp language documentation and toolsspec/- Jasmine tests for KidLispants/- AestheticAnts automated maintenance system
Key Patterns#
Event Handling in Pieces#
Events use a string-based pattern matching system:
event.is("keyboard:down:a") // 'a' key pressed
event.is("touch") // Any touch event
event.is("lift") // Touch/click released
event.is("draw") // Drag with pen down
event.is("keyboard:down:arrowup") // Arrow key
State Management#
Pieces maintain state in module-level variables:
let score = 0;
let enemies = [];
function boot() {
// Initialize state
}
function sim() {
// Update state
score += 1;
}
API Requests from Pieces#
Use the net API for HTTP requests:
function boot({ net }) {
net.pieces("@user/list").then((data) => {
// Handle response
});
}
Multiplayer Networking (Dual-Channel Pattern)#
Multiplayer pieces use both WebSocket (reliable) and UDP (low-latency) channels. See squash.mjs for the canonical implementation.
function boot({ net: { socket, udp }, handle }) {
// UDP for high-frequency position sync (low latency, may drop packets)
udpChannel = udp((type, content) => {
if (type === "game:move") {
const d = typeof content === "string" ? JSON.parse(content) : content;
// Update remote player position
}
});
// WebSocket for reliable game events (join/leave, scoring, round control)
server = socket((id, type, content) => {
if (type.startsWith("connected")) { /* joined session */ }
if (type === "left") { /* player disconnected */ }
if (type === "game:join") { /* another player joined */ }
if (type === "game:score") { /* reliable score event */ }
});
// Send reliable event
server.send("game:join", { handle: handle() });
// Send low-latency position update
udpChannel.send("game:move", { x, y, vx, vy });
}
Session server routing:
- UDP handlers: add
channel.on("game:move", ...)in geckos section ofsession-server/session.mjs - WebSocket: position messages use
others()(relay to all except sender), game events useeveryone()(catch-all relay) - Chat invites: typing
'piece-name'in chat creates a clickable link to join
Reference pieces: squash.mjs (2D platformer), 1v1.mjs (3D FPS), udp.mjs (minimal UDP test)
UI Components#
function boot({ ui: { Button, TextInput } }) {
const btn = new Button("Click me", { box: [10, 10, 100, 40] });
}
function act({ event: e }) {
if (btn.trigger(e)) {
// Button was clicked
}
}
Notes#
- The codebase uses
.mjsextension for ES modules - Prefer
constdestructuring for API parameters to minimize imports - Graphics operations are immediate-mode (no retained scene graph)
- All coordinates are in pixels
- Default color depth is 8-bit RGB (0-255 per channel)
- The
wipefunction clears the screen and should be called first inpaint - When making changes, consult
ants/mindset-and-rules.mdfor the ant operating philosophy