Added QFT

This commit is contained in:
2026-05-29 15:40:42 -04:00
parent e59b024b62
commit 56c59a1f9c
148 changed files with 428741 additions and 7 deletions

View File

@@ -0,0 +1,373 @@
/* ============================================================
qft-variations-craft.mjs — sketch 06.
Each variation isolates ONE craft idea so the effect is
readable on its own. Five blocks:
A · Cubic origin plays — push origin off-page or into the
viewer's space along the (isometric) Z axis
B · Multi-rosettes — offset origins, varied scales
C · Lighter-on-midtone — paper at MID tone so a brighter
rosette can actually read as lighter-than-ground
D · Plain white on light — bold white strokes on warm paper
E · Schlegel plays — weight / origin / shifted axes
(outerR · innerR · rot3D)
============================================================ */
import { writeFileSync, mkdirSync } from 'node:fs';
import { generateQFTScene } from '../src/qft/scene.js';
import { paramsFromSeed } from '../src/qft/params.js';
import { renderQFTSVG } from '../src/qft/renderer.js';
const BASE_SEED = 'FEYNMAN-7167';
const OUT_DIR = 'output/qft/sketch06';
const SIZE = 1800;
mkdirSync(OUT_DIR, { recursive: true });
const F = (hueStart, hueEnd, saturation, lightness, opacity) =>
({ hueStart, hueEnd, saturation, lightness, opacity });
const paper = (flat, glowInDelta = [18, 16, 14], glowOutDelta = [-25, -22, -20]) => ({
flat,
glowIn: [flat[0] + glowInDelta[0], flat[1] + glowInDelta[1], flat[2] + glowInDelta[2]],
glowOut: [flat[0] + glowOutDelta[0], flat[1] + glowOutDelta[1], flat[2] + glowOutDelta[2]],
});
const WHITE = (op = 0.95) => F(0, 0, 0, 0.97, op);
const variations = [
// ─────────────────────── BLOCK A · cubic origin plays ───────────────────────
{
name: 'A1_cubic-origin-far-right',
label: 'A1 · cubic origin pushed off-page right — orthogonal mesh streams in from the right edge',
overrides: {
paperOverride: paper([225, 215, 200]),
vignOverride: [70, 60, 45], featureOverride: [60, 45, 30],
stroke: 1.8, gradientMode: 'linear-x',
cubicScale: 1.4, cubicOriginX: 1.45, cubicOriginY: 0, // mostly off-screen to the right
schlegelScale: 0.65,
distStrength: 0, // disable distortion when origin is offset
e8Origins: [{ x: -0.45, y: 0, scale: 0.42 }], // counterweight rosette left
linkCount: 4,
fields: {
cubic: F(0.55, 0.08, 0.65, 0.35, 0.78),
schlegel: F(0.10, 0.08, 0.45, 0.40, 0.55),
e8: F(0.10, 0.04, 0.65, 0.45, 0.88),
links: F(0.05, 0.10, 0.70, 0.40, 0.55),
},
},
},
{
name: 'A2_cubic-zoom-toward-viewer',
label: 'A2 · cubic zoomed in (Z toward viewer) — we are INSIDE the lattice',
overrides: {
paperOverride: paper([225, 215, 198]),
vignOverride: [60, 50, 35], featureOverride: [70, 55, 30],
stroke: 2.4,
cubicScale: 3.2, // huge — fills well past the frame
schlegelScale: 0,
distStrength: 0,
e8Origins: [{ x: 0.55, y: 0.55, scale: 0.32 }], // single small rosette one corner
linkCount: 3,
fields: {
cubic: F(0.04, 0.13, 0.65, 0.32, 0.80),
schlegel: F(0, 0, 0, 0.5, 0),
e8: F(0.05, 0.12, 0.65, 0.45, 0.88),
links: F(0.06, 0.12, 0.65, 0.40, 0.55),
},
},
},
{
name: 'A3_cubic-origin-upper-left',
label: 'A3 · cubic origin pushed upper-left, rest of canvas opens to lower-right',
overrides: {
paperOverride: paper([232, 222, 205]),
vignOverride: [70, 55, 40], featureOverride: [60, 50, 35],
stroke: 1.6, gradientMode: 'radial',
cubicScale: 1.6, cubicOriginX: -0.85, cubicOriginY: -0.85,
schlegelScale: 0.55, schlegelOriginX: -0.30, schlegelOriginY: -0.30,
distStrength: 0,
e8Origins: [{ x: 0.40, y: 0.40, scale: 0.45 }], // rosette in opposing corner
linkCount: 4,
fields: {
cubic: F(0.55, 0.05, 0.65, 0.36, 0.80),
schlegel: F(0.58, 0.10, 0.45, 0.40, 0.60),
e8: F(0.06, 0.12, 0.70, 0.45, 0.88),
links: F(0.05, 0.10, 0.65, 0.40, 0.55),
},
},
},
// ─────────────────────── BLOCK B · multi-rosettes ────────────────────────────
{
name: 'B1_three-rosettes-horizontal',
label: 'B1 · three rosettes along a horizontal axis',
overrides: {
paperOverride: paper([232, 222, 205]),
vignOverride: [60, 50, 35], featureOverride: [55, 45, 30],
stroke: 1.8,
cubicScale: 1.3, schlegelScale: 0,
e8Origins: [
{ x: -0.55, y: 0, scale: 0.32 },
{ x: 0.00, y: 0, scale: 0.42 },
{ x: 0.55, y: 0, scale: 0.32 },
],
linkCount: 6,
fields: {
cubic: F(0.50, 0.10, 0.45, 0.40, 0.35),
schlegel: F(0, 0, 0, 0.5, 0),
e8: F(0.55, 0.05, 0.55, 0.48, 0.85),
links: F(0.06, 0.12, 0.65, 0.42, 0.55),
},
},
},
{
name: 'B2_triangular-rosette-cluster',
label: 'B2 · three rosettes in a triangle · varied sizes',
overrides: {
paperOverride: paper([222, 210, 220]),
vignOverride: [55, 40, 60], featureOverride: [65, 45, 75],
stroke: 1.6,
cubicScale: 1.4, schlegelScale: 0.65,
e8Origins: [
{ x: 0.00, y: -0.45, scale: 0.50 },
{ x: -0.45, y: 0.30, scale: 0.32 },
{ x: 0.45, y: 0.30, scale: 0.32 },
],
linkCount: 6,
fields: {
cubic: F(0.78, 0.92, 0.50, 0.38, 0.40),
schlegel: F(0.82, 0.92, 0.40, 0.40, 0.50),
e8: F(0.82, 0.95, 0.55, 0.46, 0.85),
links: F(0.85, 0.96, 0.65, 0.42, 0.55),
},
},
},
{
name: 'B3_five-rosettes-quincunx',
label: 'B3 · quincunx: 4 corners + centre · five rosettes total',
overrides: {
paperOverride: paper([232, 222, 205]),
vignOverride: [60, 50, 35], featureOverride: [55, 45, 30],
stroke: 1.6,
cubicScale: 1.3, schlegelScale: 0,
e8Origins: [
{ x: -0.50, y: -0.50, scale: 0.26 },
{ x: 0.50, y: -0.50, scale: 0.26 },
{ x: 0.00, y: 0.00, scale: 0.42 },
{ x: -0.50, y: 0.50, scale: 0.26 },
{ x: 0.50, y: 0.50, scale: 0.26 },
],
linkCount: 8,
fields: {
cubic: F(0.10, 0.16, 0.40, 0.40, 0.35),
schlegel: F(0, 0, 0, 0.5, 0),
e8: F(0.10, 0.16, 0.60, 0.45, 0.85),
links: F(0.06, 0.12, 0.65, 0.42, 0.55),
},
},
},
{
name: 'B4_central-large-plus-orbit',
label: 'B4 · one large central rosette + smaller orbiting satellites',
overrides: {
paperOverride: paper([225, 215, 198]),
vignOverride: [60, 50, 35], featureOverride: [55, 45, 30],
stroke: 1.6, gradientMode: 'radial',
cubicScale: 1.4, schlegelScale: 0,
e8Origins: [
{ x: 0.00, y: 0.00, scale: 0.70 },
{ x: 0.55, y: -0.55, scale: 0.18 },
{ x: -0.55, y: -0.55, scale: 0.18 },
{ x: 0.00, y: 0.62, scale: 0.18 },
],
linkCount: 6,
fields: {
cubic: F(0.55, 0.05, 0.55, 0.38, 0.35),
schlegel: F(0, 0, 0, 0.5, 0),
e8: F(0.55, 0.05, 0.55, 0.46, 0.85),
links: F(0.06, 0.12, 0.70, 0.42, 0.55),
},
},
},
// ────────────────── BLOCK C · lighter-on-midtone ─────────────────────────────
{
name: 'C1_lighter-on-deep-cream',
label: 'C1 · lighter rosette on DEEP-cream midtone',
overrides: {
paperOverride: paper([180, 165, 140]), // mid-tone deep cream
vignOverride: [90, 70, 45], featureOverride: [240, 230, 215],
stroke: 2.4,
cubicScale: 1.45, schlegelScale: 0.75,
e8Origins: [{ x: 0, y: 0, scale: 0.78 }],
fields: {
cubic: F(0.06, 0.13, 0.30, 0.38, 0.55),
schlegel: F(0.08, 0.10, 0.30, 0.38, 0.50),
e8: F(0.08, 0.13, 0.20, 0.88, 0.80), // LIGHTER than the midtone paper
links: F(0.06, 0.12, 0.40, 0.92, 0.85),
},
},
},
{
name: 'C2_lighter-on-warm-grey',
label: 'C2 · lighter rosette on warm-grey midtone',
overrides: {
paperOverride: paper([155, 150, 140]), // warm grey midtone
vignOverride: [80, 70, 55], featureOverride: [235, 230, 220],
stroke: 2.6,
cubicScale: 1.55, schlegelScale: 0,
e8Origins: [{ x: -0.20, y: 0, scale: 0.55 },
{ x: 0.20, y: 0, scale: 0.55 }],
fields: {
cubic: F(0.06, 0.12, 0.20, 0.40, 0.55),
schlegel: F(0, 0, 0, 0.5, 0),
e8: F(0.06, 0.12, 0.18, 0.90, 0.80), // pale ivory on grey
links: F(0.06, 0.12, 0.30, 0.94, 0.85),
},
},
},
{
name: 'C3_lighter-on-sage',
label: 'C3 · lighter rosette on sage-grey midtone',
overrides: {
paperOverride: paper([155, 165, 145]), // sage midtone
vignOverride: [55, 70, 50], featureOverride: [225, 235, 215],
stroke: 2.4,
cubicScale: 1.4, schlegelScale: 0.75,
e8Origins: [{ x: 0, y: 0, scale: 0.82 }],
fields: {
cubic: F(0.40, 0.48, 0.30, 0.42, 0.55),
schlegel: F(0.42, 0.48, 0.25, 0.40, 0.50),
e8: F(0.40, 0.48, 0.22, 0.90, 0.80),
links: F(0.40, 0.50, 0.35, 0.94, 0.85),
},
},
},
// ────────────────── BLOCK D · plain white on light ───────────────────────────
{
name: 'D1_thick-white-on-cream',
label: 'D1 · bold WHITE strokes on warm cream — embossed',
overrides: {
paperOverride: paper([222, 210, 188]),
vignOverride: [70, 55, 35], featureOverride: [60, 50, 35],
stroke: 3.4, // thick
cubicScale: 1.45, schlegelScale: 1.20,
e8Origins: [{ x: 0, y: 0, scale: 0.72 }],
fields: {
cubic: WHITE(0.85),
schlegel: WHITE(0.65),
e8: WHITE(0.92),
links: WHITE(0.78),
},
},
},
{
name: 'D2_white-on-pale-rose',
label: 'D2 · plain white rosettes on pale rose',
overrides: {
paperOverride: paper([232, 210, 210]),
vignOverride: [80, 55, 55], featureOverride: [90, 55, 55],
stroke: 3.2,
cubicScale: 1.35, schlegelScale: 0,
e8Origins: [{ x: 0, y: 0, scale: 0.95 },
{ x: 0, y: 0, scale: 0.60 },
{ x: 0, y: 0, scale: 0.30 }],
linkCount: 0,
fields: {
cubic: WHITE(0.40), // very subtle cubic atmosphere
schlegel: F(0, 0, 0, 0.5, 0),
e8: WHITE(0.95), // strong solid white rosettes
links: WHITE(0.5),
},
},
},
{
name: 'D3_white-on-pale-blue',
label: 'D3 · white schlegel + white rosette on pale blue',
overrides: {
paperOverride: paper([190, 215, 230]),
vignOverride: [40, 55, 70], featureOverride: [30, 50, 75],
stroke: 3.0,
cubicScale: 1.0, schlegelScale: 1.50, // schlegel prominent
e8Origins: [{ x: 0, y: 0, scale: 0.65 }],
fields: {
cubic: WHITE(0.45),
schlegel: WHITE(0.85), // bold white tesseract
e8: WHITE(0.92),
links: WHITE(0.55),
},
},
},
// ────────────────── BLOCK E · schlegel plays ─────────────────────────────────
{
name: 'E1_schlegel-thin-nested',
label: 'E1 · Schlegel: deep nesting (tiny inner cube) · thin lines',
overrides: {
paperOverride: paper([232, 222, 200]),
vignOverride: [60, 50, 40], featureOverride: [60, 50, 35],
stroke: 1.4,
cubicScale: 0.55, schlegelScale: 1.40,
schlegelInnerR: 0.08, // very small inner cube → strong perspective
schlegelRot3D: 0.20,
e8Origins: [{ x: 0, y: 0, scale: 0.42 }],
fields: {
cubic: F(0.58, 0.62, 0.30, 0.45, 0.40),
schlegel: F(0.60, 0.68, 0.50, 0.32, 0.78), // schlegel is the subject
e8: F(0.55, 0.65, 0.50, 0.45, 0.78),
links: F(0.58, 0.66, 0.65, 0.42, 0.55),
},
},
},
{
name: 'E2_schlegel-shifted-axis-thick',
label: 'E2 · Schlegel with strongly rotated 4D axis · thick lines',
overrides: {
paperOverride: paper([222, 212, 195]),
vignOverride: [70, 55, 40], featureOverride: [60, 45, 30],
stroke: 3.0, // thick
cubicScale: 0.65, schlegelScale: 1.55,
schlegelInnerR: 0.42, // larger inner cube — less perspective, more parallel
schlegelRot3D: 1.10, // strong rotation — looking at the tesseract from different angle
schlegelOriginX: 0.15,
e8Origins: [{ x: -0.45, y: -0.45, scale: 0.32 }],
fields: {
cubic: F(0.06, 0.12, 0.35, 0.40, 0.40),
schlegel: F(0.06, 0.13, 0.55, 0.32, 0.85),
e8: F(0.08, 0.16, 0.65, 0.45, 0.85),
links: F(0.06, 0.12, 0.65, 0.40, 0.55),
},
},
},
{
name: 'E3_dual-schlegels-rotated',
label: 'E3 · BIG schlegel offset · faint thin counter-rotated cubic',
overrides: {
paperOverride: paper([200, 200, 215]), // pale lavender-grey midtone
vignOverride: [60, 55, 75], featureOverride: [40, 40, 60],
stroke: 2.6,
cubicScale: 1.6, cubicRot: 0.80, // cubic strongly rotated
schlegelScale: 1.85, // schlegel very large bleeding
schlegelInnerR: 0.55, // inner cube larger
schlegelRot3D: -0.30, // negative rotation — opposite angle
schlegelOriginX: -0.10, schlegelOriginY: 0.10,
e8Origins: [{ x: 0.40, y: -0.40, scale: 0.32 },
{ x: -0.40, y: 0.40, scale: 0.32 }],
fields: {
cubic: F(0.62, 0.70, 0.30, 0.55, 0.35),
schlegel: F(0.62, 0.72, 0.55, 0.32, 0.78),
e8: F(0.65, 0.75, 0.60, 0.45, 0.85),
links: F(0.62, 0.78, 0.70, 0.42, 0.55),
},
},
},
];
const base = paramsFromSeed(BASE_SEED);
for (const v of variations) {
const params = { ...base, ...v.overrides };
if (v.overrides.fields) params.fields = v.overrides.fields;
const svg = renderQFTSVG(generateQFTScene(params), params, SIZE);
const path = `${OUT_DIR}/${v.name}.svg`;
writeFileSync(path, svg);
console.log(`ok ${v.name}`);
}
console.log(`\nrendered ${variations.length} variations → ${OUT_DIR}/`);

