A real-time 2D molecular dynamics simulator built in Python using pygame and numpy. Atoms of H, O, N, and He are drag-and-placed into a simulation box where Lennard-Jones forces, harmonic bond springs, Langevin thermostat noise, and angle constraints govern their motion. Bonds form and break dynamically, molecules are detected in real time via graph traversal, and the HUD displays all recognised chemical species as they appear.
chemistry_sim/
├── config.py — all constants: display, physics, elements, bonding, molecules
├── physics.py — Atom class, LJ force, spring force, Langevin thermostat, angle force, Velocity Verlet integrator
├── chemistry.py — BondManager, bond formation/breaking, molecule detection via BFS
├── renderer.py — all pygame drawing: atoms, bonds, flash effects, palette panel, HUD
└── main.py — game loop, drag-and-place state machine, event handling
Place atoms into the sim box by selecting an element from the palette, clicking and dragging — release velocity sets the initial speed and direction.
When two atoms with open valence slots come within bonding range, a harmonic spring forms between them. A flash ring marks the moment of bonding and the molecule HUD updates in real time.
Unbound atoms interact via the Lennard-Jones potential — they repel strongly at short range and attract weakly at medium range, producing realistic collision behaviour.
Non-bonded atom pairs interact via the LJ potential, which combines a short-range repulsive wall with a medium-range attractive well:
The scalar force derived from this potential is:
Mixed-pair sigma is computed via the Lorentz-Berthelot combining rule:
Forces are capped at force_cap and only computed within a cutoff radius
Bonded atom pairs are held together by a harmonic spring with equilibrium length
If the bond is stretched beyond the break threshold
Each free atom receives a drag force and a stochastic kick every step, implementing the fluctuation-dissipation theorem to maintain temperature
The balance between drag and noise keeps the system at a controlled thermal energy. Raising
To enforce molecular geometry, a restoring torque is applied to every central atom with two or more bonds. For a central atom
Forces are applied perpendicular to each bond vector, pushing the angle toward the target
| Molecule | Central atom | |
|---|---|---|
| H₂O | O | 104.5° |
| NH₃ | N | 107.0° |
The integrator is second-order accurate and conserves energy far better than Euler. Each timestep runs in the following sequence:
Forces are recomputed between the two half-steps so the velocity update uses the average of old and new accelerations.
| Element | Max bonds | Role |
|---|---|---|
| H | 1 | Forms bonds with O and N |
| O | 2 | Central atom in H₂O, H₂O₂ |
| N | 3 | Central atom in NH₃, N₂ |
| He | 0 | Noble gas — never bonds |
He is included specifically to demonstrate inert behaviour — it collides elastically with everything but never forms a bond regardless of proximity.
| Formula | Name |
|---|---|
| H₂ | Hydrogen gas |
| O₂ | Oxygen gas |
| N₂ | Nitrogen gas |
| H₂O | Water |
| H₂O₂ | Hydrogen peroxide |
| OH· | Hydroxyl radical |
| HO₂· | Hydroperoxyl radical |
| NH₃ | Ammonia |
| N₂H₄ | Hydrazine |
| NH₂· | Amino radical |
| NO· | Nitric oxide |
| NO₂· | Nitrogen dioxide |
| N₂O | Nitrous oxide |
| N₂O₃ | Dinitrogen trioxide |
| HNO₃ | Nitric acid |
| HNO₂ | Nitrous acid |
| NH₂OH | Hydroxylamine |
| HNO | Nitroxyl |
| Area | Library |
|---|---|
| Rendering | pygame 2.6.1 |
| Numerics | numpy |
| Core | Python 3.13 standard library |
Requirements: Python 3.10+, pygame, numpy
Install dependencies:
pip install pygame numpy
Run the simulator:
python main.py
A 1100×700 window opens with the sim box on the left and the element palette on the right. Select an element from the palette, click and drag into the sim box to place atoms. Space to pause, Escape to quit.
| Input | Action |
|---|---|
| Click element in palette | Select element |
| Click and drag in sim box | Place atom with velocity |
| Space | Pause / unpause |
| Escape | Quit |
All physics constants live in config.py and are the intended way to experiment:
| Constant | Effect |
|---|---|
temperature |
Controls thermal speed of atoms |
gamma |
Drag coefficient — higher values damp motion faster |
spring_k |
Bond stiffness — lower values reduce oscillation on bonding |
angle_spring_k |
Strength of geometry enforcement |
dt |
Timestep — reduce if simulation explodes |
epsilon |
LJ well depth — higher values strengthen repulsion |
Built and tested on Python 3.13.2, pygame 2.6.1, Windows 10. Written entirely from scratch with no starter code. All physics implemented manually — no physics engine used.


