Four AI-generated museum plates, sampled into sixty thousand GPU particles each. One geometry, two target states, one crossfade uniform — and a cursor that is legally wind.
The exhibit artwork was generated with GPT Image 2 via Higgsfield, prompted as a coherent museum series — "Specimens of Light" — with one hard constraint: a single luminous subject, centered, on a pure black field. The black is not an aesthetic; it is the data format. Particles are sampled by brightness, so anything black simply doesn't exist.
No.01 moth
No.02 bloom
No.03 relic
No.04 emberdraw plate into a 240×320 offscreen canvas for each pixel: luma = .32r + .55g + .13b if luma < 0.085 → skip // jpeg-noise floor: black is not a specimen else → candidate {x, y, color, luma} fill 60,000 slots from candidates (near-uniform density, bright pixels get at most a 2× chance — structure keeps its particles, brightness is carried by color and size instead) z = (luma − .42) · 2.4 + jitter // bright pixels float forward color = pow(rgb, 1.6) · gain // gamma-deepen: darks die, brights glow
Every particle owns two target states: aT0 (where it is) and aT1 (where it's going), plus a seed. Morphing is a single uniform:
m = smoothstep(clamp(uMix·1.3 − seed·0.3)) // dim particles leave first, pos = mix(aT0, aT1, m) // bright ones arrive last pos += valueNoise(pos·0.32 + t) · sin(m·π) · 7.5 // mid-morph: the image becomes weather
When the tween lands, aT1 is copied into aT0, uMix resets to zero, and the museum is quietly ready for its next departure. The swarm needs no physics simulation and no FBO ping-pong — it is a pure function of one number, which is why it never breaks.
uDissolve: each particle departs along its own noise direction, biased upward. Fully dissolved, the exhibit advances — the plaque calls it what it is: you let it go, it became the next thing.Darkroom brown-black #0D0A07 (never pure black — pure black is where the particles live), bone #EDE3D2, copper #C88A5E for the brass of the museum. Plaques in Cormorant Garamond — small caps energy, italic captions; catalog numbers in Fragment Mono. CSS grain, a faint copper halo behind the exhibit, 420 motes of hall dust for depth. Additive blending does the glowing; there is deliberately no post-processing.
Headless framebuffer verification (manual frame stepping + readPixels ASCII maps) of all four exhibits, the swarm mid-morph, and the dissolve. Found and fixed: a jpeg-noise particle haze (raised the luma floor), washed-out palette and a gray-lifted background traced to the post-processing chain double-encoding sRGB — solved by deleting the composer entirely; additive sprites already bloom.
Per-exhibit gain so the ember's thin filaments speak at the same volume as the moth's wings; near-uniform sampling density to preserve fine structure; hall dust; exhibit-aware page titles; keyboard navigation; auto-advance ring timed at sixteen seconds.
Mobile audit (gate wordmark tracking overflowed 375px — retracked), plaque/catalog collision check, reduced-motion path, gsap lag-smoothing disabled so hidden tabs resume by completing rather than crawling, meta/OG, console-clean.
ephemera/ ├── index.html # gate, plaque, catalog, one canvas ├── css/main.css # museum chrome: plaque, dots, grain, halo ├── js/main.js # ~430 lines: sampler, shader, morphs, wind ├── assets/*.jpg # 4 plates, 716×960, <470 KB total ├── lib/ # three.module.js, gsap.min.js └── fonts/ # Cormorant Garamond + Fragment Mono # generate your own specimens (any model works): higgsfield generate create gpt_image_2 --prompt "single luminous subject, perfectly centered, PURE BLACK background …" --aspect_ratio 3:4 --wait