View File

@@ -0,0 +1,251 @@
/* ============================================================
qft-variations-fine.mjs — sketch 05.
Two new mechanics exercised:
(a) DENSER cubic mesh (cubicN=2 → 125 vertices / ~300 edges)
+ finer photon waves (photonCyclesPerUnit ↑) → "even
finer oscillating sub-grid with each line another hue
in a gradient"
(b) LARGER softer rosettes — E8 cluster scaled 0.550.85
with lightness pushed ABOVE the paper's lightness and
opacity dropped to 0.350.55 → the rosette becomes a
soft luminous halo rather than a focal jewel.
Plus fewer / dimmer cross-field links so they punctuate
instead of dominate.
Output → output/qft/sketch05/
============================================================ */
import { writeFileSync, mkdirSync } from 'node:fs';
import { generateQFTScene } from '../src/qft/scene.js';
import { paramsFromSeed } from '../src/qft/params.js';
import { renderQFTSVG } from '../src/qft/renderer.js';
const BASE_SEED = 'FEYNMAN-7167';
const OUT_DIR = 'output/qft/sketch05';
const SIZE = 1800;
mkdirSync(OUT_DIR, { recursive: true });
const F = (hueStart, hueEnd, saturation, lightness, opacity) =>
({ hueStart, hueEnd, saturation, lightness, opacity });
const paper = (flat, glowInDelta = [18, 16, 14], glowOutDelta = [-25, -22, -20]) => ({
flat,
glowIn: [flat[0] + glowInDelta[0], flat[1] + glowInDelta[1], flat[2] + glowInDelta[2]],
glowOut: [flat[0] + glowOutDelta[0], flat[1] + glowOutDelta[1], flat[2] + glowOutDelta[2]],
});
// shared base for the new sketches
// Dense cubic (300 edges) needs MUCH thinner strokes than sketch04, otherwise it
// piles into solid wallpaper and buries the soft rosettes. Stroke 1.0 + cubic
// opacity ~0.45 leaves room for the rosette + schlegel to read.
const FINE_BASE = {
cubicN: 2, // 5³=125 vertices / ~300 edges
photonCyclesPerUnit: 22, // finer waves than default (12)
segmentsPerEdge: 10,
linkCount: 5, // fewer cross-field links (was 8)
stroke: 1.0, // thin so the dense mesh stays atmospheric, not opaque
};
const variations = [
{
name: '01_spectral-comb-pearl',
label: 'Spectral comb · pearl · dense cubic + radial spectrum + soft rosette',
overrides: {
...FINE_BASE,
paperOverride: paper([240, 235, 230]),
vignOverride: [80, 70, 65], featureOverride: [80, 70, 60],
gradientMode: 'radial',
cubicScale: 1.40, schlegelScale: 0, // no schlegel — let cubic + rosette breathe
e8Origins: [{ x: 0, y: 0, scale: 0.72 }], // single large central rosette
fields: {
cubic: F(0.55, 0.04, 0.65, 0.45, 0.30), // cool centre → warm corners
schlegel: F(0, 0, 0, 0.5, 0), // off
e8: F(0.55, 0.08, 0.55, 0.48, 0.85), // very pale tinted-white halo (L > paper)
links: F(0.04, 0.10, 0.55, 0.45, 0.55), // dim warm links
},
},
},
{
name: '02_dense-radial-cream',
label: 'Dense radial spectrum · cream · large pale rosette',
overrides: {
...FINE_BASE,
substrate: 'cream',
gradientMode: 'radial',
cubicScale: 1.55, schlegelScale: 0.85,
e8Origins: [{ x: 0, y: 0, scale: 0.78 }],
fields: {
cubic: F(0.58, 0.05, 0.70, 0.40, 0.30),
schlegel: F(0.10, 0.05, 0.35, 0.45, 0.55),
e8: F(0.08, 0.04, 0.55, 0.48, 0.85), // pale warm halo on cream
links: F(0.05, 0.10, 0.60, 0.45, 0.55),
},
},
},
{
name: '03_linear-horizon-ivory',
label: 'Linear horizon · ivory · dense cubic with left→right gradient',
overrides: {
...FINE_BASE,
paperOverride: paper([232, 224, 202]),
vignOverride: [70, 55, 40], featureOverride: [60, 50, 35],
gradientMode: 'linear-x',
cubicScale: 1.65, schlegelScale: 0.90,
e8Origins: [{ x: -0.35, y: 0.25, scale: 0.55 },
{ x: 0.35, y: 0.25, scale: 0.55 }], // twin pale rosettes lower
fields: {
cubic: F(0.58, 0.04, 0.65, 0.36, 0.30),
schlegel: F(0.60, 0.08, 0.40, 0.40, 0.55),
e8: F(0.55, 0.10, 0.55, 0.48, 0.85),
links: F(0.06, 0.12, 0.60, 0.40, 0.55),
},
},
},
{
name: '04_twin-rosettes-mauve',
label: 'Twin overlapping rosettes · pale mauve · soft & atmospheric',
overrides: {
...FINE_BASE,
paperOverride: paper([222, 205, 224]),
vignOverride: [55, 35, 60], featureOverride: [75, 50, 90],
gradientMode: 'along-edge',
cubicScale: 1.45, schlegelScale: 0, // no schlegel
e8Origins: [{ x: -0.20, y: 0, scale: 0.65 },
{ x: 0.20, y: 0, scale: 0.65 }],
fields: {
cubic: F(0.72, 0.92, 0.55, 0.38, 0.30),
schlegel: F(0, 0, 0, 0.5, 0),
e8: F(0.78, 0.92, 0.55, 0.48, 0.85), // pale lighter-than-mauve glow
links: F(0.82, 0.95, 0.65, 0.40, 0.55),
},
},
},
{
name: '05_finest-waves-linen',
label: 'Finest photon waves · linen · super-dense cubic + soft halo',
overrides: {
...FINE_BASE,
paperOverride: paper([225, 210, 178]),
vignOverride: [85, 60, 35], featureOverride: [80, 55, 30],
photonCyclesPerUnit: 30, // even finer ripples than the FINE_BASE
cubicScale: 1.50, schlegelScale: 0.75,
e8Origins: [{ x: 0, y: 0, scale: 0.82 }],
fields: {
cubic: F(0.08, 0.14, 0.60, 0.35, 0.30),
schlegel: F(0.04, 0.10, 0.40, 0.35, 0.55),
e8: F(0.10, 0.06, 0.55, 0.48, 0.85),
links: F(0.06, 0.10, 0.60, 0.40, 0.55),
},
},
},
{
name: '06_big-mandala-pale-blue',
label: 'Big central mandala · pale blue · single huge rosette',
overrides: {
...FINE_BASE,
paperOverride: paper([190, 215, 230]),
vignOverride: [40, 55, 70], featureOverride: [30, 50, 75],
gradientMode: 'radial',
cubicScale: 1.50, schlegelScale: 0.80,
e8Origins: [{ x: 0, y: 0, scale: 0.92 }], // very large central rosette
fields: {
cubic: F(0.60, 0.66, 0.65, 0.25, 0.30),
schlegel: F(0.58, 0.62, 0.45, 0.28, 0.55),
e8: F(0.55, 0.65, 0.55, 0.48, 0.85), // near-white tinted-blue halo
links: F(0.58, 0.66, 0.65, 0.32, 0.55),
},
},
},
{
name: '07_dense-ghostly-schlegel-bone',
label: 'Dense cubic + ghostly large Schlegel · bone china',
overrides: {
...FINE_BASE,
paperOverride: paper([234, 230, 222]),
vignOverride: [80, 70, 60], featureOverride: [70, 60, 50],
gradientMode: 'radial',
cubicScale: 1.55, schlegelScale: 1.85, // big BUT...
e8Origins: [{ x: 0, y: 0, scale: 0.72 }],
fields: {
cubic: F(0.55, 0.05, 0.65, 0.38, 0.30),
schlegel: F(0.62, 0.10, 0.25, 0.92, 0.35), // ...made ghostly: pale + low opacity
e8: F(0.10, 0.06, 0.55, 0.48, 0.85),
links: F(0.06, 0.10, 0.55, 0.40, 0.50),
},
},
},
{
name: '08_atmospheric-mint',
label: 'Atmospheric · pale mint · large soft rosette dominant',
overrides: {
...FINE_BASE,
paperOverride: paper([210, 225, 208]),
vignOverride: [30, 55, 35], featureOverride: [40, 70, 45],
stroke: 1.6, // thinner — let the rosette breathe
cubicScale: 1.30, schlegelScale: 0,
e8Origins: [{ x: 0, y: 0, scale: 0.85 }],
fields: {
cubic: F(0.42, 0.50, 0.45, 0.36, 0.30),
schlegel: F(0, 0, 0, 0.5, 0),
e8: F(0.40, 0.50, 0.55, 0.48, 0.85),
links: F(0.12, 0.18, 0.55, 0.42, 0.55),
},
},
},
{
name: '09_all-dense-layered-pearl',
label: 'Everything dense and layered · pearl · 4 rosettes, full spectrum',
overrides: {
...FINE_BASE,
paperOverride: paper([240, 235, 230]),
vignOverride: [80, 70, 65], featureOverride: [80, 70, 60],
gradientMode: 'radial',
cubicScale: 1.60, schlegelScale: 1.20,
e8Origins: [
{ x: -0.30, y: -0.30, scale: 0.42 },
{ x: 0.30, y: -0.30, scale: 0.42 },
{ x: -0.30, y: 0.30, scale: 0.42 },
{ x: 0.30, y: 0.30, scale: 0.42 },
],
linkCount: 8,
fields: {
cubic: F(0.55, 0.05, 0.65, 0.40, 0.30),
schlegel: F(0.58, 0.10, 0.35, 0.42, 0.55),
e8: F(0.55, 0.08, 0.55, 0.48, 0.85),
links: F(0.06, 0.10, 0.55, 0.42, 0.55),
},
},
},
{
name: '10_concentric-rosettes-pale-rose',
label: 'Concentric nested rosettes · pale rose · all centred',
overrides: {
...FINE_BASE,
paperOverride: paper([232, 210, 210]),
vignOverride: [80, 55, 55], featureOverride: [90, 55, 55],
gradientMode: 'radial',
cubicScale: 1.30, schlegelScale: 0,
e8Origins: [
{ x: 0, y: 0, scale: 0.95 }, // outermost
{ x: 0, y: 0, scale: 0.62 }, // middle
{ x: 0, y: 0, scale: 0.30 }, // innermost
],
linkCount: 0, // no links — the nested rosettes are the picture
fields: {
cubic: F(0.95, 0.03, 0.55, 0.38, 0.30),
schlegel: F(0, 0, 0, 0.5, 0),
e8: F(0.92, 0.05, 0.55, 0.48, 0.85),
links: F(0.9, 0.05, 0.55, 0.42, 0.50),
},
},
},
];
const base = paramsFromSeed(BASE_SEED);
for (const v of variations) {
const params = { ...base, ...v.overrides };
if (v.overrides.fields) params.fields = v.overrides.fields;
const svg = renderQFTSVG(generateQFTScene(params), params, SIZE);
const path = `${OUT_DIR}/${v.name}.svg`;
writeFileSync(path, svg);
console.log(`ok ${v.name} · ${v.label}`);
}
console.log(`\nrendered ${variations.length} variations → ${OUT_DIR}/`);

