Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346#!/usr/bin/env node// ─────────────────────────────────────────────────────────────────────────// tezos/series/generate.mjs//// Generates KidLisp source for three keeps SUB-SERIES that mint into the live// v11 contract (KT1Q1irsjSZ7EfUN4qHzAB2t7xLBPsAWYwBB)://// • feedback — living textures: a seed shape + accumulating pixel feedback// (no per-frame wipe → trails compound forever)// • clock — real-time pieces driven by wall-clock second triggers// (Ns / Ns... fire on the second; the work visibly ticks)// • melody — tiny audiovisual songs: (melody ...) + amplitude-reactive form//// Output → tezos/series/manifest.json (source + a `code` slot the bridge fills// after running each piece through the logged-in Electron webview).//// Syntax here is grounded in real .lisp fixtures (complex-timing, fia-birthday,// justsound, lab, brush) and the melody-parser — not guessed.//// Usage:// node tezos/series/generate.mjs # write manifest.json// node tezos/series/generate.mjs --seed 7 # re-roll palettes (motion stays)// node tezos/series/generate.mjs --print # print sources, don't write// ─────────────────────────────────────────────────────────────────────────
import fs from "fs";import path from "path";import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));const MANIFEST = path.join(__dirname, "manifest.json");
// ── args ──────────────────────────────────────────────────────────────────const argv = process.argv.slice(2);const getArg = (k, d) => { const i = argv.indexOf(`--${k}`); return i >= 0 ? argv[i + 1] : d;};const seed = parseInt(getArg("seed", "1"), 10) || 1;const printOnly = argv.includes("--print");
// ── tiny seeded RNG so palette re-rolls are reproducible ────────────────────function mulberry32(a) { return function () { a |= 0; a = (a + 0x6d2b79f5) | 0; let t = Math.imul(a ^ (a >>> 15), 1 | a); t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; };}const rng = mulberry32(seed * 2654435761);const pick = (arr) => arr[Math.floor(rng() * arr.length)];
// Curated palettes (each a cycling set used inside (Ns... ...)). All names are// CSS colors KidLisp accepts.const PALETTES = [ ["cyan", "teal", "blue", "navy"], ["magenta", "purple", "indigo", "blue"], ["white", "aqua", "skyblue"], ["pink", "red", "orange", "yellow"], ["lime", "green", "white"], ["red", "orange", "yellow", "lime", "green"], ["gold", "orange", "crimson"], ["aquamarine", "turquoise", "mediumblue"], ["violet", "deeppink", "white"], ["springgreen", "lime", "yellow"],];const cyc = (speed, palette) => `(${speed}s... ${palette.join(" ")})`;
// ─────────────────────────────────────────────────────────────────────────// FEEDBACK — accumulating-buffer organisms (no per-frame wipe)// ─────────────────────────────────────────────────────────────────────────function feedbackSeries() { const p = () => pick(PALETTES); const cs = () => pick(["0.1", "0.15", "0.2", "0.3"]);
const items = [ { title: "drift", source: [ "; feedback · drift", "(once (wipe black))", `(ink ${cyc(cs(), p())})`, "(circle (+ width/2 (* 40 (sin))) height/2 4)", "(scroll 2 0)", "(zoom 1.003)", "(blur 1)", ], }, { title: "vortex", source: [ "; feedback · vortex", "(once (wipe black))", `(ink ${cyc(cs(), p())})`, "(box (- width/2 3) (- height/2 3) 6 6)", "(spin 2)", "(zoom 1.02)", "(blur 1)", ], }, { title: "swell", source: [ "; feedback · swell", "(once (wipe navy))", `(ink ${cyc(cs(), p())})`, "(circle width/2 height/2 (+ 6 (* 5 (sin))))", "(zoom 1.012)", "(scroll 0 1)", "(blur 1)", ], }, { title: "bloom", source: [ "; feedback · bloom", "(once (wipe black))", `(ink ${cyc(cs(), p())})`, "(circle (+ width/2 (* 30 (sin))) height/2 5)", "(zoom 1.01)", "(spin 1)", "(blur 2)", ], }, { title: "weave", source: [ "; feedback · weave", "(once (wipe black))", `(ink ${cyc(cs(), p())})`, "(box (+ width/2 (* 50 (sin))) (- height/2 2) 4 4)", "(scroll 1 1)", "(zoom 1.004)", "(blur 1)", ], }, { title: "ember", source: [ "; feedback · ember", "(once (wipe black))", `(ink ${cyc(cs(), p())})`, "(circle width/2 (+ height/2 (* 40 (sin))) 3)", "(scroll 0 -1)", "(zoom 0.997)", "(blur 1)", ], }, ]; return items.map((i) => ({ ...i, source: i.source.join("\n") }));}
// ─────────────────────────────────────────────────────────────────────────// CLOCK — real-time pieces that visibly tick on wall-clock seconds// ─────────────────────────────────────────────────────────────────────────function clockSeries() { const p = () => pick(PALETTES);
const items = [ { title: "tick", source: [ "; clock · tick", "(wipe black)", `(ink ${cyc("1", p())})`, "(circle width/2 height/2 (+ 10 (* 8 (sin))))", "(ink white)", "(1s (box 0 (- height 10) width 10))", ], }, { title: "metronome", source: [ "; clock · metronome", "(0.5s (wipe black))", `(ink ${cyc("1", p())})`, "(box (1s... 8 (- width 24)) (- height/2 8) 16 16)", ], }, { title: "pulse", source: [ "; clock · pulse", "(wipe (1s... black navy))", `(ink ${cyc("0.5", p())})`, "(circle width/2 height/2 (+ 6 (* 20 (sin))))", ], }, { title: "grow", source: [ "; clock · grow", "(8s (wipe black))", `(ink ${cyc("0.5", p())})`, "(circle width/2 height/2 (* 6 (mod frame 80)))", "(blur 1)", ], }, { title: "pendulum", source: [ "; clock · pendulum", "(wipe black)", `(ink ${cyc("1", p())})`, "(line width/2 0 (+ width/2 (* 60 (sin))) height)", "(ink white)", "(0.5s (circle (+ width/2 (* 60 (sin))) (- height 8) 5))", ], }, ]; return items.map((i) => ({ ...i, source: i.source.join("\n") }));}
// ─────────────────────────────────────────────────────────────────────────// MELODY — tiny audiovisual songs ((melody ...) + amplitude-reactive form)// ─────────────────────────────────────────────────────────────────────────function melodySeries() { const p = () => pick(PALETTES);
const items = [ { title: "ascend", source: [ "; melody · ascend", "(wipe black)", '(melody "4cdefgab5c" 132)', `(ink ${cyc("0.1", p())})`, "(circle width/2 height/2 (+ 8 (* 50 amplitude)))", "(blur 1)", ], }, { title: "waltz", source: [ "; melody · waltz", "(wipe (0.5s... midnightblue black))", '(melody "5ceg5ceg 4c.." 120 "3/4" "waltz")', `(ink ${cyc("0.2", p())})`, "(circle width/2 height/2 (+ 6 (* 45 amplitude)))", ], }, { title: "penta", source: [ "; melody · penta", "(wipe black)", '(melody "5cdega6c" 144 "4/4" "swing")', `(ink ${cyc("0.15", p())})`, "(circle width/2 height/2 (+ 6 (* 45 amplitude)))", "(spin 1)", ], }, { title: "duet", source: [ "; melody · duet", "(wipe black)", '(melody "5ceg 3c4g" 120)', `(ink ${cyc("0.1", p())})`, "(circle (- width/2 30) height/2 (+ 6 (* 40 amplitude)))", "(circle (+ width/2 30) height/2 (+ 6 (* 40 amplitude)))", ], }, { title: "bells", source: [ "; melody · bells", "(wipe midnightblue)", '(melody "{sine}6cegb6ce" 160)', `(ink ${cyc("0.1", p())})`, "(circle width/2 (+ height/2 (* 30 (sin))) (+ 4 (* 30 amplitude)))", ], }, { title: "drone", source: [ "; melody · drone", "(wipe black)", '(melody "{sine}3c,,,3g,,," 60)', `(ink ${cyc("0.3", p())})`, "(box (- width/2 20) (- height/2 20) (+ 8 (* 60 amplitude)) (+ 8 (* 60 amplitude)))", "(blur 2)", ], }, ]; return items.map((i) => ({ ...i, source: i.source.join("\n") }));}
// ── assemble ────────────────────────────────────────────────────────────────const series = { feedback: feedbackSeries(), clock: clockSeries(), melody: melodySeries(),};
// Preserve any codes already captured by the bridge across re-generation,// keyed by source text (so re-rolling palettes doesn't wipe minted codes).let prevBySource = new Map();if (fs.existsSync(MANIFEST)) { try { const prev = JSON.parse(fs.readFileSync(MANIFEST, "utf8")); for (const list of Object.values(prev.series || {})) { for (const it of list) if (it.code) prevBySource.set(it.source, it.code); } } catch {}}
let total = 0;for (const list of Object.values(series)) { for (const it of list) { it.code = prevBySource.get(it.source) || null; total++; }}
const manifest = { contract: "KT1Q1irsjSZ7EfUN4qHzAB2t7xLBPsAWYwBB", network: "mainnet", seed, generatedAt: null, // stamped by the caller / bridge; kept null for determinism note: "Sub-series tagged by `series`. Run tezos/series/bridge.mjs to mint-cache as @jeffrey.", series,};
if (printOnly) { for (const [name, list] of Object.entries(series)) { console.log(`\n══════════ ${name} (${list.length}) ══════════`); for (const it of list) { console.log(`\n── ${name}/${it.title} ──`); console.log(it.source); } }} else { fs.writeFileSync(MANIFEST, JSON.stringify(manifest, null, 2)); console.log(`✅ Wrote ${MANIFEST}`); console.log( ` ${total} pieces — feedback:${series.feedback.length} clock:${series.clock.length} melody:${series.melody.length} (seed ${seed})`, ); const carried = [...prevBySource.values()].length; if (carried) console.log(` carried ${carried} already-minted $code(s) forward`); console.log(`\nNext: launch the Electron app (production, logged in as @jeffrey), then:`); console.log(` node tezos/series/bridge.mjs # run + cache all pieces`);}