Metabolism: song as substrate, ecology grown from the track
A new top-level section building the metabolism concept: the track supplies the food field, event impulses and per-section laws; the seed picks the species (a rule set, not a parameter vector). Same song + seed => bit-identical frames; different songs => different worlds. - analysis: single FFT pass -> per-frame feature table; score (onsets folded into world geometry via voice ring + pitch-class sector), wells (food placed by the harmony), graph (band correlations -> interaction matrix), laws (per-section regimes) - sim: typed-array world, 3 castes (builder/orbit, forager/commute, spark/orbit-wide) with causality rings, persistence trails, consequence field; fully deterministic - lens: software framebuffer renderer (PNG in node, ImageData in browser) + event-reactive camera (cuts on activity change, alternating shot sizes, drift-and-push) - metrics: coupling x residual fitness (not per-frame descriptors) plus a grown-structure fingerprint; report + strip tools; 15 tests green - DESIGN.md captures the concept, invariants and build order 3 reference test signals (drone/runner/dusk) grow measurably distinct worlds while seeds of one song stay in the same biome.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
// Minimal PNG writer (8-bit RGBA, no interlace). Node only: the browser demo
|
||||
// blits the same framebuffer through ImageData and never touches this file.
|
||||
|
||||
import zlib from 'node:zlib';
|
||||
|
||||
const CRC_TABLE = (() => {
|
||||
const t = new Uint32Array(256);
|
||||
for (let n = 0; n < 256; n++) {
|
||||
let c = n;
|
||||
for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
||||
t[n] = c >>> 0;
|
||||
}
|
||||
return t;
|
||||
})();
|
||||
|
||||
function crc32(buf) {
|
||||
let c = 0xffffffff;
|
||||
for (let i = 0; i < buf.length; i++) c = CRC_TABLE[(c ^ buf[i]) & 0xff] ^ (c >>> 8);
|
||||
return (c ^ 0xffffffff) >>> 0;
|
||||
}
|
||||
|
||||
function chunk(type, data) {
|
||||
const len = Buffer.alloc(4);
|
||||
len.writeUInt32BE(data.length, 0);
|
||||
const body = Buffer.concat([Buffer.from(type, 'ascii'), data]);
|
||||
const crc = Buffer.alloc(4);
|
||||
crc.writeUInt32BE(crc32(body), 0);
|
||||
return Buffer.concat([len, body, crc]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} width
|
||||
* @param {number} height
|
||||
* @param {Uint8ClampedArray|Uint8Array} rgba
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
export function encodePNG(width, height, rgba) {
|
||||
const stride = width * 4;
|
||||
const raw = Buffer.alloc((stride + 1) * height);
|
||||
for (let y = 0; y < height; y++) {
|
||||
raw[y * (stride + 1)] = 0;
|
||||
raw.set(rgba.subarray(y * stride, (y + 1) * stride), y * (stride + 1) + 1);
|
||||
}
|
||||
const ihdr = Buffer.alloc(13);
|
||||
ihdr.writeUInt32BE(width, 0);
|
||||
ihdr.writeUInt32BE(height, 4);
|
||||
ihdr[8] = 8;
|
||||
ihdr[9] = 6;
|
||||
ihdr[10] = 0;
|
||||
ihdr[11] = 0;
|
||||
ihdr[12] = 0;
|
||||
return Buffer.concat([
|
||||
Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
|
||||
chunk('IHDR', ihdr),
|
||||
chunk('IDAT', zlib.deflateSync(raw, { level: 6 })),
|
||||
chunk('IEND', Buffer.alloc(0)),
|
||||
]);
|
||||
}
|
||||
|
||||
/** Tile several bitmaps into one grid. Bitmaps are {w, h, data}. */
|
||||
export function tile(bitmaps, cols, rows, gap = 0) {
|
||||
if (!bitmaps.length) throw new Error('tile: nothing to tile');
|
||||
const w = bitmaps[0].w;
|
||||
const h = bitmaps[0].h;
|
||||
const W = cols * w + (cols - 1) * gap;
|
||||
const H = rows * h + (rows - 1) * gap;
|
||||
const out = new Uint8ClampedArray(W * H * 4);
|
||||
for (let i = 0; i < out.length; i += 4) {
|
||||
out[i] = 8;
|
||||
out[i + 1] = 8;
|
||||
out[i + 2] = 10;
|
||||
out[i + 3] = 255;
|
||||
}
|
||||
for (let i = 0; i < bitmaps.length; i++) {
|
||||
const b = bitmaps[i];
|
||||
const cx = i % cols;
|
||||
const cy = (i / cols) | 0;
|
||||
const ox = cx * (w + gap);
|
||||
const oy = cy * (h + gap);
|
||||
for (let y = 0; y < h; y++) {
|
||||
const src = y * b.w * 4;
|
||||
const dst = ((oy + y) * W + ox) * 4;
|
||||
out.set(b.data.subarray(src, src + b.w * 4), dst);
|
||||
}
|
||||
}
|
||||
return { w: W, h: H, data: out };
|
||||
}
|
||||
Reference in New Issue
Block a user