Monorepo for Aesthetic.Computer
aesthetic.computer
1#!/usr/bin/env node
2// KidLisp WASM Runner — Verifiable Visual Compute
3//
4// The WASM module contains everything: renderer + pixel buffer + piece code.
5// This host only provides memory and reads pixels out. Nothing to fake.
6
7import { readFileSync, writeFileSync } from "fs";
8import { Compiler } from "./compiler.mjs";
9
10const WIDTH = parseInt(process.argv[3]) || 128;
11const HEIGHT = parseInt(process.argv[4]) || 128;
12
13const input = process.argv[2] || "hello.lisp";
14const source = readFileSync(new URL(input, import.meta.url).pathname, "utf-8");
15
16console.log(`Compiling ${input}...`);
17const compiler = new Compiler();
18const wasmBytes = compiler.compile(source);
19console.log(`WASM binary: ${wasmBytes.length} bytes (self-contained renderer)`);
20
21// Instantiate — math imports only, rendering is self-contained.
22const { instance } = await WebAssembly.instantiate(wasmBytes, {
23 math: {
24 sin: (x) => Math.fround(Math.sin(x)),
25 cos: (x) => Math.fround(Math.cos(x)),
26 random: () => Math.fround(Math.random()),
27 },
28});
29
30console.log(`Running paint(${WIDTH}, ${HEIGHT}, 0)...`);
31instance.exports.paint(WIDTH, HEIGHT, 0);
32
33// Read pixels directly from WASM linear memory
34const mem = new Uint8Array(instance.exports.memory.buffer);
35
36// ─── Output PPM ─────────────────────────────────────────────────────
37
38const header = `P6\n${WIDTH} ${HEIGHT}\n255\n`;
39const ppm = Buffer.alloc(header.length + WIDTH * HEIGHT * 3);
40ppm.write(header);
41let offset = header.length;
42for (let i = 0; i < WIDTH * HEIGHT * 4; i += 4) {
43 ppm[offset++] = mem[i];
44 ppm[offset++] = mem[i + 1];
45 ppm[offset++] = mem[i + 2];
46}
47
48const outFile = input.replace(/\.lisp$/, ".ppm");
49writeFileSync(new URL(outFile, import.meta.url).pathname, ppm.slice(0, offset));
50console.log(`Wrote ${outFile} (${WIDTH}x${HEIGHT})`);
51
52// ─── Terminal Preview ───────────────────────────────────────────────
53
54const PW = Math.min(WIDTH, 64);
55const sx = WIDTH / PW;
56const sy = (HEIGHT / PW) * 2;
57
58console.log(`\nPreview (${PW} cols):`);
59for (let row = 0; row < PW; row++) {
60 let line = "";
61 for (let col = 0; col < PW; col++) {
62 const tx = Math.floor(col * sx);
63 const ty = Math.floor(row * sy);
64 const by = Math.min(Math.floor(row * sy + sy / 2), HEIGHT - 1);
65 const ti = (ty * WIDTH + tx) * 4;
66 const bi = (by * WIDTH + tx) * 4;
67 line += `\x1b[38;2;${mem[ti]};${mem[ti+1]};${mem[ti+2]};48;2;${mem[bi]};${mem[bi+1]};${mem[bi+2]}m\u2580`;
68 }
69 process.stdout.write(line + "\x1b[0m\n");
70}
71
72// ─── Verify ─────────────────────────────────────────────────────────
73
74// Hash the pixel buffer for verifiability
75const { createHash } = await import("crypto");
76const pixelData = mem.slice(0, WIDTH * HEIGHT * 4);
77const hash = createHash("sha256").update(pixelData).digest("hex").slice(0, 16);
78console.log(`\nPixel hash: ${hash}`);
79console.log(`Module size: ${wasmBytes.length} bytes | Buffer: ${WIDTH}x${HEIGHT} RGBA`);