Files
bubblechambersimart/tools/liberated-templates.mjs
2026-06-10 18:23:47 +01:00

64 lines
4.9 KiB
JavaScript

/* ============================================================
liberated-templates.mjs — the eight deconstructed compositions,
each piped through ONE signature liberation (the move that most
amplifies it): luminous waves, open/dark grounds, directional or
off-centre light, off-canvas bleed, and the high-Q resonance axis.
Usage: node tools/liberated-templates.mjs [size]
============================================================ */
import { writeFileSync, mkdirSync } from 'node:fs';
import { TEMPLATES, buildTemplate } from '../src/compose/templates.js';
import { renderComposition } from '../src/compose/composition.js';
const SIZE = +(process.argv[2] || 1300);
const OUT = 'output/templates-liberated';
mkdirSync(OUT, { recursive: true });
const merge = (a, b) => { const o = { ...a }; for (const k in b) o[k] = (b[k] && typeof b[k] === 'object' && !Array.isArray(b[k])) ? merge(a[k] || {}, b[k]) : b[k]; return o; };
// one signature liberation per template id
const LIB = {
'great-wave': { note: 'luminous foam · waves lighter than a dusk ground',
over: { fieldSea: { color: { light: 0.84 } }, background: { color: 'rgb(120,120,142)', glow: { strength: 0.3 }, vignette: { strength: 0.42 } } } },
'monk-by-sea': { note: 'a lone light over a dark sea-void',
over: { background: { color: 'rgb(26,28,38)', glow: { strength: 0.4, followSun: true }, vignette: { strength: 0.25 }, film: { opacity: 0.3 }, grain: { opacity: 0.3 } }, fieldSea: { color: { light: 0.72, sat: 0.3 } }, disk: { hue: 0.58, sat: 0.42, pressure: 0.2 } } },
'rothko': { note: 'raking directional light across the fields',
over: { background: { vignette: { mode: 'linear', angle: 115, strength: 0.55, start: 0.3 } } } },
'turner': { note: 'light leaking in from a corner',
over: { background: { vignette: { mode: 'linear', angle: 210, strength: 0.45, start: 0.4 }, glow: { strength: 1.0, followSun: true } } } },
'ma': { note: 'the event bled off-canvas · asymmetric light',
over: { bubble: { transform: { x: 1.18, y: 0.9, scale: 1.1 } }, background: { vignette: { mode: 'radial', cx: 0.7, cy: 0.35, radius: 0.7, strength: 0.55 } }, fiduciaries: { target: [0.85, 0.7], from: [0.2, -0.1] } } },
'suprematist': { note: 'a luminous form on a dark void',
over: { background: { color: 'rgb(24,22,30)', glow: { strength: 0.45, followSun: true }, vignette: { strength: 0.2 } }, disk: { hue: 0.04, sat: 0.9, pressure: 0.4, intensity: 0.95 }, bubble: { palette: 'kindrise' } } },
'golden-thirds': { note: 'asymmetric light pulled toward the φ sun',
over: { background: { vignette: { mode: 'radial', cx: 0.32, cy: 0.34, radius: 0.66, strength: 0.5 } }, fieldSea: { color: { light: 0.5 } } } },
'wright': { note: 'a Cherokee-red high-Q resonance threading the grid',
over: { fieldGrid: { resonance: { amp: 0.5, q: 16, axis: 'ties', hue: 0.03, sat: 0.85, light: 0.5 } } } },
};
let n = 0; const items = [];
for (const t of TEMPLATES) {
const lib = LIB[t.id] || { note: '', over: {} };
for (let i = 0; i < t.reps; i++) {
const comp = merge(buildTemplate(t, i), lib.over);
const name = `${String(++n).padStart(2, '0')}_${t.id}_v${i + 1}`;
writeFileSync(`${OUT}/${name}.svg`, renderComposition(comp, SIZE));
writeFileSync(`${OUT}/${name}.mjs`, `/* ${t.name} (${t.source}) · liberated: ${lib.note} */\nexport const composition = ${JSON.stringify(comp, null, 2)};\n`);
items.push({ name, t, lib });
console.log(` ${name} ${t.name}${lib.note}`);
}
}
console.log(`liberated templates — ${n}${OUT}/`);
const cols = 5;
writeFileSync(`${OUT}/matrix.html`, `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Traces of the Invisible — through the liberated lens</title>
<style>html,body{margin:0;background:#17150f;color:#cabfa6}body{padding:60px 54px 72px;font:12px/1.5 ui-monospace,Menlo,monospace}
.head{letter-spacing:.34em;text-transform:uppercase;font-size:14px;color:#9fb7af;margin:0 0 4px}.sub{color:#6f6a5c;font-size:12px;margin:0 0 40px;letter-spacing:.06em}
.wall{display:grid;grid-template-columns:repeat(${cols},1fr);gap:46px 38px}figure{margin:0}
.mat{background:#f3ecdb;padding:14px;box-shadow:0 2px 0 #0008,0 20px 44px -20px #000c;border:1px solid #000}.mat img{display:block;width:100%;aspect-ratio:1/1;object-fit:cover;background:#fff}
figcaption{margin-top:10px;text-align:center}.ttl{color:#cabfa6}.note{color:#6f6a5c;font-style:italic;display:block;font-size:11px}</style></head><body>
<p class="head">Traces of the Invisible — through the liberated lens</p>
<p class="sub">the eight grammars, each severed from one expectation · ${n} plates</p>
<div class="wall">
${items.map((it, k) => `<figure><div class="mat"><img src="${it.name}.svg"></div><figcaption><span class="ttl">${String(k + 1).padStart(2, '0')} · ${it.t.name}</span><span class="note">${it.lib.note}</span></figcaption></figure>`).join('\n')}
</div></body></html>`);
console.log(`-> ${OUT}/matrix.html`);