Back to projects
Atomic icon
FlutterFragment ShadersGLSLQuantum MechanicsGPU Rendering

Atomic

A real-time explorer for hydrogenic atomic orbitals built with Flutter, rendered entirely on the GPU with fragment shaders.

Screenshots

About this project

Atomic is a real-time explorer for hydrogenic atomic orbitals, rendered entirely on the GPU with Flutter fragment shaders. Hydrogen is one of the few quantum systems with an exact closed-form solution, and that is what makes this possible at 60fps: ψ(r, θ, φ) = R(r) · Y(θ, φ) is two polynomial recurrences and an exponential. Every pixel evaluates the wavefunction from the quantum numbers alone, with no mesh, no precomputed volume and no data files, so every parameter can be dragged live and the whole image re-renders each frame.

Four Views: The cloud integrates emission and absorption along each ray, which is the honest picture of |ψ|² as a probability density, with no surface anywhere in it. The isosurface sets its threshold by the share of the electron's probability the surface encloses rather than by a raw density, because that is the number with physical meaning, and at 0.9 it reproduces the shapes drawn in every textbook. Getting the familiar picture out as a consequence is much of the point. Nodal shells keeps only a narrow band of density around that isovalue, drawing one translucent shell per radial node, and is the only view in which the inside of a 5s orbital is visible at all. The 2D slice cuts a plane and shows signed ψ on a diverging map, where all n - l - 1 radial nodes and l angular ones can be counted at once in a way they never can in three dimensions.

Navigation by Quantum Number: The picker in the corner is the counterpart of the Mandelbrot minimap in the fractals app, because the quantum numbers are the parameter space of the orbitals. Unlike a continuum there is only a discrete lattice, so the picker draws the lattice instead of previewing the render. Rows are shells with n increasing upwards, which makes the grid an energy level diagram: every cell in one row is a state of exactly the same energy, of them, and that degeneracy is why the row exists at all.

Everything in Units of the Orbital: The hard problem here is not the wavefunction, it is dynamic range. Between 1s and 8s the bounding radius grows by a factor of about eighty and the peak of |ψ|² falls by six orders of magnitude, so no fixed camera and no fixed density slider can follow it. Three numbers are computed once per orbital: the radius enclosing all but 0.03% of the radial probability, the peak density, and the isovalue for any enclosed probability. The camera, the step length and the slice pan are all expressed as multiples of them. Switching shells keeps the framing instead of throwing the orbital off screen or losing it in a pixel.

The Physics is Pinned by Tests: A Dart reference implementation mirrors the shader function for function and is the one covered by tests, because a sign error in a shader flips a lobe's colour and still looks entirely plausible, which is the worst kind of bug to have. The tests check ψ against the hand-checkable closed forms for 1s, 2s and 2p(z), then fall back to properties that hold for every state: radial functions normalised and mutually orthogonal, harmonics normalised over the sphere, n - l - 1 radial nodes, and the expectation value of r.

Performance: A volume integral has no early-out worth the name, so interactive frames give up both samples per pixel and step count, settling back 220ms after the last pointer event. Every ray offsets its samples by a per-pixel hash, which trades the concentric rings that thin shells would otherwise alias into for fine noise that supersampling averages away. Export renders a fresh frame off-screen at a resolution independent of the window size.

Check out Atomic

View the source on GitHub