View File

@@ -0,0 +1,230 @@
/* ============================================================
qft-variations-light.mjs — sketch 04.
Lighter backgrounds, larger field scales (most bleed past the
frame), per-edge hue GRADIENTS within each field, and
noticeably THICKER strokes than previous sketches. Custom
paperOverride RGB for the more unusual pale grounds.
Output → output/qft/sketch04/
============================================================ */
import { writeFileSync, mkdirSync } from 'node:fs';
import { generateQFTScene } from '../src/qft/scene.js';
import { paramsFromSeed } from '../src/qft/params.js';
import { renderQFTSVG } from '../src/qft/renderer.js';
const BASE_SEED = 'FEYNMAN-7167';
const OUT_DIR = 'output/qft/sketch04';
const SIZE = 1800;
mkdirSync(OUT_DIR, { recursive: true });
const F = (hueStart, hueEnd, saturation, lightness, opacity) =>
({ hueStart, hueEnd, saturation, lightness, opacity });
// helper: build a paperOverride from one RGB and derive sensible glow stops
const paper = (flat, glowInDelta = [18, 16, 14], glowOutDelta = [-25, -22, -20]) => ({
flat,
glowIn: [flat[0] + glowInDelta[0], flat[1] + glowInDelta[1], flat[2] + glowInDelta[2]],
glowOut: [flat[0] + glowOutDelta[0], flat[1] + glowOutDelta[1], flat[2] + glowOutDelta[2]],
});
// dark header/vignette tones to read against light paper
const DARK_FEATURE = [60, 48, 36];
const DARK_VIGN = [70, 55, 40];
const variations = [
{
name: '01_warm-sunset-cream',
label: 'Warm sunset · cream · huge cubic bleeding past frame',
overrides: {
substrate: 'cream',
stroke: 2.6,
cubicScale: 1.75,
schlegelScale: 0.85,
e8Count: 3, e8Scale: 0.22, e8OriginRadius: 0.55,
fields: {
cubic: F(0.10, 0.02, 0.65, 0.40, 0.82), // amber → red
schlegel: F(0.13, 0.06, 0.45, 0.38, 0.65), // gold → orange
e8: F(0.05, 0.13, 0.75, 0.42, 0.92), // red → gold
links: F(0.95, 0.04, 0.85, 0.38, 0.96), // pink → red bright
},
},
},
{
name: '02_pale-verdigris-linen',
label: 'Pale verdigris · linen ground · large scales',
overrides: {
paperOverride: paper([222, 210, 184]),
vignOverride: DARK_VIGN,
featureOverride: DARK_FEATURE,
stroke: 2.4,
cubicScale: 1.55,
schlegelScale: 1.35,
e8Count: 3, e8Scale: 0.24, e8OriginRadius: 0.48,
fields: {
cubic: F(0.44, 0.50, 0.55, 0.34, 0.82), // teal → cyan
schlegel: F(0.06, 0.10, 0.55, 0.38, 0.66), // copper accents
e8: F(0.35, 0.46, 0.60, 0.32, 0.90), // sage → teal
links: F(0.04, 0.10, 0.80, 0.38, 0.95),
},
},
},
{
name: '03_rose-peach',
label: 'Rose pastel on peach paper · huge cubic',
overrides: {
paperOverride: paper([232, 200, 175]),
vignOverride: [90, 50, 40],
featureOverride: [110, 60, 50],
stroke: 2.8,
cubicScale: 1.65,
schlegelScale: 0.75, schlegelOriginX: 0.20,
e8Count: 2, e8Scale: 0.28, e8OriginRadius: 0.52,
fields: {
cubic: F(0.92, 0.04, 0.55, 0.42, 0.82),
schlegel: F(0.88, 0.94, 0.45, 0.40, 0.66),
e8: F(0.96, 0.06, 0.65, 0.42, 0.92),
links: F(0.90, 0.04, 0.85, 0.38, 0.96),
},
},
},
{
name: '04_pale-sky-ivory',
label: 'Pale sky · ivory · huge Schlegel tesseract',
overrides: {
paperOverride: paper([232, 222, 200]),
vignOverride: [60, 50, 40],
featureOverride: DARK_FEATURE,
stroke: 2.4,
cubicScale: 0.70,
schlegelScale: 1.95, // massive — bleeds significantly
e8Count: 3, e8Scale: 0.20, e8OriginRadius: 0.60,
fields: {
cubic: F(0.58, 0.66, 0.55, 0.30, 0.80), // blue → violet
schlegel: F(0.62, 0.72, 0.55, 0.32, 0.66), // indigo → violet
e8: F(0.55, 0.70, 0.65, 0.30, 0.92),
links: F(0.60, 0.80, 0.85, 0.32, 0.96),
},
},
},
{
name: '05_mint-gold-pale',
label: 'Mint & gold · pale-mint ground',
overrides: {
paperOverride: paper([210, 225, 208]),
vignOverride: [30, 55, 35],
featureOverride: [40, 70, 45],
stroke: 2.6,
cubicScale: 1.50,
schlegelScale: 1.25,
e8Count: 3, e8Scale: 0.26, e8OriginRadius: 0.48,
fields: {
cubic: F(0.40, 0.50, 0.55, 0.30, 0.82), // mint teal
schlegel: F(0.10, 0.16, 0.60, 0.36, 0.66), // gold
e8: F(0.13, 0.20, 0.80, 0.40, 0.92),
links: F(0.08, 0.14, 0.85, 0.40, 0.96),
},
},
},
{
name: '06_bone-radial-gradient',
label: 'Bone china · radial gradient (cool centre → warm edges)',
overrides: {
paperOverride: paper([234, 230, 222]),
vignOverride: [70, 60, 50],
featureOverride: DARK_FEATURE,
stroke: 2.6, gradientMode: 'radial',
cubicScale: 1.55, schlegelScale: 1.30,
e8Count: 3, e8Scale: 0.22, e8OriginRadius: 0.55,
fields: {
cubic: F(0.55, 0.06, 0.62, 0.36, 0.82), // cyan centre → orange edges
schlegel: F(0.58, 0.04, 0.50, 0.34, 0.70),
e8: F(0.55, 0.05, 0.72, 0.40, 0.92),
links: F(0.55, 0.05, 0.85, 0.40, 0.96),
},
},
},
{
name: '07_pale-mauve-violet',
label: 'Pale mauve fog · violet → pink gradients · thick',
overrides: {
paperOverride: paper([222, 205, 224]),
vignOverride: [55, 35, 60],
featureOverride: [75, 50, 90],
stroke: 3.0,
cubicScale: 1.85, // bleeds heavily
schlegelScale: 0.90,
e8Count: 3, e8Scale: 0.22, e8OriginRadius: 0.50,
fields: {
cubic: F(0.74, 0.92, 0.55, 0.34, 0.84),
schlegel: F(0.78, 0.86, 0.50, 0.32, 0.68),
e8: F(0.82, 0.96, 0.65, 0.38, 0.92),
links: F(0.78, 0.95, 0.85, 0.36, 0.96),
},
},
},
{
name: '08_faded-blueprint',
label: 'Faded blueprint · pale cyan paper · huge Schlegel',
overrides: {
paperOverride: paper([186, 214, 228]),
vignOverride: [40, 55, 70],
featureOverride: [30, 50, 75],
stroke: 2.8,
cubicScale: 0.80,
schlegelScale: 1.90,
e8Count: 3, e8Scale: 0.22, e8OriginRadius: 0.55,
fields: {
cubic: F(0.60, 0.66, 0.60, 0.22, 0.86), // dark blue → navy
schlegel: F(0.58, 0.62, 0.55, 0.20, 0.66),
e8: F(0.62, 0.72, 0.70, 0.28, 0.92),
links: F(0.58, 0.68, 0.85, 0.28, 0.96),
},
},
},
{
name: '09_linen-sepia-large',
label: 'Linen · warm sepia gradients · everything LARGE · thickest strokes',
overrides: {
paperOverride: paper([226, 210, 178]),
vignOverride: [80, 55, 30],
featureOverride: [85, 55, 30],
stroke: 3.4, // chunkiest of the set
cubicScale: 1.65, schlegelScale: 1.55,
e8Count: 3, e8Scale: 0.28, e8OriginRadius: 0.50,
fields: {
cubic: F(0.06, 0.12, 0.55, 0.30, 0.86),
schlegel: F(0.10, 0.03, 0.45, 0.28, 0.70),
e8: F(0.08, 0.16, 0.70, 0.36, 0.92),
links: F(0.02, 0.12, 0.85, 0.30, 0.96),
},
},
},
{
name: '10_pearl-pastel-spectrum',
label: 'Pearl · pastel rainbow (per-field hue families)',
overrides: {
paperOverride: paper([240, 235, 230]),
vignOverride: [70, 65, 60],
featureOverride: [80, 70, 60],
stroke: 2.8,
cubicScale: 1.50, schlegelScale: 1.40,
e8Count: 3, e8Scale: 0.24, e8OriginRadius: 0.55,
fields: {
cubic: F(0.55, 0.70, 0.50, 0.42, 0.80), // cyan → violet pastel
schlegel: F(0.08, 0.18, 0.45, 0.42, 0.62), // gold → yellow pastel
e8: F(0.86, 0.04, 0.55, 0.42, 0.86), // pink → red pastel
links: F(0.38, 0.55, 0.70, 0.40, 0.94), // green → cyan pastel
},
},
},
];
const base = paramsFromSeed(BASE_SEED);
for (const v of variations) {
const params = { ...base, ...v.overrides };
if (v.overrides.fields) params.fields = v.overrides.fields;
const svg = renderQFTSVG(generateQFTScene(params), params, SIZE);
const path = `${OUT_DIR}/${v.name}.svg`;
writeFileSync(path, svg);
console.log(`ok ${v.name} · ${v.label}`);
}
console.log(`\nrendered ${variations.length} variations → ${OUT_DIR}/`);

View File

