A 1D Ising-like Monte Carlo simulation of protein aggregation, running live in the browser.
Each site on a linear chain represents a protein that can exist in one of three states: Monomer, Disordered Oligomer, or Fibril. The system evolves via the Metropolis algorithm, and you can watch nucleation, aggregation, and phase-like transitions unfold in real time.
π carlocamilloni.github.io/Aging
| State | Colour | Description |
|---|---|---|
| Monomer (M) | π΅ Sky blue | Free, soluble protein |
| Disordered Oligomer (D) | π Orange | Loosely associated, unstructured aggregate |
| Fibril (F) | π©· Pink-violet | Ordered amyloid-like aggregate β stabilised by cooperative interactions |
H = Ξ£α΅’ Ξ΅βα΅’
β J_D Ξ£β¨i,jβ© Ξ΄(D,D)
β J_F Ξ£β¨i,jβ© Ξ΄(F,F) Β· π[run β₯ minRun]
β h_FF Β· N_F Β· π[β active run]
- Ξ΅β β intrinsic energy of each state (eM, eD, eF)
- J_D β disordered nearest-neighbour coupling: two adjacent D sites lower energy
- J_F β fibril nearest-neighbour coupling: active only when both sites belong to a contiguous run of length β₯
minRun(cooperative nucleation) - h_FF β background field that lowers the energy of every fibril site once at least one active run exists anywhere on the chain (secondary nucleation / seeding)
Competition between aggregation pathways β disordered oligomers form freely at any size, while fibril coupling is gated by a minimum run length. At intermediate temperatures the system can become trapped in a disordered phase rather than reaching the fibril state, reflecting the experimental observation that disordered aggregates can kinetically compete with amyloid formation.
Cooperative fibril nucleation β isolated fibril sites gain no stabilisation from J_F, so nucleation requires assembling a critical seed of size minRun before the fibril state becomes thermodynamically favourable.
Catalytic background field β once any active run forms, h_FF lowers the energy of every fibril site regardless of local context. This captures secondary nucleation and seeding: an established fibril template changes the free-energy landscape for the whole chain. The field is O(N) and avoids the O(NΒ²) cost of explicit pairwise long-range couplings.
Irreversible fibril mode β an optional toggle locks any fibril site once it joins an active run, preventing reversion. This captures kinetic trapping of mature amyloid cores.
All energy parameters share the range 0β8.
| Parameter | Description |
|---|---|
| Chain length N | Number of monomers (10β10000) |
| k_B T | Temperature β controls the scale of thermal fluctuations |
| E_Monomer | Intrinsic energy of the monomer state |
| E_Disordered | Intrinsic energy of the disordered oligomer state |
| E_Fibril | Intrinsic energy of the fibril state |
| J_Disordered | Disorderedβdisordered nearest-neighbour coupling |
| J_Fibril | Short-range fibrilβfibril coupling (nearest neighbours, run β₯ minRun) |
| h_FF background | Background field on all F sites once an active run exists |
| Min Fibril Run | Minimum run length to activate fibril coupling and the background field |
Clicking Save (n) downloads a JSON file containing every recorded snapshot:
{
"metadata": {
"created": "2026-03-13T10:23:00.000Z",
"software": "Aging",
"n": 80,
"totalSteps": 342,
"snapshots": 342,
"params": { "eM": 0, "eD": 1.5, "eF": 3.0, "jD": 1.2, "jF": 2.5, "hFF": 0.5, "minRun": 3, "T": 1.0 }
},
"trajectory": [
{ "step": 1, "chain": [0, 0, 2, 0, 1], "E": 12.4 },
{ "step": 2, "chain": [0, 1, 2, 0, 1], "E": 11.1 }
]
}Each entry contains the step index, the full chain as an integer array (0 = Monomer, 1 = Disordered, 2 = Fibril), and the instantaneous energy. The buffer is cleared on Reset.
You need Node.js v18 or later.
git clone https://github.com/carlocamilloni/Aging.git
cd Aging
npm install
npm run devThen open http://localhost:5173.
sudo port install nodejs22 npm10The simulation can be run headlessly from the terminal, writing the time trace and full trajectory to TSV files incrementally.
node aging-cli.js [options]| Flag | Default | Description |
|---|---|---|
--steps N |
1000 | Number of MC steps |
--n N |
100 | Chain length |
--seq MMMFFF... |
β | Starting conformation string (overrides --n) |
--T N |
1.0 | Temperature k_BT |
--eM N |
0 | Intrinsic energy β Monomer |
--eD N |
1.4 | Intrinsic energy β Disordered |
--eF N |
6.0 | Intrinsic energy β Fibril |
--jD N |
1.0 | J_Disordered coupling |
--jF N |
3.5 | J_Fibril coupling |
--hFF N |
3.5 | h_FF background field |
--minRun N |
3 | Min fibril run length |
--irreversible |
off | Enable irreversible fibril locking |
--stopOnFibril F |
off | Stop when fibril fraction reaches F β (0, 1] (default 1.0 if flag given without value) |
--trace FILE |
trace.tsv | Time trace output file |
--traj FILE |
trajectory.tsv | Trajectory output file |
--help |
β | Show usage |
Both output files are written incrementally β safe to inspect mid-run with tail -f.
trace.tsv β one row per step:
step fM fD fF fActiveF E avgE
0 1.0000 0.0000 0.0000 0.0000 80.0000 80.0000
1 0.9875 0.0125 0.0000 0.0000 79.5000 79.7500
trajectory.tsv β one row per step, chain encoded as a string of M/D/F characters:
step chain E
0 MMMMMMMM... 80.0000
1 MMDMMMMD... 79.5000
Examples:
# Run 5000 steps at low temperature
node aging-cli.js --steps 5000 --n 100 --T 0.5 --jF 3.0 --hFF 1.0
# Stop as soon as 80% of sites are fibril
node aging-cli.js --steps 10000 --stopOnFibril 0.8
# Stop at 100% fibril (shorthand)
node aging-cli.js --steps 10000 --stopOnFibril
# Via npm
npm run cli -- --steps 5000 --T 0.5The model and MC engine are covered by a regression suite using Vitest. All tests use hand-verified expected values β deterministic, no MC randomness except the low-temperature energy test.
npm testTests in tests/aging.test.js cover:
fibrilRunLengthβ boundary cases, run detection, non-fibril sitescomputeEnergyβ intrinsic energies, D-D coupling, F-F short-range coupling,h_FFbackground field (firing conditions, sub-threshold sites, two-run cases)buildRunLenβ correct lengths for all-monomer, single fibril, contiguous runs, and multiple runscountActiveRunsβ threshold detection including sub-threshold runsupdateRunLenβ correctness after split, merge, extension, shrink, and long-run casesdeltaEnergyFastβ exact consistency withcomputeEnergyfor 17+ flip cases covering MβD, MβF, DβF, boundary conditions, and h_FF transitionsmcStepβ chain length preservation, irreversible locking, energy non-increase at low temperature
The CI workflow (.github/workflows/test.yml) runs the suite on every push and pull request.
npm run buildThe static output lands in dist/ and can be served from any static host.
Pushes to main deploy automatically to GitHub Pages via GitHub Actions. See .github/workflows/deploy.yml.
After cloning, enable Pages in your repo under Settings β Pages β Source β GitHub Actions.
Aging/
βββ .github/workflows/
β βββ deploy.yml # deploy to GitHub Pages on push to main
β βββ test.yml # run test suite on push and pull request
βββ src/
β βββ model.js # Hamiltonian, energy functions, runLen cache
β βββ simulation.js # MC engine (mcStep), chain helpers, constants
β βββ worker.js # Web Worker owning the MC loop
β βββ aging.jsx # React UI
β βββ main.jsx # entry point
βββ tests/
β βββ aging.test.js # Vitest regression suite
βββ aging-cli.js # headless CLI runner
βββ index.html
βββ package.json
βββ vite.config.js # Vite + Vitest config
βββ README.md
- React 18 β UI and state management
- Vite 5 + Vitest 1 β build tooling and testing
- Vanilla JS β MC engine (no runtime dependencies)
This codebase was developed with the assistance of Claude Sonnet 4.6 (Anthropic), which contributed to the model design, implementation, refactoring, and test suite.
MIT Β© The Authors