Monorepo for Aesthetic.Computer
aesthetic.computer
1import { readFileSync } from "fs";
2import { basename } from "path";
3import { Compiler } from "./compiler.mjs";
4
5export function hashString(text) {
6 let hash = 0x811c9dc5;
7 for (let i = 0; i < text.length; i += 1) {
8 hash ^= text.charCodeAt(i);
9 hash = Math.imul(hash, 0x01000193);
10 }
11 return hash >>> 0;
12}
13
14export function createSeededRandom(seed) {
15 let state = (seed >>> 0) || 1;
16 return () => {
17 state ^= state << 13;
18 state ^= state >>> 17;
19 state ^= state << 5;
20 return (state >>> 0) / 0x100000000;
21 };
22}
23
24export function createMathImports(random = Math.random) {
25 return {
26 math: {
27 sin: (x) => Math.fround(Math.sin(x)),
28 cos: (x) => Math.fround(Math.cos(x)),
29 random: () => Math.fround(random()),
30 },
31 };
32}
33
34export function loadPiece(input) {
35 const path = new URL(input, import.meta.url).pathname;
36 const source = readFileSync(path, "utf-8");
37 return {
38 input,
39 path,
40 source,
41 name: basename(input, ".lisp"),
42 };
43}
44
45export async function instantiatePiece(source, options = {}) {
46 const compiler = new Compiler();
47 const wasmBytes = compiler.compile(source);
48 const seed = options.seed ?? hashString(source);
49 const random = options.random ?? createSeededRandom(seed);
50 const { instance } = await WebAssembly.instantiate(wasmBytes, createMathImports(random));
51 return { instance, wasmBytes, seed };
52}
53
54export function renderFrame(instance, width, height, frame) {
55 instance.exports.paint(width, height, frame);
56 const byteLength = width * height * 4;
57 return new Uint8Array(instance.exports.memory.buffer, 0, byteLength).slice();
58}