@@ -0,0 +1,385 @@
/* ============================================================
qft-variations-perturb.mjs — sketch 08.
New mechanics — fields perturbed BY their own physics:
WAVEPACKETS — Gaussian local perturbation of the cubic
(and schlegel) lattice; vertices push radially outward
under a bell-curve falloff. The "particle" is now a
feature OF the field, not a separate object placed on it.
RIPPLES — concentric expanding wavefronts from a source
point. Each ring a many-segmented polyline; renderer
applies propagator decoration to each chord.
Plus DENSER cubic (cubicN=2 → 125 vertices / ~300 edges) so
more grid lines show the wavepacket distortion clearly.
Output → output/qft/sketch08/
============================================================ */
import { writeFileSync, mkdirSync } from 'node:fs';
import { generateQFTScene } from '../src/qft/scene.js';
import { paramsFromSeed } from '../src/qft/params.js';
import { renderQFTSVG } from '../src/qft/renderer.js';
const BASE_SEED = 'FEYNMAN-7167';
const OUT_DIR = 'output/qft/sketch08';
const SIZE = 1800;
mkdirSync(OUT_DIR, { recursive: true });
const F = (hueStart, hueEnd, saturation, lightness, opacity, stroke) => {
const f = { hueStart, hueEnd, saturation, lightness, opacity };
if (stroke != null) f.stroke = stroke;
return f;
};
const paper = (flat, glowInDelta = [16, 14, 12], glowOutDelta = [-22, -20, -18]) => ({
flat,
glowIn: [flat[0] + glowInDelta[0], flat[1] + glowInDelta[1], flat[2] + glowInDelta[2]],
glowOut: [flat[0] + glowOutDelta[0], flat[1] + glowOutDelta[1], flat[2] + glowOutDelta[2]],
});
// Wavepacket helper: pos + amp + sigma
const W = (x, y, amplitude, sigma) => ({ x, y, amplitude, sigma });
// Ripple helper: pos + ring count + r0 + dR + optional propagator
const R = (x, y, count = 6, r0 = 0.06, dR = 0.09, propagator = 'photon') =>
({ x, y, count, r0, dR, propagator });
const BASE = {
cubicN: 2, // DENSER cubic — more grid lines so wavepacket bulges read
photonCyclesPerUnit: 14,
segmentsPerEdge: 8, // fewer segments per edge — denser mesh + many circles
linkCount: 8,
e8Style: 'nautilus',
nautilusTurns: 2.4, nautilusPerTurn: 14, nautilusGrowth: 0.22,
};
const variations = [
{
name: '01_single-wavepacket-bulge',
label: 'Single wavepacket · lattice bulges around a centred particle',
overrides: {
...BASE,
paperOverride: paper([198, 198, 215]),
vignOverride: [60, 55, 75], featureOverride: [40, 40, 60],
cubicScale: 1.55, schlegelScale: 1.45,
schlegelInnerR: 0.40, schlegelRot3D: 0.45,
wavepackets: [W(0, 0, 0.085, 0.16)], // single positive bulge at centre
e8Origins: [], linkCount: 6,
fields: {
cubic: F(0.60, 0.70, 0.50, 0.40, 0.60, 1.1),
schlegel: F(0.62, 0.72, 0.55, 0.35, 0.78, 2.0),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.05, 0.12, 0.85, 0.50, 0.92, 2.2),
},
},
},
{
name: '02_two-wavepackets-interference',
label: 'Two wavepackets · interference bulges between them',
overrides: {
...BASE,
paperOverride: paper([180, 175, 195]),
vignOverride: [55, 50, 70], featureOverride: [40, 40, 55],
cubicScale: 1.60, schlegelScale: 1.30,
schlegelInnerR: 0.42, schlegelRot3D: 0.55,
wavepackets: [W(-0.32, 0, 0.075, 0.15), W(0.32, 0, 0.075, 0.15)],
e8Origins: [], linkCount: 8,
fields: {
cubic: F(0.55, 0.68, 0.50, 0.40, 0.62, 1.1),
schlegel: F(0.60, 0.74, 0.55, 0.35, 0.75, 1.8),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.10, 0.04, 0.90, 0.55, 0.95, 2.4),
},
},
},
{
name: '03_wavepacket-dimple',
label: 'Wavepacket dimple · inward pull (negative amplitude)',
overrides: {
...BASE,
paperOverride: paper([175, 170, 160]),
vignOverride: [60, 55, 45], featureOverride: [50, 45, 35],
cubicScale: 1.55, schlegelScale: 1.30,
schlegelInnerR: 0.38, schlegelRot3D: 0.50,
wavepackets: [W(0, 0, -0.075, 0.18)], // negative = pull vertices INWARD
e8Origins: [{ x: 0, y: 0, scale: 0.32 }],
linkCount: 8,
fields: {
cubic: F(0.05, 0.13, 0.55, 0.38, 0.60, 1.1),
schlegel: F(0.06, 0.14, 0.55, 0.35, 0.72, 1.8),
e8: F(0.08, 0.18, 0.85, 0.55, 0.92, 2.0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.50, 0.10, 0.95, 0.55, 0.95, 2.4),
},
},
},
{
name: '04_ripples-single-source',
label: 'Single ripple source · concentric wavefronts expanding from origin',
overrides: {
...BASE,
paperOverride: paper([170, 175, 190]),
vignOverride: [50, 55, 70], featureOverride: [40, 45, 60],
cubicScale: 1.55, schlegelScale: 1.25,
schlegelInnerR: 0.40, schlegelRot3D: 0.45,
ripples: [R(0, 0, 7, 0.06, 0.10)],
e8Origins: [], linkCount: 8,
fields: {
cubic: F(0.55, 0.62, 0.40, 0.42, 0.45, 0.9),
schlegel: F(0.55, 0.68, 0.45, 0.38, 0.65, 1.6),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0.08, 0.18, 0.85, 0.55, 0.90, 2.4), // hot amber rings
links: F(0.05, 0.13, 0.90, 0.55, 0.92, 2.2),
},
},
},
{
name: '05_two-ripple-sources-interfering',
label: 'Two ripple sources interfering · double-slit feel',
overrides: {
...BASE,
paperOverride: paper([170, 175, 190]),
vignOverride: [50, 55, 70], featureOverride: [40, 45, 60],
cubicScale: 1.55, schlegelScale: 1.25,
ripples: [R(-0.30, 0, 6, 0.05, 0.09), R(0.30, 0, 6, 0.05, 0.09)],
e8Origins: [], linkCount: 8,
fields: {
cubic: F(0.55, 0.62, 0.40, 0.42, 0.45, 0.9),
schlegel: F(0.55, 0.68, 0.45, 0.38, 0.60, 1.4),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0.50, 0.62, 0.85, 0.50, 0.88, 2.4), // cyan rings
links: F(0.05, 0.13, 0.90, 0.55, 0.92, 2.2),
},
},
},
{
name: '06_schrodinger-atom',
label: 'Schrödinger atom · wavepacket + ripples at the SAME centre',
overrides: {
...BASE,
paperOverride: paper([180, 175, 165]),
vignOverride: [60, 55, 45], featureOverride: [55, 45, 35],
cubicScale: 1.55, schlegelScale: 1.40,
schlegelInnerR: 0.38, schlegelRot3D: 0.50,
wavepackets: [W(0, 0, 0.080, 0.18)],
ripples: [R(0, 0, 5, 0.18, 0.12)], // ripples start at larger r0 (outside the wavepacket)
e8Origins: [], linkCount: 10,
fields: {
cubic: F(0.06, 0.14, 0.55, 0.40, 0.55, 1.0),
schlegel: F(0.08, 0.18, 0.60, 0.36, 0.75, 1.8),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0.10, 0.04, 0.95, 0.55, 0.92, 2.6),
links: F(0.95, 0.10, 0.95, 0.55, 0.95, 2.4),
},
},
},
{
name: '07_dense-cubic-with-wavepacket',
label: 'Dense cubic + wavepacket · more grid lines show the distortion',
overrides: {
...BASE,
paperOverride: paper([185, 180, 200]),
vignOverride: [50, 50, 70], featureOverride: [40, 40, 55],
cubicN: 2,
cubicScale: 1.50, schlegelScale: 0.95,
wavepackets: [W(0.20, -0.10, 0.090, 0.18)],
e8Origins: [], linkCount: 7,
fields: {
cubic: F(0.50, 0.72, 0.55, 0.42, 0.50, 0.8), // dense + thin
schlegel: F(0.60, 0.78, 0.50, 0.38, 0.60, 1.6),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.06, 0.14, 0.90, 0.55, 0.92, 2.2),
},
},
},
{
name: '08_dense-cubic-with-ripples',
label: 'Dense cubic backdrop · ripples expanding through it',
overrides: {
...BASE,
paperOverride: paper([195, 180, 165]),
vignOverride: [70, 55, 40], featureOverride: [55, 45, 30],
cubicN: 2,
cubicScale: 1.50, schlegelScale: 0.95,
ripples: [R(0, 0, 8, 0.07, 0.085)],
e8Origins: [], linkCount: 8,
fields: {
cubic: F(0.06, 0.16, 0.50, 0.40, 0.48, 0.8),
schlegel: F(0.06, 0.14, 0.45, 0.38, 0.55, 1.4),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0.06, 0.18, 0.95, 0.50, 0.92, 2.6),
links: F(0.95, 0.10, 0.92, 0.55, 0.92, 2.4),
},
},
},
{
name: '09_multi-particles-with-ripples-and-nautilus',
label: 'Multiple wavepackets + ripples + nautilus chain · fully tangled',
overrides: {
...BASE,
paperOverride: paper([175, 170, 175]),
vignOverride: [55, 50, 60], featureOverride: [45, 40, 50],
cubicScale: 1.55, schlegelScale: 1.50,
schlegelInnerR: 0.40, schlegelRot3D: 0.50,
wavepackets: [W(-0.30, -0.30, 0.060, 0.14), W(0.30, 0.30, 0.060, 0.14)],
ripples: [R(-0.30, -0.30, 4, 0.08, 0.08), R(0.30, 0.30, 4, 0.08, 0.08)],
e8Origins: [{ x: 0, y: 0, scale: 0.35 }],
linkCount: 12,
fields: {
cubic: F(0.55, 0.85, 0.50, 0.40, 0.52, 0.9),
schlegel: F(0.60, 0.85, 0.55, 0.36, 0.72, 1.6),
e8: F(0.05, 0.15, 0.85, 0.55, 0.90, 2.0),
ripple: F(0.50, 0.80, 0.90, 0.50, 0.90, 2.2),
links: F(0.95, 0.08, 0.95, 0.55, 0.95, 2.4),
},
},
},
{
name: '10_ripple-field-dominant',
label: 'Ripple field dominant · five concentric expansions across the canvas',
overrides: {
...BASE,
paperOverride: paper([165, 165, 180]),
vignOverride: [50, 50, 60], featureOverride: [40, 40, 55],
cubicScale: 1.45, schlegelScale: 0,
ripples: [
R(-0.45, -0.45, 4, 0.05, 0.08),
R( 0.45, -0.45, 4, 0.05, 0.08),
R(-0.45, 0.45, 4, 0.05, 0.08),
R( 0.45, 0.45, 4, 0.05, 0.08),
R( 0.00, 0.00, 6, 0.06, 0.10),
],
e8Origins: [], linkCount: 10,
fields: {
cubic: F(0.50, 0.62, 0.40, 0.42, 0.40, 0.8),
schlegel: F(0, 0, 0, 0.5, 0),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0.55, 0.85, 0.85, 0.50, 0.85, 2.0), // cool spectrum rings
links: F(0.50, 0.85, 0.95, 0.55, 0.92, 2.2),
},
},
},
{
name: '11_wavepacket-at-edge-cubic-bleed',
label: 'Wavepacket near edge · cubic bleeding · particle entering/leaving frame',
overrides: {
...BASE,
paperOverride: paper([185, 175, 155]),
vignOverride: [70, 55, 35], featureOverride: [60, 45, 25],
cubicScale: 1.80, schlegelScale: 1.30,
schlegelInnerR: 0.40, schlegelRot3D: 0.55,
wavepackets: [W(0.55, -0.25, 0.100, 0.20)],
ripples: [R(0.55, -0.25, 5, 0.05, 0.10)],
e8Origins: [], linkCount: 9,
fields: {
cubic: F(0.06, 0.14, 0.55, 0.38, 0.55, 1.0),
schlegel: F(0.04, 0.13, 0.60, 0.34, 0.78, 1.8),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0.10, 0.04, 0.95, 0.55, 0.92, 2.4),
links: F(0.50, 0.10, 0.95, 0.55, 0.95, 2.6),
},
},
},
{
name: '12_asymmetric-perturbed-field',
label: 'Asymmetric · wavepacket off-centre · everything biased',
overrides: {
...BASE,
paperOverride: paper([170, 175, 195]),
vignOverride: [50, 55, 70], featureOverride: [40, 45, 60],
cubicScale: 1.70, cubicOriginX: -0.10,
schlegelScale: 1.75, schlegelOriginX: -0.20,
schlegelInnerR: 0.42, schlegelRot3D: 0.40,
wavepackets: [W(-0.30, -0.20, 0.075, 0.16)],
ripples: [R(0.35, 0.30, 5, 0.05, 0.10)],
e8Origins: [{ x: 0.20, y: 0.45, scale: 0.32 }],
linkCount: 12,
fields: {
cubic: F(0.55, 0.68, 0.45, 0.40, 0.50, 1.0),
schlegel: F(0.55, 0.70, 0.55, 0.36, 0.72, 1.8),
e8: F(0.05, 0.16, 0.85, 0.55, 0.92, 2.0),
ripple: F(0.50, 0.10, 0.95, 0.55, 0.90, 2.4),
links: F(0.95, 0.10, 0.95, 0.55, 0.95, 2.6),
},
},
},
{
name: '13_diatomic-two-particles-linked',
label: 'Diatomic · two wavepackets + ripples · molecular feel',
overrides: {
...BASE,
paperOverride: paper([185, 175, 175]),
vignOverride: [60, 50, 55], featureOverride: [45, 40, 45],
cubicScale: 1.60, schlegelScale: 1.30,
schlegelInnerR: 0.40, schlegelRot3D: 0.55,
wavepackets: [W(-0.25, 0, 0.075, 0.15), W(0.25, 0, 0.075, 0.15)],
ripples: [R(-0.25, 0, 4, 0.05, 0.08), R(0.25, 0, 4, 0.05, 0.08)],
e8Origins: [],
linkCount: 14, // dense bond-like links
fields: {
cubic: F(0.85, 0.05, 0.50, 0.42, 0.52, 1.0),
schlegel: F(0.88, 0.05, 0.55, 0.38, 0.72, 1.6),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0.92, 0.06, 0.90, 0.50, 0.90, 2.2),
links: F(0.05, 0.16, 0.95, 0.55, 0.95, 2.6),
},
},
},
{
name: '14_field-cascade',
label: 'Field cascade · ripples at varied scales · ladder of disturbances',
overrides: {
...BASE,
paperOverride: paper([175, 165, 145]),
vignOverride: [65, 50, 30], featureOverride: [55, 45, 30],
cubicScale: 1.55, schlegelScale: 1.30,
schlegelInnerR: 0.40, schlegelRot3D: 0.50,
ripples: [
R(-0.50, 0.35, 3, 0.04, 0.05), // small tight ripples
R(-0.10, 0.05, 5, 0.06, 0.08), // medium
R( 0.40, -0.30, 7, 0.08, 0.12), // large widely spaced
],
e8Origins: [{ x: 0.40, y: 0.35, scale: 0.28 }],
linkCount: 11,
fields: {
cubic: F(0.06, 0.14, 0.50, 0.40, 0.50, 1.0),
schlegel: F(0.05, 0.13, 0.55, 0.36, 0.70, 1.6),
e8: F(0.10, 0.18, 0.85, 0.55, 0.92, 2.0),
ripple: F(0.10, 0.04, 0.95, 0.55, 0.92, 2.4),
links: F(0.95, 0.06, 0.95, 0.55, 0.95, 2.6),
},
},
},
{
name: '15_quiet-single-ripple',
label: 'Quiet counter-example · single small ripple · contemplative',
overrides: {
...BASE,
paperOverride: paper([190, 185, 178]),
vignOverride: [55, 50, 45], featureOverride: [50, 45, 40],
cubicScale: 1.40, schlegelScale: 1.20,
schlegelInnerR: 0.40, schlegelRot3D: 0.45,
ripples: [R(0, 0, 4, 0.08, 0.10)],
e8Origins: [], linkCount: 4, // few links
fields: {
cubic: F(0.60, 0.68, 0.30, 0.42, 0.38, 0.8),
schlegel: F(0.62, 0.70, 0.40, 0.40, 0.55, 1.2),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0.08, 0.14, 0.70, 0.50, 0.82, 1.8),
links: F(0.06, 0.14, 0.75, 0.50, 0.80, 1.6),
},
},
},
];
const base = paramsFromSeed(BASE_SEED);
for (const v of variations) {
const params = { ...base, ...v.overrides };
if (v.overrides.fields) params.fields = v.overrides.fields;
const svg = renderQFTSVG(generateQFTScene(params), params, SIZE);
const path = `${OUT_DIR}/${v.name}.svg`;
writeFileSync(path, svg);
console.log(`ok ${v.name}`);
}
console.log(`\nrendered ${variations.length} variations → ${OUT_DIR}/`);

