How this page works
The background of my homepage is not a particle effect. It is a network of Hodgkin-Huxley neurons integrating four coupled differential equations per cell per frame, wiring and unwiring themselves according to spike timing. The scheduler widget further down is a working backfill loop with a real pending queue. Both run in the browser, and neither costs anything at first paint.
That last part is the interesting constraint, so this is a note about how you put a physics simulation on a page that still has to load in under a second and a half on 4G.
The backdrop is a connectome
Each node is a point neuron running the 1952 Hodgkin-Huxley equations — the model that won Hodgkin and Huxley the Nobel Prize for explaining how the action potential actually works. Membrane voltage evolves as a balance of sodium, potassium and leak currents:
C dV/dt = I_ext - g_Na m³h (V - E_Na)
- g_K n⁴ (V - E_K)
- g_L (V - E_L)
The m, h and n terms are gating variables, each
with its own voltage-dependent rate equation. Forward Euler at dt = 0.05,
six substeps a frame. The threshold behaviour is not scripted — a neuron fires when the
sodium conductance runs away, and the refractory period falls out of the
h gate being slow to recover. You get the biology for free because the
biology is what is being solved.
Three more layers sit on top:
-
Spike-timing dependent plasticity (Bi & Poo, 1998). When an
action potential arrives at a synapse, the weight change depends on the sign of the
interval between the pre- and post-synaptic spikes:
Δw = A₊ exp(−Δt/τ₊)if the presynaptic cell fired first, and a negative term if it fired second. Causality gets rewarded, coincidence gets punished. - Structural plasticity (Holtmaat & Bhatt, 2009). Synapses are not fixed. New ones form probabilistically when two neurons both carry an elevated spike trace, weighted by distance. Weak ones that stay quiet for 180 frames get pruned. The network you see after thirty seconds is not the one that was built at load.
-
Self-attention (Vaswani et al., 2017). Every tenth frame, each
neuron's state vector
[V, m, h, n]is projected to a query, key and value, and a single-headsoftmax(QKᵀ/√d)·Vpass modulates the synaptic current. This one is an anachronism on purpose — a 2017 idea from machine learning dropped into a 1952 biophysical model, which is roughly the joke of my entire career so far.
Click anywhere on the homepage and it injects external current into nearby neurons through a Gaussian kernel. The cells near your cursor depolarize and fire.
The scheduler is a real scheduler
The widget near the top of the homepage models what I spend most of my time on. Jobs arrive with a resource request — some number of GPUs or CPU cores — and land in a pending queue. Every few ticks a backfill pass walks the queue oldest-first and places anything that fits into free slots across the partition.
The behaviour worth watching is what happens to wide jobs. A job asking for four GPUs
cannot start until four are simultaneously free, so it can sit in PENDING
while narrower jobs behind it start immediately. That is not a bug in the simulation.
That is the single most common reason a real cluster shows a full queue and idle
hardware at the same time, and most of the tuning work on a production partition is
about managing exactly that tension.
The drain button does what scontrol update nodename=… state=drain does:
the node stops accepting new work while its current jobs run to completion. It is the
first thing you reach for before maintenance, and the scheduler simply routes around
it.
Making it free
None of this is allowed to slow the page down. Someone landing here from an email has about forty seconds of patience and possibly a bad connection, and an animation that delays the text is strictly worse than no animation.
The rules I settled on:
-
Nothing animated starts before the page has painted. The backdrop
boots from
requestIdleCallbackafter theloadevent. Until the browser is idle, it does not exist. This is the difference between 500 ms of total blocking time and roughly zero. - The budget scales with the viewport. A phone runs seven neurons at four Euler substeps; a desktop runs fourteen at six. Device pixel ratio is capped at 1.5 on mobile. The simulation degrades rather than dropping frames.
-
Nothing offscreen computes. The widgets are driven by an
IntersectionObserverand are completely inert until scrolled into view. Everything stops onvisibilitychangewhen the tab goes to the background. -
Resize is debounced. Rebuilding the connectome is expensive, and
mobile browsers fire
resizeevery time the address bar hides. -
prefers-reduced-motionis honoured properly. Not slowed down — stopped. The backdrop paints exactly one static frame and never schedules an animation loop, and each widget swaps its canvas for a text summary of what it would have shown.
The whole page is a single HTML file with no build step, no framework and no third-party scripts. Inlining the CSS and JavaScript means one round trip. The largest thing on the page is a portrait photograph, and the version that ships is 18 KB.
| Metric | Mobile |
|---|---|
| First Contentful Paint | 0.9 s |
| Largest Contentful Paint | 1.2 s |
| Total Blocking Time | ~0 ms |
| Page weight | 71 KB |
There is a lesson in here that generalizes past personal websites, which is that the expensive thing is rarely the computation. It is doing the computation at the wrong time. The neuron simulation was never the problem. An oversized image in the critical path was — it was pushing Largest Contentful Paint to seventeen seconds, and no amount of optimizing the physics would have touched that.
Profile before you optimize, and be honest about which part is actually slow. It is usually not the part you find interesting.