From deed3a233c3e3ced578a4d089cc8588dfabbf7d3 Mon Sep 17 00:00:00 2001 From: developtheweb Date: Tue, 7 Jul 2026 21:53:01 -0400 Subject: [PATCH] feat: entropy-reversal SVG engine with SMIL animation and demon state --- assets/entropy.svg | 126 +++++++++++++++++++++++++++++++++ engine/generate_entropy_svg.py | 120 +++++++++++++++++++++++++++++++ state/demon.json | 1 + 3 files changed, 247 insertions(+) create mode 100644 assets/entropy.svg create mode 100644 engine/generate_entropy_svg.py create mode 100644 state/demon.json diff --git a/assets/entropy.svg b/assets/entropy.svg new file mode 100644 index 0000000..cf0c11c --- /dev/null +++ b/assets/entropy.svg @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +S = 100.0 k·bits +dS/dt < 0 +bits sorted by visitors: 0 + diff --git a/engine/generate_entropy_svg.py b/engine/generate_entropy_svg.py new file mode 100644 index 0000000..c053295 --- /dev/null +++ b/engine/generate_entropy_svg.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +"""Generate assets/entropy.svg — a SMIL-animated entropy-reversal field. + +120 particles loop from a seeded chaos cloud into a (2,5) torus-knot +lattice, hold, and scatter back. State (bits sorted by visitors, chaos +generation) comes from state/demon.json. Stdlib only; deterministic for +a given generation so regenerated output is diffable. +""" + +import hashlib +import json +import math +import os +import sys + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +STATE_PATH = os.path.join(ROOT, "state", "demon.json") +OUT_PATH = os.path.join(ROOT, "assets", "entropy.svg") + +WIDTH, HEIGHT = 880, 260 +MARGIN = 20 +N_PARTICLES = 120 +KNOT_R, KNOT_r = 150, 58 +DUR = "11s" +KEY_TIMES = "0;0.64;0.82;1" +KEY_SPLINES = "0.25 0.1 0.25 1;0 0 1 1;0.55 0 0.45 1" + +BG = "#010409" +BORDER = "#21262d" +CYAN = "#4dd4e8" +DEEP = "#1d6f7d" +GREEN = "#7ee2a8" +MUTED = "#8b949e" + + +def seeded_unit(index, generation, channel): + """Deterministic float in [0, 1) from particle index + generation.""" + digest = hashlib.sha256(f"{index}:{generation}:{channel}".encode()).digest() + return int.from_bytes(digest[:8], "big") / 2**64 + + +def knot_points(n): + """(2,5) torus knot sampled at n points, scaled/centered to the canvas.""" + raw = [] + for i in range(n): + t = 2 * math.pi * i / n + rad = KNOT_R + KNOT_r * math.cos(5 * t) + raw.append((rad * math.cos(2 * t), 0.62 * rad * math.sin(2 * t))) + xs, ys = [p[0] for p in raw], [p[1] for p in raw] + span_x, span_y = max(xs) - min(xs), max(ys) - min(ys) + scale = min((WIDTH - 2 * MARGIN) / span_x, (HEIGHT - 2 * MARGIN) / span_y) + cx, cy = (max(xs) + min(xs)) / 2, (max(ys) + min(ys)) / 2 + return [ + (WIDTH / 2 + (x - cx) * scale, HEIGHT / 2 + (y - cy) * scale) + for x, y in raw + ] + + +def main(): + with open(STATE_PATH) as fh: + state = json.load(fh) + bits = int(state.get("bits_sorted", 0)) + generation = int(state.get("generation", 0)) + entropy = max(12.7, 100 - 0.0002 * bits) + s_label = f"{entropy:.1f}" + + lattice = knot_points(N_PARTICLES) + parts = [ + f'', + f'', + ] + + for i, (lx, ly) in enumerate(lattice): + chaos_x = MARGIN + seeded_unit(i, generation, "x") * (WIDTH - 2 * MARGIN) + chaos_y = MARGIN + seeded_unit(i, generation, "y") * (HEIGHT - 2 * MARGIN) + fill = DEEP if seeded_unit(i, generation, "depth") < 0.3 else CYAN + cxv = f"{chaos_x:.1f};{lx:.1f};{lx:.1f};{chaos_x:.1f}" + cyv = f"{chaos_y:.1f};{ly:.1f};{ly:.1f};{chaos_y:.1f}" + parts.append( + f'' + f'' + f'' + f'' + ) + + font = 'font-family="ui-monospace,SFMono-Regular,Menlo,monospace"' + parts.append( + f'' + f'S = {s_label} k·bits' + ) + parts.append( + f'dS/dt < 0' + ) + parts.append( + f'bits sorted by visitors: {bits}' + ) + parts.append("") + + svg = "\n".join(parts) + "\n" + os.makedirs(os.path.dirname(OUT_PATH), exist_ok=True) + with open(OUT_PATH, "w") as fh: + fh.write(svg) + size = os.path.getsize(OUT_PATH) + print(f"wrote {OUT_PATH} ({size} bytes, S={s_label}, bits={bits}, gen={generation})") + if size > 200 * 1024: + print("error: SVG exceeds 200 KB budget", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/state/demon.json b/state/demon.json new file mode 100644 index 0000000..fe021d0 --- /dev/null +++ b/state/demon.json @@ -0,0 +1 @@ +{ "bits_sorted": 0, "generation": 0, "demons": 0, "last_sorted_at": null }