View File

@@ -0,0 +1,148 @@
/* ============================================================
qft-variations-positions.mjs — sketch 03.
Hold the palette CONSTANT (per-field monochrome — each field
one clear hue so the eye can track its position) and vary
only origin / scale per field. Some fields deliberately
extend past the canvas edge (scale > 1).
Output → output/qft/sketch03/
============================================================ */
import { writeFileSync, mkdirSync } from 'node:fs';
import { generateQFTScene } from '../src/qft/scene.js';
import { paramsFromSeed } from '../src/qft/params.js';
import { renderQFTSVG } from '../src/qft/renderer.js';
const BASE_SEED = 'FEYNMAN-7167';
const OUT_DIR = 'output/qft/sketch03';
const SIZE = 1800;
mkdirSync(OUT_DIR, { recursive: true });
const F = (hueStart, hueEnd, saturation, lightness, opacity) =>
({ hueStart, hueEnd, saturation, lightness, opacity });
// HELD CONSTANT: per-field monochrome palette so each field's position is legible
// cyan cubic · magenta Schlegel · amber E8 · green cross-field links
const COLORS = {
cubic: F(0.55, 0.55, 0.55, 0.58, 0.72),
schlegel: F(0.88, 0.88, 0.55, 0.55, 0.68),
e8: F(0.12, 0.12, 0.75, 0.62, 0.92),
links: F(0.34, 0.34, 0.80, 0.55, 0.95),
};
const SUBSTRATE = 'vintage';
const BASE_OVERRIDES = { substrate: SUBSTRATE, gradientMode: 'along-edge', fields: COLORS };
const variations = [
{
name: '01_centered-reference',
label: 'Reference · everything centred at default scale',
overrides: {}, // no position/scale overrides — defaults
},
{
name: '02_cubic-left-schlegel-right',
label: 'Cubic to the left, Schlegel to the right',
overrides: {
cubicOriginX: -0.45, cubicOriginY: 0,
schlegelOriginX: 0.40, schlegelOriginY: 0,
e8Count: 3, e8OriginRadius: 0.30,
},
},
{
name: '03_huge-cubic-bleed',
label: 'Cubic blown up beyond the frame · others normal',
overrides: {
cubicScale: 1.85,
schlegelScale: 0.65,
e8Count: 3, e8Scale: 0.14, e8OriginRadius: 0.55,
},
},
{
name: '04_huge-schlegel-bleed',
label: 'Schlegel dominating · cube-in-cube extends past edges',
overrides: {
cubicScale: 0.45,
schlegelScale: 1.85,
e8Count: 2, e8Scale: 0.14, e8OriginRadius: 0.30,
},
},
{
name: '05_tiny-lots-of-paper',
label: 'Tiny lattices, lots of paper — a small island',
overrides: {
cubicScale: 0.42,
schlegelScale: 0.32,
e8Count: 2, e8Scale: 0.09, e8OriginRadius: 0.18,
},
},
{
name: '06_e8-cluster-lower-right',
label: 'A single large E8 cluster in the lower-right',
overrides: {
cubicScale: 0.85, cubicOriginX: -0.25, cubicOriginY: -0.20,
schlegelScale: 0.70, schlegelOriginX: -0.30, schlegelOriginY: -0.25,
e8Origins: [{ x: 0.45, y: 0.40, scale: 0.42 }],
},
},
{
name: '07_e8-quartet-at-corners',
label: 'Four E8 clusters near the corners',
overrides: {
cubicScale: 0.85,
schlegelScale: 0.70,
e8Origins: [
{ x: -0.65, y: -0.65, scale: 0.20 },
{ x: 0.65, y: -0.65, scale: 0.20 },
{ x: -0.65, y: 0.65, scale: 0.20 },
{ x: 0.65, y: 0.65, scale: 0.20 },
],
linkCount: 12,
},
},
{
name: '08_composition-shifted-upper-left',
label: 'Whole composition pushed to upper-left · negative space lower-right',
overrides: {
compositionOffsetX: -0.32, compositionOffsetY: -0.32,
cubicScale: 0.85, schlegelScale: 0.78,
e8Count: 3, e8Scale: 0.16, e8OriginRadius: 0.55,
},
},
{
name: '09_vertical-stack',
label: 'Vertical stack · cubic top, Schlegel middle, E8 bottom',
overrides: {
cubicScale: 0.55, cubicOriginX: 0, cubicOriginY: -0.45,
schlegelScale: 0.55, schlegelOriginX: 0, schlegelOriginY: 0,
e8Origins: [{ x: -0.25, y: 0.50, scale: 0.18 }, { x: 0.25, y: 0.50, scale: 0.18 }],
linkCount: 10,
},
},
{
name: '10_triptych-corners',
label: 'Triptych · each field in its own corner',
overrides: {
cubicScale: 0.55, cubicOriginX: -0.40, cubicOriginY: -0.40,
schlegelScale: 0.55, schlegelOriginX: 0.40, schlegelOriginY: -0.40,
e8Origins: [{ x: 0, y: 0.45, scale: 0.25 }],
linkCount: 12,
},
},
{
name: '11_inverse-scales',
label: 'Bonus · inverted scales (tiny cubic, giant Schlegel)',
overrides: {
cubicScale: 0.30,
schlegelScale: 1.70,
e8Count: 3, e8Scale: 0.20, e8OriginRadius: 0.65,
},
},
];
const base = paramsFromSeed(BASE_SEED);
for (const v of variations) {
const params = { ...base, ...BASE_OVERRIDES, ...v.overrides };
params.fields = COLORS;
const svg = renderQFTSVG(generateQFTScene(params), params, SIZE);
const path = `${OUT_DIR}/${v.name}.svg`;
writeFileSync(path, svg);
console.log(`ok ${v.name} · ${v.label}`);
}
console.log(`\nrendered ${variations.length} variations → ${OUT_DIR}/`);

View File

