How this site was built
One self-contained HTML file. No framework, no animation library, no stock imagery. The background is a real physics simulation you can push around with your cursor.
The background is a simulation, not a video
Most "particle backgrounds" are a looping video or a few hundred CPU-animated dots. This is neither. Particle position and velocity live in floating-point textures on the GPU. Every frame, a simulation shader reads the current state, applies forces, and writes the next state to a second texture — then the two swap. That technique is called ping-pong framebuffering, and it is what makes tens of thousands of independently-simulated particles affordable at 60fps.
A separate render pass draws each texel of that texture as a point. The particles you see are the literal contents of GPU memory, one frame old.
Three forces act on every particle
- Curl noise supplies idle motion. Taking the curl of a noise field gives a flow that is divergence-free — mathematically it has no sinks, so particles circulate forever instead of collapsing into clumps. This is why the field keeps breathing when nothing is touching it.
- Cursor repulsion parts the field. A smoothstep falloff means the edge of your influence is soft while the centre pushes firmly.
- A home spring gives every particle a remembered position, so after you disturb the field it re-forms rather than drifting away. Without it, a minute of mouse movement would leave the screen empty.
Move the cursor quickly and a fourth term kicks in: particles are dragged along your direction of travel. That term is gated on pointer speed, so a resting cursor only repels — an early version pulled particles toward the pointer whenever it stopped, which looked like a bug because it was one.
Why no imagery
An earlier version of this site used AI-generated photographic backdrops. They were beautiful individually and wrong together — you could see the seams between them, because they were separate photographs stitched into one page. Everything here is drawn instead: the particle field, the hairline rules, the timeline beam. One coherent world, no joins.
Restraint as the brief
The palette is deliberately narrow: near-black ground, warm off-white text, and a single cold blue accent used sparingly. Type is Inter for text with Instrument Serif italic carrying the emphasis — the contrast between a neutral grotesque and a high-contrast serif does the expressive work, so nothing else has to shout.
The particles are tuned to near-invisibility at rest. They brighten only where they are moving. The field should read as a material the page sits on, not as a light show.
Performance, measured rather than assumed
| Check | Result |
|---|---|
| Lighthouse, 4× throttled mobile | 100 / 100 / 100 / 100, 45 passed, 0 failed |
| Page weight | 94 KB |
| External dependencies | 0 |
| Languages, all inline | 4 |
Particle density is derived from viewport area rather than fixed, so a phone gets far fewer particles than a desktop and the perceived density stays constant. A fixed count packed into a narrow screen turned atmosphere into noise.
What happens when things are unavailable
- No float textures (older mobile GPUs) → a CPU canvas field with fewer particles, still genuinely interactive.
- No WebGL → a quiet CSS gradient; the page is fully readable.
- Reduced motion → no simulation at all, no smooth scroll, no magnetic buttons. A calm static document.
Native scroll is never hijacked, so keyboard navigation, anchor links and find-in-page all behave normally.
Four languages, and the detail most sites miss
English, French, Spanish and German ship in the same file. Number formatting follows the
locale: $23,045 becomes 23 045 $ in French and
23.045 $ in German — separator and symbol placement both change.
$1.8B keeps its symbol leading everywhere, because the magnitude letter has
to stay attached to the digits.
A note on the numbers. $23,045 is revenue from Zayaneflow's own e-commerce brand. $1.8B is Holtzbrinck's scale as a publishing group — not money produced for them. 101K is a creator's audience size, not a result. Those distinctions are encoded in the content layer so they cannot drift as the copy changes.
Built with Claude Code · hand-written GLSL · no libraries · back to the site