01 / 15  ·  hydrogendrag · orbit   scroll · zoom
binding energy
ejected0%2p0%3d0%
drive field
t = 0 as
intensity2.4e13
wavelength90 nm
a technical note

How this works

The application solves the time-dependent Schrödinger equation for a single electron bound to a proton — hydrogen — and renders the evolving probability density in real time inside a web browser. Hydrogen is the only atom whose electronic structure is exactly solvable, which makes it possible to drive the dynamics directly from the equation rather than from precomputed or tabulated data. The entire system — numerical integrator, concurrency layer, and volumetric renderer — is contained in a single HTML file with no external dependencies or build step. The only hard runtime requirement is a WebGL2 context with floating-point textures.

The central engineering problem is dimensionality. The full wavefunction is a complex field over three spatial dimensions; resolved finely enough to capture both the dense core and a departing electron, it occupies on the order of hundreds of millions of grid points, far beyond what can be stepped frame by frame in a browser. The system avoids this cost through an exact symmetry reduction rather than a coarser grid.

reduction to coupled radial channels

All driving fields in the simulation are linearly polarised along a single fixed axis. For a field along one axis, the magnetic quantum number m is conserved and remains zero throughout, so the wavefunction can be expanded in spherical-harmonic partial waves of the form ψ(r, θ) = Σl [ul(r) / r] Yl0(θ). This is an exact re-expression of the same wavefunction in a cheaper basis, not an approximation; the only quantity that approximates anything is the number of angular channels retained. The three-dimensional problem collapses to a set of coupled one-dimensional radial functions ul(r), one per angular momentum l from 0 to a chosen maximum. The single physical situation this basis cannot represent is light arriving from more than one direction at once, which is why the polarisation axis is always fixed.

spatial discretisation and the Hamiltonian

Each radial channel is discretised on a uniform grid ri = (i + 1)·dr for i = 0 … Nr − 1, with dr = rmax / Nr. The grid deliberately begins at dr rather than the origin, which keeps the Coulomb singularity off the mesh and enforces the correct u(0) = 0 boundary implicitly.

Within a channel the radial Hamiltonian is tridiagonal. The diagonal carries the kinetic term, the centrifugal barrier, and the Coulomb potential, 1/dr2 + l(l+1)/(2r2) − 1/r; the off-diagonals carry the three-point finite-difference kinetic coupling, −1/(2dr2). The external field enters in the length gauge as an interaction proportional to r·E(t)·cosθ, which couples each channel only to its immediate neighbours l ± 1. The angular coupling coefficient is the standard matrix element l / √((2l−1)(2l+1)). All quantities are expressed in Hartree atomic units; conversions to electronvolts (×27.2114) and femtoseconds (×0.0242) are applied only at the reporting boundary.

time propagation

The state is advanced by a symmetric Strang split-operator scheme. A single step factorises the evolution into a half-step of the radial Hamiltonian, a full step of the angular field coupling, and a second half-step of the radial Hamiltonian. Each of the three factors is applied with a Crank–Nicolson (Cayley) propagator of the form (1 − iA·Δ/2)·ψnew = (1 + iA·Δ/2)·ψold. This propagator is implicit, unconditionally stable, and exactly unitary in exact arithmetic, so probability is conserved to machine precision regardless of step size, and the overall split is second-order accurate in time.

The scheme is efficient because both Crank–Nicolson solves reduce to tridiagonal linear systems. The radial factor is tridiagonal along the radial index and is solved independently for each channel; the coupling factor is tridiagonal along the angular index l and is solved independently at each radial point. Both are dispatched through one complex tridiagonal (Thomas) solver, reused without modification across the radial sweep of length Nr, the angular sweep of length L, and the eigenstate relaxation described below. After the three factors, an absorbing mask is applied, completing the step.

initial states

The states the simulation begins from and projects onto — 1s, 2p, and 3d — are not hard-coded shapes; they are computed numerically for each configuration by imaginary-time propagation. Replacing real time with imaginary time turns the unitary propagator into a contraction that drives any non-orthogonal seed toward the lowest eigenstate of a given angular channel. Seeding channel l with a profile of the form rp·e−r/(l+1) and iterating the same tridiagonal Cayley step in imaginary time converges to the desired bound state; the corresponding energy is then read off as the expectation value of the Hamiltonian. This relaxation runs once per configuration and is cached, so it does not affect the per-frame cost.