@@ -0,0 +1,387 @@
/* ============================================================
qft-variations-tangle.mjs — sketch 07.
Pursuing "chaotic interconnectedness" via:
- Cubic mesh FILLS the page (often bleeding past edges)
- Schlegel hypercube also taking much space
- Many bright Feynman cross-field links (814 per plate)
- NAUTILUS rosettes (open spirals with chambers growing
with radius) replacing closed concentric rings
- Per-field stroke weights for hierarchical emphasis
- Mid-tone substrates so brightness can work in BOTH
directions (lighter than bg AND darker)
Output → output/qft/sketch07/
============================================================ */
import { writeFileSync, mkdirSync } from 'node:fs';
import { generateQFTScene } from '../src/qft/scene.js';
import { paramsFromSeed } from '../src/qft/params.js';
import { renderQFTSVG } from '../src/qft/renderer.js';
const BASE_SEED = 'FEYNMAN-7167';
const OUT_DIR = 'output/qft/sketch07';
const SIZE = 1800;
mkdirSync(OUT_DIR, { recursive: true });
// Field config: hueStart, hueEnd, sat, light, opacity. Optional `stroke` per-field.
const F = (hueStart, hueEnd, saturation, lightness, opacity, stroke) => {
const f = { hueStart, hueEnd, saturation, lightness, opacity };
if (stroke != null) f.stroke = stroke;
return f;
};
const paper = (flat, glowInDelta = [16, 14, 12], glowOutDelta = [-22, -20, -18]) => ({
flat,
glowIn: [flat[0] + glowInDelta[0], flat[1] + glowInDelta[1], flat[2] + glowInDelta[2]],
glowOut: [flat[0] + glowOutDelta[0], flat[1] + glowOutDelta[1], flat[2] + glowOutDelta[2]],
});
// Shared base: every plate has cubic+schlegel large, many links, nautilus rosettes
const BASE = {
cubicN: 1, // N=2 is too busy when cubic fills the page
photonCyclesPerUnit: 14,
segmentsPerEdge: 10,
linkCount: 11,
// nautilus defaults (used unless overridden)
e8Style: 'nautilus',
nautilusTurns: 2.6, nautilusPerTurn: 14, nautilusGrowth: 0.22,
};
const variations = [
{
name: '01_E3-evolved-nautilus',
label: 'E3 evolved · lavender-grey · twin nautilus + counter-rotated cubic',
overrides: {
...BASE,
paperOverride: paper([198, 198, 215]),
vignOverride: [60, 55, 75], featureOverride: [40, 40, 60],
cubicScale: 1.55, cubicRot: 0.80,
schlegelScale: 1.85, schlegelInnerR: 0.55, schlegelRot3D: -0.30,
schlegelOriginX: -0.10, schlegelOriginY: 0.10,
e8Origins: [{ x: 0.42, y: -0.42, scale: 0.55 },
{ x: -0.42, y: 0.42, scale: 0.55 }],
linkCount: 12,
fields: {
cubic: F(0.62, 0.70, 0.40, 0.50, 0.50, 1.1),
schlegel: F(0.62, 0.72, 0.60, 0.32, 0.82, 2.6), // schlegel thick
e8: F(0.65, 0.78, 0.65, 0.48, 0.88, 2.0),
links: F(0.05, 0.12, 0.85, 0.50, 0.92, 2.4), // bright warm links
},
},
},
{
name: '02_single-huge-nautilus',
label: 'Single huge nautilus spiraling out · dense link web',
overrides: {
...BASE,
paperOverride: paper([175, 170, 185]), // dusty lavender-grey
vignOverride: [55, 50, 70], featureOverride: [40, 40, 55],
cubicScale: 1.50, schlegelScale: 1.20,
e8Origins: [{ x: 0, y: 0, scale: 0.95 }], // ONE huge spiral
nautilusTurns: 3.2, nautilusGrowth: 0.24,
linkCount: 14,
fields: {
cubic: F(0.58, 0.68, 0.40, 0.42, 0.45, 1.0),
schlegel: F(0.06, 0.13, 0.50, 0.40, 0.70, 1.8),
e8: F(0.08, 0.15, 0.85, 0.52, 0.92, 2.6), // saturated warm spiral
links: F(0.05, 0.15, 0.95, 0.55, 0.95, 2.6),
},
},
},
{
name: '03_triangular-three-nautilus',
label: 'Three nautilus in a triangle · many crisscrossing links',
overrides: {
...BASE,
paperOverride: paper([185, 175, 160]), // warm tan-grey
vignOverride: [70, 55, 35], featureOverride: [50, 40, 25],
cubicScale: 1.60, schlegelScale: 1.30,
schlegelRot3D: 0.85,
e8Origins: [
{ x: 0.00, y: -0.40, scale: 0.40 },
{ x: -0.45, y: 0.35, scale: 0.40 },
{ x: 0.45, y: 0.35, scale: 0.40 },
],
linkCount: 14,
fields: {
cubic: F(0.04, 0.12, 0.45, 0.38, 0.50, 1.0),
schlegel: F(0.06, 0.14, 0.55, 0.32, 0.72, 1.6),
e8: F(0.05, 0.18, 0.80, 0.48, 0.90, 2.2),
links: F(0.95, 0.10, 0.95, 0.50, 0.95, 2.4), // hot pink → orange links
},
},
},
{
name: '04_asymmetric-chaos',
label: 'Asymmetric · nautilus low-right · schlegel bleeding upper-left',
overrides: {
...BASE,
paperOverride: paper([170, 165, 175]), // cool grey-mauve
vignOverride: [60, 55, 70], featureOverride: [40, 40, 55],
cubicScale: 1.70, cubicOriginX: -0.10,
schlegelScale: 2.05, schlegelOriginX: -0.30, schlegelOriginY: -0.30,
schlegelInnerR: 0.40, schlegelRot3D: 0.65,
e8Origins: [{ x: 0.45, y: 0.40, scale: 0.55 },
{ x: 0.20, y: 0.55, scale: 0.30 }],
linkCount: 13,
fields: {
cubic: F(0.72, 0.85, 0.45, 0.40, 0.48, 0.9),
schlegel: F(0.74, 0.92, 0.55, 0.32, 0.78, 2.4),
e8: F(0.86, 0.98, 0.85, 0.50, 0.90, 2.2),
links: F(0.50, 0.12, 0.95, 0.55, 0.95, 2.6), // teal→amber crosslinks
},
},
},
{
name: '05_warm-tangle-tan',
label: 'Warm tan-grey · dense cubic · many small nautilus · many links',
overrides: {
...BASE,
paperOverride: paper([188, 168, 145]),
vignOverride: [80, 55, 30], featureOverride: [60, 45, 25],
cubicScale: 1.60, schlegelScale: 0.95,
e8Origins: [
{ x: -0.50, y: -0.45, scale: 0.30 },
{ x: 0.45, y: -0.50, scale: 0.30 },
{ x: -0.40, y: 0.45, scale: 0.30 },
{ x: 0.50, y: 0.40, scale: 0.30 },
{ x: 0.00, y: 0.00, scale: 0.30 }, // 5 small nautilus
],
nautilusTurns: 2.0,
linkCount: 14,
fields: {
cubic: F(0.04, 0.12, 0.55, 0.35, 0.55, 1.0),
schlegel: F(0.04, 0.10, 0.50, 0.32, 0.68, 1.4),
e8: F(0.06, 0.14, 0.85, 0.52, 0.92, 2.2),
links: F(0.50, 0.10, 0.90, 0.55, 0.92, 2.4),
},
},
},
{
name: '06_sage-with-bright-lighter',
label: 'Sage midtone · bright LIGHTER nautilus glowing above',
overrides: {
...BASE,
paperOverride: paper([155, 165, 145]), // sage midtone
vignOverride: [55, 70, 50], featureOverride: [235, 240, 225],
cubicScale: 1.55, schlegelScale: 1.40,
schlegelInnerR: 0.45, schlegelRot3D: 0.30,
e8Origins: [{ x: -0.30, y: -0.10, scale: 0.55 },
{ x: 0.30, y: 0.10, scale: 0.55 }],
linkCount: 11,
fields: {
cubic: F(0.42, 0.50, 0.40, 0.38, 0.55, 1.1),
schlegel: F(0.42, 0.48, 0.45, 0.32, 0.72, 1.8),
e8: F(0.38, 0.50, 0.30, 0.90, 0.88, 2.2), // pale-mint lighter than sage
links: F(0.38, 0.50, 0.45, 0.92, 0.92, 2.4),
},
},
},
{
name: '07_deep-teal-luminous',
label: 'Deep teal · luminous nautilus + cubic glowing brighter',
overrides: {
...BASE,
paperOverride: paper([60, 95, 100]), // deep teal
vignOverride: [25, 45, 50], featureOverride: [195, 220, 220],
cubicScale: 1.55, schlegelScale: 1.35,
schlegelInnerR: 0.30, schlegelRot3D: 0.45,
e8Origins: [{ x: 0, y: 0, scale: 0.80 }],
linkCount: 13,
fields: {
cubic: F(0.50, 0.55, 0.35, 0.82, 0.62, 1.0), // pale cyan glowing
schlegel: F(0.48, 0.55, 0.30, 0.80, 0.65, 1.8),
e8: F(0.10, 0.20, 0.85, 0.65, 0.92, 2.4), // hot amber-gold spiral
links: F(0.10, 0.04, 0.95, 0.65, 0.95, 2.6), // bright orange links
},
},
},
{
name: '08_bone-thick-white-nautilus',
label: 'Bone midtone · thick WHITE nautilus chain · dense links',
overrides: {
...BASE,
paperOverride: paper([185, 175, 160]),
vignOverride: [60, 50, 35], featureOverride: [55, 45, 30],
cubicScale: 1.50, schlegelScale: 1.40,
schlegelInnerR: 0.45, schlegelRot3D: 0.55,
e8Origins: [
{ x: -0.50, y: 0, scale: 0.40 },
{ x: 0.00, y: 0, scale: 0.40 },
{ x: 0.50, y: 0, scale: 0.40 },
],
nautilusTurns: 2.4,
linkCount: 12,
fields: {
cubic: F(0.08, 0.14, 0.35, 0.42, 0.45, 1.0),
schlegel: F(0.08, 0.14, 0.40, 0.38, 0.65, 1.6),
e8: F(0.0, 0.0, 0, 0.96, 0.95, 3.0), // pure white THICK nautilus
links: F(0.0, 0.0, 0, 0.96, 0.88, 2.6), // white connectors too
},
},
},
{
name: '09_galaxies-all-same-direction',
label: 'Spiral galaxies · all nautilus winding the same way',
overrides: {
...BASE,
paperOverride: paper([170, 170, 195]), // dusty blue-violet
vignOverride: [50, 50, 70], featureOverride: [40, 40, 60],
cubicScale: 1.65, schlegelScale: 1.50,
schlegelInnerR: 0.40, schlegelRot3D: 0.20,
e8Origins: [
{ x: -0.45, y: -0.45, scale: 0.32 },
{ x: 0.45, y: -0.30, scale: 0.36 },
{ x: -0.30, y: 0.45, scale: 0.36 },
{ x: 0.50, y: 0.40, scale: 0.32 },
],
nautilusTurns: 3.0,
linkCount: 12,
fields: {
cubic: F(0.60, 0.70, 0.40, 0.42, 0.50, 1.0),
schlegel: F(0.62, 0.72, 0.50, 0.38, 0.65, 1.4),
e8: F(0.55, 0.80, 0.85, 0.55, 0.92, 2.4), // each nautilus its own hue
links: F(0.10, 0.18, 0.95, 0.55, 0.95, 2.4),
},
},
},
{
name: '10_nested-nautilus-mandala',
label: 'Nested nautilus at three scales · cubic backdrop',
overrides: {
...BASE,
paperOverride: paper([180, 170, 175]), // dusky rose-grey
vignOverride: [70, 50, 55], featureOverride: [55, 35, 40],
cubicScale: 1.45, schlegelScale: 0.95,
e8Origins: [
{ x: 0, y: 0, scale: 0.95 },
{ x: 0, y: 0, scale: 0.55 },
{ x: 0, y: 0, scale: 0.25 },
],
nautilusTurns: 2.8,
linkCount: 10,
fields: {
cubic: F(0.92, 0.05, 0.45, 0.42, 0.45, 0.9),
schlegel: F(0.92, 0.05, 0.45, 0.40, 0.55, 1.4),
e8: F(0.94, 0.06, 0.85, 0.50, 0.88, 2.0),
links: F(0.85, 0.95, 0.90, 0.50, 0.92, 2.2),
},
},
},
{
name: '11_windblown-biased',
label: 'Wind-blown · everything biased to the left · drift',
overrides: {
...BASE,
paperOverride: paper([175, 165, 145]),
vignOverride: [65, 50, 30], featureOverride: [55, 45, 30],
cubicScale: 1.55, cubicOriginX: -0.30,
schlegelScale: 1.60, schlegelOriginX: -0.25,
schlegelInnerR: 0.40, schlegelRot3D: 0.55,
compositionOffsetX: -0.10, // whole scene shifted left
e8Origins: [
{ x: -0.55, y: -0.25, scale: 0.42 },
{ x: -0.20, y: 0.35, scale: 0.36 },
],
linkCount: 11,
fields: {
cubic: F(0.06, 0.14, 0.50, 0.38, 0.50, 1.0),
schlegel: F(0.04, 0.12, 0.55, 0.35, 0.72, 1.8),
e8: F(0.08, 0.18, 0.85, 0.50, 0.92, 2.4),
links: F(0.95, 0.10, 0.95, 0.55, 0.95, 2.4),
},
},
},
{
name: '12_linked-chain-across',
label: 'Linked chain · nautilus across the diagonal, very bright links',
overrides: {
...BASE,
paperOverride: paper([170, 175, 185]), // cool storm-grey
vignOverride: [50, 55, 70], featureOverride: [40, 45, 60],
cubicScale: 1.55, schlegelScale: 1.40,
schlegelInnerR: 0.42, schlegelRot3D: 0.40,
e8Origins: [
{ x: -0.55, y: -0.40, scale: 0.32 },
{ x: -0.18, y: -0.10, scale: 0.32 },
{ x: 0.18, y: 0.10, scale: 0.32 },
{ x: 0.55, y: 0.40, scale: 0.32 },
],
linkCount: 14,
fields: {
cubic: F(0.55, 0.65, 0.40, 0.42, 0.50, 1.0),
schlegel: F(0.56, 0.66, 0.50, 0.38, 0.70, 1.6),
e8: F(0.08, 0.18, 0.85, 0.55, 0.92, 2.4), // amber nautilus
links: F(0.06, 0.16, 0.95, 0.60, 0.98, 2.8), // brightest links of the set
},
},
},
{
name: '13_spectral-radial-tangle',
label: 'Spectral radial · everything cycling through the spectrum',
overrides: {
...BASE,
paperOverride: paper([175, 170, 165]),
vignOverride: [60, 55, 50], featureOverride: [50, 45, 40],
gradientMode: 'radial',
cubicScale: 1.55, schlegelScale: 1.45,
schlegelInnerR: 0.40, schlegelRot3D: 0.40,
e8Origins: [{ x: -0.30, y: -0.30, scale: 0.40 },
{ x: 0.30, y: 0.30, scale: 0.40 }],
linkCount: 12,
fields: {
cubic: F(0.55, 0.05, 0.65, 0.42, 0.55, 1.0),
schlegel: F(0.55, 0.05, 0.55, 0.38, 0.70, 1.6),
e8: F(0.55, 0.05, 0.85, 0.50, 0.92, 2.2),
links: F(0.55, 0.05, 0.95, 0.55, 0.95, 2.6),
},
},
},
{
name: '14_quiet-counterexample',
label: 'Quiet counter-example · single nautilus, fewer links · contemplative',
overrides: {
...BASE,
paperOverride: paper([195, 190, 185]),
vignOverride: [55, 50, 45], featureOverride: [55, 50, 45],
cubicScale: 1.40, schlegelScale: 1.20,
schlegelInnerR: 0.42, schlegelRot3D: 0.40,
e8Origins: [{ x: 0, y: 0, scale: 0.65 }],
linkCount: 5, // quiet
fields: {
cubic: F(0.60, 0.68, 0.30, 0.42, 0.40, 0.9),
schlegel: F(0.62, 0.70, 0.40, 0.40, 0.60, 1.4),
e8: F(0.06, 0.12, 0.65, 0.50, 0.85, 1.8),
links: F(0.06, 0.12, 0.70, 0.50, 0.80, 1.8),
},
},
},
{
name: '15_heavy-weight-contrast',
label: 'Weight contrast · thin cubic · THICK schlegel · medium nautilus',
overrides: {
...BASE,
paperOverride: paper([180, 170, 175]),
vignOverride: [55, 45, 50], featureOverride: [40, 35, 40],
cubicScale: 1.65, schlegelScale: 1.55,
schlegelInnerR: 0.32, schlegelRot3D: 0.60,
e8Origins: [{ x: -0.30, y: 0.10, scale: 0.42 },
{ x: 0.35, y: -0.15, scale: 0.42 }],
linkCount: 11,
fields: {
cubic: F(0.85, 0.95, 0.40, 0.45, 0.45, 0.6), // very thin
schlegel: F(0.05, 0.12, 0.55, 0.30, 0.85, 3.4), // very thick
e8: F(0.05, 0.15, 0.85, 0.55, 0.92, 2.0), // medium
links: F(0.50, 0.10, 0.95, 0.55, 0.95, 2.8), // thick links
},
},
},
];
const base = paramsFromSeed(BASE_SEED);
for (const v of variations) {
const params = { ...base, ...v.overrides };
if (v.overrides.fields) params.fields = v.overrides.fields;
const svg = renderQFTSVG(generateQFTScene(params), params, SIZE);
const path = `${OUT_DIR}/${v.name}.svg`;
writeFileSync(path, svg);
console.log(`ok ${v.name}`);
}
console.log(`\nrendered ${variations.length} variations → ${OUT_DIR}/`);

View File

