app
Circuit Trace Generators
Public Made by Adomby adom
Two dependency-free, seeded circuit-background generators: a seamless torus tile with ambient or progressive reveal, and the v9 continuous field with obstacles, scroll reveal, and five ambient systems.
master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
// Runs the REAL engine against a recording canvas on a synthetic clock, and
// asks the only question that matters for "popping": on how many frames is a
// route visible while it is still incomplete? A route that only ever shows up
// at full length popped, however good its schedule was.
//
// usage: node checks/frames.mjs [engine.js] (default: the reveal engine)
import { copyFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const here = dirname(fileURLToPath(import.meta.url));
const source = process.argv[2] || join(here, "..", "circuit-background-reveal.js");
// Node reads .js as CommonJS without a package.json, so import a .mjs copy.
const enginePath = join(tmpdir(), `reveal-engine-${process.pid}.mjs`);
copyFileSync(source, enginePath);
const layers = new Map(); // canvas -> name
let nextId = 0;
// Every stroke/fill is recorded against the canvas it landed on. Path lengths
// are accumulated so we can compare a partial route with a complete route.
function makeContext(canvas) {
const ctx = {
canvas,
_path: [],
_len: 0,
globalAlpha: 1,
globalCompositeOperation: "source-over",
strokeStyle: "#000",
fillStyle: "#000",
lineWidth: 1,
lineCap: "butt",
lineJoin: "miter",
setTransform() {},
save() {},
restore() {},
clearRect() {
canvas.ops.push({ op: "clear" });
},
fillRect() {},
beginPath() {
ctx._path = [];
ctx._len = 0;
},
moveTo(x, y) {
ctx._path = [[x, y]];
},
lineTo(x, y) {
const p = ctx._path[ctx._path.length - 1];
if (p) ctx._len += Math.hypot(x - p[0], y - p[1]);
ctx._path.push([x, y]);
},
arc() {
ctx._path.push(["arc"]);
},
stroke() {
if (ctx._len > 0) canvas.ops.push({ op: "stroke", len: ctx._len, alpha: ctx.globalAlpha });
},
fill() {
canvas.ops.push({ op: "fill", alpha: ctx.globalAlpha });
},
drawImage(src) {
canvas.ops.push({ op: "draw", from: layers.get(src) || "?", alpha: ctx.globalAlpha });
},
};
return ctx;
}
function makeCanvas() {
const canvas = {
_id: nextId++,
width: 0,
height: 0,
style: {},
ops: [],
getContext: () => canvas._ctx,
};
canvas._ctx = makeContext(canvas);
return canvas;
}
const noopEl = () => {
const el = {
style: {},
children: [],
dataset: {},
setAttribute() {},
getAttribute: () => null,
removeAttribute() {},
appendChild(c) {
el.children.push(c);
return c;
},
append(...c) {
el.children.push(...c);
},
insertBefore(c) {
el.children.unshift(c);
return c;
},
get firstChild() {
return el.children[0] || null;
},
prepend(c) {
el.children.unshift(c);
return c;
},
removeChild() {},
remove() {},
replaceChildren() {
el.children = [];
},
addEventListener() {},
removeEventListener() {},
querySelector: () => null,
querySelectorAll: () => [],
getBoundingClientRect: () => ({ left: 0, top: 0, width: 1400, height: 900 }),
clientWidth: 1400,
clientHeight: 900,
cloneNode: () => noopEl(),
contains: () => false,
};
return el;
};
let clock = 0;
const rafQueue = [];
globalThis.performance = { now: () => clock };
globalThis.ResizeObserver = class {
observe() {}
disconnect() {}
};
globalThis.document = {
hidden: false,
createElement(tag) {
if (tag === "canvas") {
const c = makeCanvas();
layers.set(c, `canvas#${c._id}`);
return c;
}
return noopEl();
},
createElementNS: () => noopEl(),
addEventListener() {},
removeEventListener() {},
};
globalThis.window = {
devicePixelRatio: 1,
requestAnimationFrame(fn) {
rafQueue.push(fn);
return rafQueue.length;
},
cancelAnimationFrame() {},
setTimeout: () => 0,
clearTimeout() {},
setInterval: () => 0,
clearInterval() {},
matchMedia: () => ({ matches: false, addEventListener() {}, removeEventListener() {} }),
getComputedStyle: () => ({ getPropertyValue: () => "#0d1117" }),
};
globalThis.getComputedStyle = globalThis.window.getComputedStyle;
globalThis.devicePixelRatio = 1;
globalThis.Element = class Element {};
globalThis.SVGElement = class SVGElement extends Element {};
const { createCircuitBackground } = await import(enginePath);
const mount = Object.assign(new globalThis.Element(), noopEl());
const circuit = createCircuitBackground(mount, { seed: "nitrogen", reveal: "flood", animate: false, ...(process.env.COOL ? { revealCool: Number(process.env.COOL) } : {}) });
// The visible canvas is the one the engine prepends to the mount.
const visible = mount.children.find((c) => c.ops);
layers.set(visible, "VISIBLE");
// Name the offscreen layers by the order the engine creates them:
// settled, heat A, heat B, tip (see ensureRevealCanvas)
const offscreen = [...layers.keys()].filter((c) => c !== visible && c.ops);
const names = ["settled", "heatA", "heatB", "tip"];
offscreen.slice(-4).forEach((c, i) => layers.set(c, names[i] || `extra${i}`));
const FRAME = 16.7;
const seen = new Map(); // route -> {partial, whole}
let frames = 0;
let compositedFrom = new Set();
while (rafQueue.length && frames < 700) {
const batch = rafQueue.splice(0, rafQueue.length);
clock += FRAME;
for (const c of layers.keys()) c.ops.length = 0;
for (const fn of batch) fn(clock);
frames += 1;
// which layers actually reached the screen this frame?
const drawn = new Set(visible.ops.filter((o) => o.op === "draw" && o.alpha > 0.001).map((o) => o.from));
drawn.forEach((d) => compositedFrom.add(d));
// stroke length that reached the screen, split by layer
for (const [canvas, name] of layers) {
if (canvas === visible || !drawn.has(name)) continue;
const len = canvas.ops.filter((o) => o.op === "stroke").reduce((a, o) => a + o.len, 0);
const rec = seen.get(name) || { frames: 0, len: 0 };
if (len > 0) rec.frames += 1;
rec.len += len;
seen.set(name, rec);
}
}
console.log(`engine: ${source}`);
console.log(`frames simulated: ${frames}`);
console.log(`layers ever composited to the visible canvas: ${[...compositedFrom].sort().join(", ") || "(none)"}`);
const never = names.filter((n) => !compositedFrom.has(n));
console.log(`layers drawn into but NEVER composited: ${never.join(", ") || "(none)"}`);
for (const [name, rec] of seen) {
console.log(` ${name}: painted on ${rec.frames} frames, ${Math.round(rec.len)}px of stroke reached the screen`);
}