boundary absorption

A departing electron must be allowed to leave the grid without reflecting off its edge and contaminating the interior. Beyond a radius of 0.82·rmax, the wavefunction is multiplied each step by a smooth mask that ramps the amplitude toward zero at the outer boundary following a cos0.125 profile. The gentle exponent suppresses reflection across a wide range of outgoing electron energies. Because absorbed amplitude is permanently removed, the loss of total norm provides a direct, physically meaningful measure of how much of the electron has been ionised and has reached the boundary.

driving fields and resolution presets

Every field is a finite pulse with a sin2 envelope that ramps on, drives, and switches off, after which the field is identically zero and the populated state evolves freely. The integrator distinguishes four field constructions: a pulse tuned to an internal energy-level difference; a detuned variant offset from that difference by a fixed factor; a single-frequency ultraviolet pulse at an arbitrary frequency; and an intense infrared pulse (defaulting to 800 nm with a fixed carrier-envelope phase). Field strength is specified either directly or as an intensity in W/cm2, converted to atomic field amplitude by E0 = √(I / 3.51×1016).

Three parameter presets trade channel count and grid extent against speed, matched to the angular momentum each regime populates. Bound-state transitions use seven channels on a 130-unit grid; single-photon ionisation, where the freed electron leaves as a near-pure p-wave, uses five; the intense infrared regime uses thirty-three, because the field drives the electron back through the core and it acquires high angular momentum on the return. Grid spacing and time step are set per preset accordingly.

spontaneous emission

Spontaneous emission is not part of the bare Schrödinger evolution and is modelled phenomenologically. During the relevant sequence, excited amplitude — all channels with l ≥ 1, together with the excited l = 0 content — is drained by a small factor each step while the ground-state component is preserved, after which the state is renormalised. The retained ground-state-to-excited coherence produces a radiating-dipole oscillation as the excited population decays, and the cloud settles to the ground configuration without any discontinuity in the displayed state.

execution model

The integrator runs on a background Web Worker thread; the main thread is reserved for the user interface, camera, and rendering. Each computed frame is transferred to the main thread as a pair of floating-point arrays holding the real and imaginary parts of every channel, accompanied by a small set of scalar observables: total norm, ionised fraction, the population that has crossed a fixed escape radius, projections onto the 1s, 2p, and 3d eigenstates, and the peak density used for display scaling.

Pacing is deterministic and decoupled from hardware speed. Each scripted segment declares a wall-clock duration; the worker emits roughly sixty frames per second across that window by computing a fixed number of integration steps per frame, easing the frame spacing outward over the final stretch so the motion glides to a stop. If a single frame's computation exceeds its time budget for long enough, the worker slows the playback and signals that the hardware cannot keep up, rather than allowing the animation to stutter unpredictably.

rendering

The renderer is a WebGL2 fragment shader performing single-pass volumetric raymarching. The channel data arrives as an Nr × L two-channel floating-point texture holding the real and imaginary radial functions, re-uploaded each frame, alongside a 256-entry colour lookup table. A single full-screen triangle drives the shader.

For each pixel, a ray is cast from the camera and intersected analytically with the bounding sphere of radius rmax; the segment inside is marched at a fixed sample count. At every sample, the density is reconstructed on the fly: the polar angle is measured from the (tilted) symmetry axis, the partial-wave sum Σl [ul(r)/r]·√((2l+1)/4π)·Pl(cosθ) is evaluated by Legendre recurrence over the channels, and its squared modulus is taken. The shader keeps the single brightest sample along each ray, a maximum-intensity projection. Because the density spans many orders of magnitude between the core and a faint outgoing wave, brightness is mapped logarithmically across a fixed number of decades below the per-frame peak density, with a gamma adjustment, before the colour lookup and a final tone-mapping pass. Rescaling to the peak present in each frame is the reason a thinning cloud appears to brighten as its dense core drains away.

— Claude Opus 4.8

photon.c6.wiki · C6 Aerospace // 29/05/2026

▌▌ paused — read on, press next to continue
⚠ throttling simulation speed — hardware seems to be struggling
C6 Aerospace // Computational Physics

Your computer is about to
solve the Schrödinger equation.

Nothing here is pre-computed. This is an actual simulation of a hydrogen atom's wavefunction in real-time. This demonstration will use it to explain the particle nature of light.

warming up the solver…
// could not start