@@ -0,0 +1,466 @@
/* ============================================================
qft-variations-wild.mjs — sketch 09 · running wild.
New mechanics this sketch:
- VORTEX perturbations (swirling rotation around a centre)
- STANDING WAVES (global plane-wave modulation; Chladni)
- CURVED Feynman links (bezier-curved propagators)
Plus everything earlier: wavepackets, ripples, dense cubic,
per-field weights, mid-tone substrates.
============================================================ */
import { writeFileSync, mkdirSync } from 'node:fs';
import { generateQFTScene } from '../src/qft/scene.js';
import { paramsFromSeed } from '../src/qft/params.js';
import { renderQFTSVG } from '../src/qft/renderer.js';
const BASE_SEED = 'FEYNMAN-7167';
const OUT_DIR = 'output/qft/sketch09';
const SIZE = 1800;
mkdirSync(OUT_DIR, { recursive: true });
const F = (hueStart, hueEnd, saturation, lightness, opacity, stroke) => {
const f = { hueStart, hueEnd, saturation, lightness, opacity };
if (stroke != null) f.stroke = stroke;
return f;
};
const paper = (flat, glowInDelta = [16, 14, 12], glowOutDelta = [-22, -20, -18]) => ({
flat,
glowIn: [flat[0] + glowInDelta[0], flat[1] + glowInDelta[1], flat[2] + glowInDelta[2]],
glowOut: [flat[0] + glowOutDelta[0], flat[1] + glowOutDelta[1], flat[2] + glowOutDelta[2]],
});
const W = (x, y, amplitude, sigma) => ({ x, y, amplitude, sigma });
const VX = (x, y, strength, sigma) => ({ x, y, strength, sigma }); // vortex
const SW = (kx, ky, amplitude, phase = 0) => ({ kx, ky, amplitude, phase });
const R = (x, y, count = 6, r0 = 0.06, dR = 0.09, propagator = 'photon') =>
({ x, y, count, r0, dR, propagator });
const BASE = {
cubicN: 2,
photonCyclesPerUnit: 14,
segmentsPerEdge: 8,
linkCount: 10,
linkCurvature: 0.0, // overridden per-variation
e8Style: 'nautilus',
nautilusTurns: 2.4, nautilusPerTurn: 14, nautilusGrowth: 0.22,
};
const variations = [
// ─── VORTEX block ───────────────────────────────────────────────────────────
{
name: '01_single-vortex-cubic',
label: 'Single vortex · cubic mesh swirls around a centred angular-momentum core',
overrides: {
...BASE,
paperOverride: paper([175, 175, 195]),
vignOverride: [55, 55, 70], featureOverride: [40, 40, 60],
cubicScale: 1.55, schlegelScale: 1.30,
schlegelInnerR: 0.42, schlegelRot3D: 0.45,
vortices: [VX(0, 0, 1.20, 0.22)],
e8Origins: [], linkCount: 6, linkCurvature: 0.25,
fields: {
cubic: F(0.55, 0.68, 0.50, 0.40, 0.55, 1.0),
schlegel: F(0.58, 0.72, 0.55, 0.36, 0.70, 1.6),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.05, 0.13, 0.90, 0.55, 0.92, 2.2),
},
},
},
{
name: '02_dipole-counter-vortices',
label: 'Counter-rotating vortices · dipole · Kármán-street feel',
overrides: {
...BASE,
paperOverride: paper([175, 175, 165]),
vignOverride: [55, 55, 45], featureOverride: [45, 45, 35],
cubicScale: 1.55, schlegelScale: 1.35,
schlegelInnerR: 0.40, schlegelRot3D: 0.50,
vortices: [VX(-0.30, 0, 1.20, 0.18), VX(0.30, 0, -1.20, 0.18)],
e8Origins: [], linkCount: 8, linkCurvature: 0.40,
fields: {
cubic: F(0.05, 0.18, 0.55, 0.40, 0.55, 1.0),
schlegel: F(0.08, 0.18, 0.55, 0.36, 0.72, 1.8),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.55, 0.08, 0.92, 0.55, 0.95, 2.4),
},
},
},
{
name: '03_vortex-with-spinning-ripples',
label: 'Vortex + ripples at same centre · spinning waves',
overrides: {
...BASE,
paperOverride: paper([180, 175, 165]),
vignOverride: [60, 55, 45], featureOverride: [50, 45, 35],
cubicScale: 1.55, schlegelScale: 1.30,
vortices: [VX(0, 0, 0.85, 0.30)],
ripples: [R(0, 0, 5, 0.10, 0.10)],
e8Origins: [], linkCount: 8, linkCurvature: 0.35,
fields: {
cubic: F(0.08, 0.18, 0.55, 0.40, 0.50, 1.0),
schlegel: F(0.06, 0.16, 0.55, 0.36, 0.70, 1.6),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0.05, 0.18, 0.95, 0.55, 0.92, 2.4),
links: F(0.95, 0.08, 0.95, 0.55, 0.95, 2.4),
},
},
},
{
name: '04_vortex-particle-spin',
label: 'Vortex + wavepacket at same centre · particle with spin',
overrides: {
...BASE,
paperOverride: paper([170, 170, 190]),
vignOverride: [50, 50, 70], featureOverride: [40, 40, 60],
cubicScale: 1.55, schlegelScale: 1.30,
schlegelInnerR: 0.40, schlegelRot3D: 0.50,
vortices: [VX(0, 0, 0.95, 0.20)],
wavepackets: [W(0, 0, 0.060, 0.16)],
e8Origins: [], linkCount: 7, linkCurvature: 0.35,
fields: {
cubic: F(0.55, 0.72, 0.55, 0.40, 0.55, 1.0),
schlegel: F(0.58, 0.78, 0.60, 0.34, 0.78, 1.8),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.05, 0.16, 0.95, 0.55, 0.95, 2.4),
},
},
},
// ─── STANDING WAVE block ────────────────────────────────────────────────────
{
name: '05_horizontal-standing-wave',
label: 'Horizontal standing wave · cubic ripples like a drum membrane',
overrides: {
...BASE,
paperOverride: paper([180, 175, 195]),
vignOverride: [55, 50, 70], featureOverride: [40, 40, 55],
cubicScale: 1.55, schlegelScale: 1.30,
schlegelInnerR: 0.40, schlegelRot3D: 0.45,
standingWaves: [SW(7.0, 0.0, 0.045)],
e8Origins: [], linkCount: 7, linkCurvature: 0.20,
fields: {
cubic: F(0.55, 0.62, 0.50, 0.42, 0.55, 1.0),
schlegel: F(0.58, 0.68, 0.55, 0.38, 0.72, 1.8),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.06, 0.16, 0.95, 0.55, 0.95, 2.2),
},
},
},
{
name: '06_chladni-interference',
label: 'Two crossed standing waves · Chladni-plate interference',
overrides: {
...BASE,
paperOverride: paper([175, 175, 165]),
vignOverride: [55, 50, 45], featureOverride: [45, 45, 35],
cubicScale: 1.55, schlegelScale: 1.30,
schlegelInnerR: 0.40, schlegelRot3D: 0.50,
standingWaves: [SW(8.0, 1.0, 0.038), SW(1.0, 8.0, 0.038)],
e8Origins: [], linkCount: 8, linkCurvature: 0.30,
fields: {
cubic: F(0.04, 0.16, 0.60, 0.38, 0.60, 1.0),
schlegel: F(0.06, 0.18, 0.60, 0.34, 0.75, 1.8),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.95, 0.06, 0.95, 0.55, 0.95, 2.4),
},
},
},
{
name: '07_standing-wave-plus-wavepacket',
label: 'Standing wave background · wavepacket on top · oscillating quantum state',
overrides: {
...BASE,
paperOverride: paper([170, 180, 165]), // sage
vignOverride: [50, 65, 45], featureOverride: [40, 55, 35],
cubicScale: 1.55, schlegelScale: 1.40,
schlegelInnerR: 0.40, schlegelRot3D: 0.50,
standingWaves: [SW(6.0, 2.0, 0.035)],
wavepackets: [W(0.10, -0.10, 0.060, 0.18)],
e8Origins: [], linkCount: 9, linkCurvature: 0.30,
fields: {
cubic: F(0.40, 0.50, 0.55, 0.38, 0.55, 1.0),
schlegel: F(0.42, 0.52, 0.60, 0.34, 0.75, 1.8),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.08, 0.16, 0.95, 0.55, 0.95, 2.4),
},
},
},
// ─── CURVED LINKS block ─────────────────────────────────────────────────────
{
name: '08_heavily-curved-links',
label: 'Heavily curved Feynman links · organic tangle',
overrides: {
...BASE,
paperOverride: paper([180, 170, 175]),
vignOverride: [55, 45, 50], featureOverride: [40, 35, 40],
cubicScale: 1.55, schlegelScale: 1.30,
schlegelInnerR: 0.42, schlegelRot3D: 0.50,
e8Origins: [{ x: -0.35, y: 0.30, scale: 0.32 },
{ x: 0.35, y: -0.30, scale: 0.32 }],
linkCount: 14, linkCurvature: 0.55,
fields: {
cubic: F(0.85, 0.96, 0.50, 0.40, 0.50, 1.0),
schlegel: F(0.88, 0.00, 0.55, 0.36, 0.70, 1.6),
e8: F(0.06, 0.16, 0.85, 0.55, 0.92, 2.0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.04, 0.14, 0.95, 0.55, 0.95, 2.6),
},
},
},
{
name: '09_curved-links-and-nautilus-chain',
label: 'Curved-link cradle around a nautilus chain · synaptic',
overrides: {
...BASE,
paperOverride: paper([170, 175, 185]),
vignOverride: [50, 55, 70], featureOverride: [40, 45, 60],
cubicScale: 1.55, schlegelScale: 1.40,
schlegelInnerR: 0.40, schlegelRot3D: 0.45,
e8Origins: [
{ x: -0.40, y: -0.30, scale: 0.32 },
{ x: 0.00, y: 0.00, scale: 0.32 },
{ x: 0.40, y: 0.30, scale: 0.32 },
],
linkCount: 14, linkCurvature: 0.50,
fields: {
cubic: F(0.55, 0.68, 0.45, 0.40, 0.50, 1.0),
schlegel: F(0.58, 0.70, 0.55, 0.36, 0.72, 1.6),
e8: F(0.05, 0.18, 0.85, 0.55, 0.92, 2.2),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.06, 0.16, 0.95, 0.55, 0.95, 2.4),
},
},
},
// ─── MULTI-MECHANIC plates (combining wildly) ───────────────────────────────
{
name: '10_full-turbulence',
label: 'Full turbulence · vortex + standing wave + ripples + curved links',
overrides: {
...BASE,
paperOverride: paper([175, 170, 175]),
vignOverride: [55, 50, 60], featureOverride: [45, 40, 50],
cubicScale: 1.60, schlegelScale: 1.45,
schlegelInnerR: 0.42, schlegelRot3D: 0.55,
vortices: [VX(-0.20, 0.20, 0.70, 0.30)],
standingWaves: [SW(5.0, 3.0, 0.025)],
ripples: [R(0.30, -0.20, 4, 0.08, 0.10)],
e8Origins: [], linkCount: 12, linkCurvature: 0.45,
fields: {
cubic: F(0.55, 0.85, 0.50, 0.40, 0.55, 0.9),
schlegel: F(0.60, 0.88, 0.55, 0.36, 0.72, 1.6),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0.50, 0.80, 0.95, 0.55, 0.90, 2.2),
links: F(0.95, 0.06, 0.95, 0.55, 0.95, 2.6),
},
},
},
{
name: '11_molecular-vibration',
label: 'Molecular vibration · two wavepackets + standing wave + curved bonds',
overrides: {
...BASE,
paperOverride: paper([185, 175, 175]),
vignOverride: [60, 50, 55], featureOverride: [45, 40, 45],
cubicScale: 1.55, schlegelScale: 1.30,
schlegelInnerR: 0.40, schlegelRot3D: 0.50,
wavepackets: [W(-0.30, 0, 0.075, 0.16), W(0.30, 0, 0.075, 0.16)],
standingWaves: [SW(6.0, 0.0, 0.028)],
e8Origins: [], linkCount: 14, linkCurvature: 0.55,
fields: {
cubic: F(0.85, 0.00, 0.50, 0.42, 0.55, 1.0),
schlegel: F(0.88, 0.05, 0.55, 0.38, 0.72, 1.6),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.05, 0.16, 0.95, 0.55, 0.95, 2.6),
},
},
},
{
name: '12_orbital-with-spin',
label: 'Orbital with spin · vortex + ripples + nautilus, curved links',
overrides: {
...BASE,
paperOverride: paper([175, 165, 145]),
vignOverride: [65, 50, 30], featureOverride: [55, 45, 30],
cubicScale: 1.55, schlegelScale: 1.35,
schlegelInnerR: 0.38, schlegelRot3D: 0.50,
vortices: [VX(0, 0, 0.80, 0.30)],
ripples: [R(0, 0, 5, 0.12, 0.12)],
e8Origins: [{ x: 0, y: 0, scale: 0.60 }],
linkCount: 11, linkCurvature: 0.40,
fields: {
cubic: F(0.06, 0.14, 0.55, 0.38, 0.55, 1.0),
schlegel: F(0.04, 0.13, 0.60, 0.34, 0.75, 1.8),
e8: F(0.10, 0.18, 0.85, 0.55, 0.92, 2.0),
ripple: F(0.10, 0.04, 0.95, 0.55, 0.92, 2.4),
links: F(0.50, 0.10, 0.95, 0.55, 0.95, 2.6),
},
},
},
{
name: '13_field-gradient-shear',
label: 'Field gradient · a single SLOW standing wave deforming the whole canvas',
overrides: {
...BASE,
paperOverride: paper([170, 175, 195]),
vignOverride: [50, 55, 70], featureOverride: [40, 45, 60],
cubicScale: 1.60, schlegelScale: 1.30,
schlegelInnerR: 0.42, schlegelRot3D: 0.45,
standingWaves: [SW(1.8, 0.0, 0.080)], // single low-frequency big amp = gradient-like shear
e8Origins: [{ x: -0.40, y: 0, scale: 0.32 },
{ x: 0.40, y: 0, scale: 0.32 }],
linkCount: 10, linkCurvature: 0.40,
fields: {
cubic: F(0.55, 0.68, 0.50, 0.42, 0.55, 1.0),
schlegel: F(0.58, 0.72, 0.55, 0.36, 0.70, 1.8),
e8: F(0.08, 0.16, 0.85, 0.55, 0.92, 2.0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.05, 0.16, 0.95, 0.55, 0.95, 2.6),
},
},
},
{
name: '14_multi-vortex-chaos',
label: 'Multi-vortex chaos · 4 spinning centres + dense curved-link tangle',
overrides: {
...BASE,
paperOverride: paper([175, 175, 165]),
vignOverride: [55, 55, 45], featureOverride: [45, 45, 35],
cubicScale: 1.55, schlegelScale: 1.30,
schlegelInnerR: 0.42, schlegelRot3D: 0.50,
vortices: [
VX(-0.30, -0.30, 0.85, 0.18),
VX( 0.30, -0.30, -0.85, 0.18),
VX(-0.30, 0.30, -0.85, 0.18),
VX( 0.30, 0.30, 0.85, 0.18),
],
e8Origins: [], linkCount: 13, linkCurvature: 0.50,
fields: {
cubic: F(0.06, 0.16, 0.55, 0.40, 0.55, 1.0),
schlegel: F(0.04, 0.14, 0.55, 0.36, 0.72, 1.8),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.95, 0.06, 0.95, 0.55, 0.95, 2.6),
},
},
},
{
name: '15_perturbation-cascade',
label: 'Perturbation cascade · 3 wavepackets at different scales + ripples + curved links',
overrides: {
...BASE,
paperOverride: paper([180, 175, 165]),
vignOverride: [60, 55, 45], featureOverride: [50, 45, 35],
cubicScale: 1.55, schlegelScale: 1.40,
schlegelInnerR: 0.40, schlegelRot3D: 0.50,
wavepackets: [
W(-0.45, 0.30, 0.040, 0.10),
W(-0.05, -0.05, 0.075, 0.18),
W( 0.40, -0.30, 0.060, 0.14),
],
ripples: [R(-0.45, 0.30, 3, 0.05, 0.06),
R(-0.05, -0.05, 5, 0.07, 0.09),
R(0.40, -0.30, 4, 0.06, 0.08)],
e8Origins: [], linkCount: 12, linkCurvature: 0.40,
fields: {
cubic: F(0.06, 0.16, 0.55, 0.40, 0.55, 1.0),
schlegel: F(0.04, 0.14, 0.55, 0.36, 0.72, 1.8),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0.10, 0.05, 0.95, 0.55, 0.92, 2.4),
links: F(0.95, 0.08, 0.95, 0.55, 0.95, 2.6),
},
},
},
// ─── BOLD experiments ───────────────────────────────────────────────────────
{
name: '16_asymmetric-riot',
label: 'Asymmetric riot · everything happening everywhere · one mind-storm plate',
overrides: {
...BASE,
paperOverride: paper([175, 170, 175]),
vignOverride: [55, 50, 60], featureOverride: [45, 40, 50],
cubicScale: 1.65, cubicOriginX: -0.05,
schlegelScale: 1.55, schlegelOriginX: 0.10,
schlegelInnerR: 0.42, schlegelRot3D: 0.60,
vortices: [VX(-0.30, 0.20, 0.95, 0.20)],
wavepackets: [W(0.25, -0.25, 0.075, 0.16)],
ripples: [R(-0.30, 0.20, 4, 0.07, 0.08),
R(0.25, -0.25, 5, 0.05, 0.08),
R(0.0, 0.0, 3, 0.10, 0.10)],
standingWaves: [SW(4.0, 1.5, 0.025)],
e8Origins: [{ x: 0.45, y: 0.40, scale: 0.30 },
{ x: -0.45, y: -0.40, scale: 0.30 }],
linkCount: 14, linkCurvature: 0.55,
fields: {
cubic: F(0.55, 0.85, 0.55, 0.40, 0.55, 0.9),
schlegel: F(0.60, 0.88, 0.55, 0.36, 0.75, 1.8),
e8: F(0.05, 0.16, 0.85, 0.55, 0.92, 2.2),
ripple: F(0.50, 0.85, 0.95, 0.55, 0.92, 2.2),
links: F(0.95, 0.10, 0.95, 0.55, 0.95, 2.6),
},
},
},
{
name: '17_spectral-vortex-storm',
label: 'Spectral vortex storm · radial gradient · full spectrum in every element',
overrides: {
...BASE,
paperOverride: paper([170, 170, 175]),
vignOverride: [55, 50, 55], featureOverride: [40, 40, 45],
gradientMode: 'radial',
cubicScale: 1.55, schlegelScale: 1.40,
schlegelInnerR: 0.40, schlegelRot3D: 0.50,
vortices: [VX(0, 0, 1.10, 0.30)],
ripples: [R(0, 0, 5, 0.13, 0.10)],
e8Origins: [], linkCount: 12, linkCurvature: 0.45,
fields: {
cubic: F(0.55, 0.05, 0.70, 0.42, 0.55, 1.0),
schlegel: F(0.55, 0.05, 0.60, 0.36, 0.72, 1.8),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0.55, 0.05, 0.95, 0.55, 0.92, 2.4),
links: F(0.55, 0.05, 0.95, 0.55, 0.95, 2.6),
},
},
},
{
name: '18_quiet-single-vortex',
label: 'Quiet · single small vortex + few curved links · contemplative counter',
overrides: {
...BASE,
paperOverride: paper([195, 188, 178]),
vignOverride: [55, 50, 45], featureOverride: [50, 45, 40],
cubicScale: 1.40, schlegelScale: 1.20,
schlegelInnerR: 0.40, schlegelRot3D: 0.45,
vortices: [VX(0, 0, 0.50, 0.25)],
e8Origins: [], linkCount: 4, linkCurvature: 0.40,
fields: {
cubic: F(0.60, 0.68, 0.30, 0.42, 0.40, 0.9),
schlegel: F(0.62, 0.70, 0.40, 0.40, 0.55, 1.4),
e8: F(0, 0, 0, 0.5, 0),
ripple: F(0, 0, 0, 0.5, 0),
links: F(0.06, 0.14, 0.80, 0.50, 0.85, 1.8),
},
},
},
];
const base = paramsFromSeed(BASE_SEED);
for (const v of variations) {
const params = { ...base, ...v.overrides };
if (v.overrides.fields) params.fields = v.overrides.fields;
const svg = renderQFTSVG(generateQFTScene(params), params, SIZE);
const path = `${OUT_DIR}/${v.name}.svg`;
writeFileSync(path, svg);
console.log(`ok ${v.name}`);
}
console.log(`\nrendered ${variations.length} variations → ${OUT_DIR}/`);

