AtomSim
- 6 Devlogs
- 70 Total hours
A physically rigorous, deeply customizable quantum mechanical atom model and visualization platform
A physically rigorous, deeply customizable quantum mechanical atom model and visualization platform
DEVLOG : 7?
Since that last update, the cleanup turned into actual features.
I finished the frontend narrator pass. Every disclosure string, every physics note, every view intro is now plain prose instead of the app talking about itself in first person.
Then I went after the parts of the app that were just annoying to use.
Plots are zoomable now. Wheel to zoom, drag to pan, on all five analytic views. I also built a small notation layer so the ASCII the engine emits gets typeset properly at render time, subscripts and superscripts where they belong, without dragging in a whole math library.
The big one was mobile. The app used to just refuse to render below a certain width. Now it works down to 320px: a proper mobile shell, view tabs instead of a sidebar list, controls that collapse, and per-view width floors so nothing spills sideways. That took a lot of small fixes I did not expect, a fixed SVG viewBox that hid fixed-width text, and an overflow rule that was already scrolling the page sideways before I touched anything.
After that, the URL got a real history. Back and Forward actually work now, and moving between orbitals pushes a history entry while flipping a display toggle just replaces one. Added a copy-link button and keyboard shortcuts for stepping through n, l, m and switching views.
Last thing, the title is now just AtomSim. The tagline was doing nothing for anyone.
Devlog #5 or 6???
Since the site went live, most of the work has been cleanup rather than new physics.
The whole app used to talk in first person. Every explanation said “I compute this” or “what I’m showing you.” It read like the code was narrating itself, which was odd. So I went through the entire Python engine module by module and rewrote it in plain technical prose. That’s done.
Then I started on the frontend, which had way more of it. The disclosure text, the physics notes under each equation, the view intros. That’s still in progress.
there were 2 things I had to be extra careful about. Some of those strings are checked word-for-word by tests, so rewording one has potential to break complete the build by itself.
In addition, I also tidied the repo layout for public viewing, fixed a deploy pipeline failure, and added privacy-safe visitor counting.
Right now i’m cutting padding, and pitching it so a high schooler can follow it without boring a physics grad.
Phase 30, Instrument frontend. Redesigned the UI to read like a physics instrument. Bundled the fonts, dropped four fields nothing actually measured, and learned that text-transform: uppercase turns 3d into 3D and α² into A², so uppercase is now confined to section headings only.
Phase 31, Guided tours. Added walkthroughs that drive the app for you. Nearly shipped a tour that quoted the wrong hydrogen energy because it skipped reduced mass, and fixed two spotlight rings that measured their target before layout settled.
Absorption band cleanup. Abutting sub-pixel SVG rects were drawing lines that weren’t in the data. Settled the mean vs deepest-per-column question and fixed decade ticks that failed at both ends of the axis.
Housekeeping. Moved specs and plans out to docs/specs and docs/plans. Personal ignores went into .git/info/exclude so no tracked file carries tooling names.
Rate limiting. Sized the burst from the app’s real fan-out per action rather than from click counts. The n=6 row plus an auto-refetch was quietly doubling the load.
Web hosting. Containerized and deployed to Fly.io as a single process. Cold start from a snapshot is about 1.6s, occasionally up to 22s if the snapshot gets discarded. Argon solves in 2.8s on the deployed box against 1.4s locally. Suspended machines cost nothing, so occasional use stays near a dollar or two a month.
Also added rate-limiter logging that identifies refused clients, and confirmed the proxy overwrites forged IP headers, so spoofing doesn’t get you past the limit.
Phase 22 to now.
P22, distinguishable electrons. Turned off exchange in Hartree-Fock. Electrons still repel, they just stop being identical. Marked COUNTERFACTUAL. Helium came back exactly zero, so it was gone.
P24, Pauli off. Dropped the occupancy cap and everything falls into the 1s. Argon becomes 1s^18 and the periodic table stops existing. P22’s slow convergence was shells, not exchange.
P25, isosurfaces. Never size a box on the grid you draw on. The fraction error bar is blind to the box. A 90% p orbital is one piece, not two lobes.
P26, HF in 3D. Now in Cloud, Plane, Radial and Surface. Whole-atom and one-orbital radii move opposite ways. Fidelity tiers were literals under the solve.
P27, total density. Added D(r), the shape both models describe. A uniform grid steps over the 1s peak, so the test asserts convergence rate, not tolerance.
P28, screened density. GSZ gets its own D(r). A density needs its own box. A log axis quietly breaks area = electron count.
P29, comparison. Both models on one axis, gap measured. Caught a noise floor, a real number printing as zero, and a wrong caption.
Sulfur, chlorine. Split ATOM_KEYS into identity and capability. Derive flags, never pass them. A client check exposed a deep-link race.
P30, instrument frontend. Fonts bundled for offline. –edge is a border colour, never a fill. Dropped four fields with no engine behind them. Uppercase turned 3d into 3D, so headings only now.
Plot polish. Zoomed line profile labels offsets from window centre, since absolute wavelengths printed the same string six times. Drew xyz axes through the cloud.
Absorption, curve of growth. Stopped a zero equivalent width stretching the axis 300 decades. The band drew anti-aliasing seams as absorption lines where transmission was really 0.98 to 1.00. It now bins one column per unit and draws the deepest sample, beside what a real detector pixel would see.
P18, line shapes. Real lines are not infinitely thin spikes. Atoms move (Doppler) and get jostled by neighbours (pressure), so each line spreads into a bell shape, a Voigt profile. I added that, plus a zoom so you can look at one line up close. The useful trick was a flux-closure check: whatever area a line has before broadening, it has to still have after. That caught two grid bugs that were invisible on screen.
P19, optical depth. Until now every line acted as if light escaped freely. In a thick gas strong lines saturate, so doubling the atoms stops doubling the brightness. That is the curve of growth. Lesson: decide which regime a line sits in from the physics, not by eyeballing the slope. Also the wavelength window has to widen as the column thickens, or you clip the wings you are trying to measure.
P20, absorption. The mirror of emission. Shine a continuum through the gas, look at what is missing. The trap was that 14 hydrogen lines land on only 6 distinct wavelengths, so matching a profile to its source line by wavelength quietly pairs the wrong ones. I key on the transition instead.
P21, Hartree-Fock. The big one. Instead of a fitted screening formula, each electron feels a potential built from all the others, solved over and over until it stops changing. That gets me sulfur and chlorine, which the old model could not touch, plus ions with no preset. Two things worth remembering: the solver’s own error bar was lying to me and needed re-deriving, and what made convergence fast was not a clever algorithm, it was the right mixing constant.
P22, cleanup. Made features honest about where they apply. The classical ghost and the force-law lab only make sense for one-electron atoms, so they explain themselves now instead of erroring on a screened atom. Fixed He+ breaking every API call, because the “+” was never URL-encoded. Unknown systems say so instead of spinning. Right now I am fixing hint text that escapes its panel on narrow screens.
An atom simulator built on one rule: Every number has a label (exact, numerical, approximation, counterfactual, or visual_liberty). Unlabeled numbers are treated as bugs.
Stack: Python physics engine -> React + Three.js frontend.
Replaces infinitely sharp lines with realistic Voigt profiles using three combined factors:
Note on Missing Physics: Collision/pressure broadening is not simulated. Instead of a text disclaimer, the app calculates how large that missing effect would be and prints the raw margin of error right next to the chart.