169
tools/qft-variations.mjs Normal file
View File

@@ -0,0 +1,169 @@
/* ============================================================
qft-variations.mjs — render a curated set of variations.
Single base seed (geometry held constant); each variation
overrides per-field hue / sat / light / opacity + gradient mode
+ substrate (or arbitrary paperOverride RGB) + cross-field
link colours.
Output → output/qft/sketch02/
============================================================ */
import { writeFileSync, mkdirSync } from 'node:fs';
import { generateQFTScene } from '../src/qft/scene.js';
import { paramsFromSeed } from '../src/qft/params.js';
import { renderQFTSVG } from '../src/qft/renderer.js';
const BASE_SEED = 'FEYNMAN-7167';
const OUT_DIR = 'output/qft/sketch02';
const SIZE = 1800;
mkdirSync(OUT_DIR, { recursive: true });
const F = (hueStart, hueEnd, saturation, lightness, opacity) =>
({ hueStart, hueEnd, saturation, lightness, opacity });
const variations = [
{
name: '01_anchor-vintage', label: 'Anchor · warm vintage muted',
overrides: {
substrate: 'vintage', gradientMode: 'along-edge',
fields: {
cubic: F(0.08, 0.05, 0.30, 0.52, 0.65),
schlegel: F(0.10, 0.10, 0.20, 0.48, 0.55),
e8: F(0.10, 0.16, 0.65, 0.60, 0.92),
links: F(0.10, 0.04, 0.90, 0.62, 0.95), // bright amber
},
},
},
{
name: '02_ember-triad', label: 'Ember triad · warm fire band',
overrides: {
substrate: 'vintage', gradientMode: 'along-edge',
fields: {
cubic: F(0.04, 0.10, 0.65, 0.55, 0.75),
schlegel: F(0.96, 0.03, 0.55, 0.50, 0.65),
e8: F(0.12, 0.18, 0.85, 0.62, 0.90),
links: F(0.02, 0.10, 0.95, 0.62, 0.95), // deep red→orange
},
},
},
{
name: '03_aurora-void', label: 'Aurora · cool spectral on void',
overrides: {
substrate: 'void', gradientMode: 'along-edge', vign: 0.55,
fields: {
cubic: F(0.50, 0.72, 0.70, 0.60, 0.85),
schlegel: F(0.40, 0.55, 0.55, 0.58, 0.75),
e8: F(0.78, 0.92, 0.85, 0.65, 0.90),
links: F(0.55, 0.85, 0.95, 0.70, 0.95), // cyan → violet bright
},
},
},
{
name: '04_spectrum-cream', label: 'Spectrum · one rainbow field on cream',
overrides: {
substrate: 'cream', gradientMode: 'along-edge',
fields: {
cubic: F(0.00, 0.95, 0.80, 0.48, 0.80),
schlegel: F(0.0, 0.0, 0.05, 0.30, 0.50),
e8: F(0.0, 0.16, 0.55, 0.42, 0.75),
links: F(0.55, 0.05, 0.85, 0.45, 0.95), // cyan → red — spans the spectrum too
},
},
},
{
name: '05_verdigris', label: 'Verdigris · oxidised copper / patina',
overrides: {
substrate: 'vintage', gradientMode: 'along-edge',
fields: {
cubic: F(0.42, 0.50, 0.50, 0.55, 0.75),
schlegel: F(0.06, 0.02, 0.45, 0.45, 0.65),
e8: F(0.32, 0.45, 0.55, 0.55, 0.85),
links: F(0.04, 0.10, 0.85, 0.58, 0.95), // copper highlights
},
},
},
{
name: '06_radial-sunrise', label: 'Radial · cool centre → warm edges',
overrides: {
substrate: 'vintage', gradientMode: 'radial',
fields: {
cubic: F(0.55, 0.06, 0.60, 0.55, 0.78),
schlegel: F(0.55, 0.06, 0.50, 0.55, 0.65),
e8: F(0.55, 0.06, 0.70, 0.60, 0.88),
links: F(0.55, 0.06, 0.90, 0.62, 0.95), // shares the radial gradient
},
},
},
{
name: '07_magenta-family', label: 'Magenta family on cream',
overrides: {
substrate: 'cream', gradientMode: 'along-edge',
fields: {
cubic: F(0.85, 0.93, 0.50, 0.45, 0.75),
schlegel: F(0.78, 0.88, 0.42, 0.42, 0.65),
e8: F(0.92, 0.00, 0.60, 0.48, 0.88),
links: F(0.92, 0.78, 0.85, 0.40, 0.95), // saturated pink → purple
},
},
},
{
name: '08_per-field-mono', label: 'Per-field monochrome',
overrides: {
substrate: 'vintage', gradientMode: 'along-edge',
fields: {
cubic: F(0.55, 0.55, 0.55, 0.55, 0.70),
schlegel: F(0.88, 0.88, 0.55, 0.55, 0.70),
e8: F(0.12, 0.12, 0.75, 0.60, 0.92),
links: F(0.34, 0.34, 0.80, 0.55, 0.95), // mono green accent — a 4th distinct hue
},
},
},
{
name: '09_cyanotype', label: 'Cyanotype · Anna Atkins',
overrides: {
substrate: 'cyanotype', gradientMode: 'along-edge',
fields: {
cubic: F(0.55, 0.50, 0.30, 0.80, 0.85),
schlegel: F(0.55, 0.55, 0.20, 0.85, 0.75),
e8: F(0.48, 0.42, 0.50, 0.78, 0.92),
links: F(0.55, 0.50, 0.30, 0.92, 0.95), // near-white connectors
},
},
},
{
name: '10_single-bright-void', label: 'Single bright · E8 rainbow on void',
overrides: {
substrate: 'void', gradientMode: 'along-edge', vign: 0.55,
fields: {
cubic: F(0.0, 0.0, 0.05, 0.40, 0.45),
schlegel: F(0.0, 0.0, 0.05, 0.42, 0.40),
e8: F(0.0, 0.95, 0.95, 0.62, 0.98),
links: F(0.0, 0.95, 0.95, 0.70, 0.98), // bright rainbow connectors
},
},
},
{
name: '11_mauve-fog-custom', label: 'Bonus · custom mauve paper',
overrides: {
gradientMode: 'along-edge',
paperOverride: { flat: [82, 76, 92], glowIn: [108, 98, 118], glowOut: [58, 52, 70] },
vignOverride: [22, 18, 28],
featureOverride: [200, 190, 215],
fields: {
cubic: F(0.00, 0.95, 0.50, 0.72, 0.80),
schlegel: F(0.0, 0.0, 0.05, 0.85, 0.55),
e8: F(0.55, 0.85, 0.65, 0.72, 0.92),
links: F(0.55, 0.85, 0.85, 0.78, 0.95), // pastel cyan → violet
},
},
},
];
const base = paramsFromSeed(BASE_SEED);
for (const v of variations) {
const params = { ...base, ...v.overrides };
if (v.overrides.fields) params.fields = v.overrides.fields;
const svg = renderQFTSVG(generateQFTScene(params), params, SIZE);
const path = `${OUT_DIR}/${v.name}.svg`;
writeFileSync(path, svg);
console.log(`ok ${v.name} · ${v.label}`);
}
console.log(`\nrendered ${variations.length} variations → ${OUT_DIR}/`);

32
tools/render-qft-svg.mjs Normal file
View File

@@ -0,0 +1,32 @@
/* Render one QFT plate to SVG from the CLI.
Usage:
node tools/render-qft-svg.mjs --seed SEED [out.svg] [size] [k=v ...]
Modular sibling of tools/render-svg.mjs and render-milkweed-svg.mjs.
*/
import { writeFileSync } from 'node:fs';
import { generateQFTScene } from '../src/qft/scene.js';
import { paramsFromSeed } from '../src/qft/params.js';
import { renderQFTSVG } from '../src/qft/renderer.js';
const argv = process.argv.slice(2);
if (argv[0] !== '--seed') {
console.error('usage: node tools/render-qft-svg.mjs --seed SEED [out.svg] [size] [k=v ...]');
process.exit(1);
}
const seed = argv[1] || 'QFT-001';
const out = argv[2] || `/tmp/qft-${seed}.svg`;
let size = 4800;
if (argv[3] && !argv[3].includes('=')) size = +argv[3];
const params = paramsFromSeed(seed);
for (const a of argv) {
if (!a.includes('=')) continue;
const [k, ...rest] = a.split('=');
const v = rest.join('=');
params[k] = v === 'true' ? true : v === 'false' ? false
: (v.trim() !== '' && !isNaN(+v) ? +v : v);
}
const svg = renderQFTSVG(generateQFTScene(params), params, size);
writeFileSync(out, svg);
console.log(`QFT SVG -> ${out} (${(svg.length / 1024).toFixed(0)} KB, archetype=${params.archetype})`);