Quantum mechanics

Particle in a box

Trap a particle between two walls and it can only have certain energies. Put it in two of them at once and the probability starts sloshing back and forth, forever, with no energy going anywhere.

EXP-032

Move the cursor over the canvas — it is part of the simulation.

Drag your finger across the canvas — it is part of the simulation.

What to look for

Set n₁ and n₂ to the same number and the motion stops dead: a single stationary state has a probability that does not depend on time. That is what stationary means, and it is why atoms do not radiate away.

The maths

φₙ(x) = √(2/L)·sin(nπx/L)

The stationary states of an infinite well. They are the only shapes that fit with zero at both walls, which is why n has to be a whole number.

Eₙ = n²π²ħ²/2mL²

Energy grows as n², not n. That quadratic gap is what makes the two states beat against each other at a visible rate.

ψ = a·φ₁e^(−iE₁t/ħ) + b·φ₂e^(−iE₂t/ħ)

A superposition. Each piece rotates in the complex plane at its own rate, and the interference between them is the motion you see.

How it is built

No integration at all: the solution is exact, so each frame evaluates the closed form at 220 points. Three curves are drawn: the real part, the imaginary part, and |ψ|² on top. The first two are not observable and are drawn faint on purpose.

The code, step by step

Cut straight from lab.js. These are the real lines that run above, not a simplified version.

01

Energies go as n squared

const E1 = n1 * n1 * 0.35, E2 = n2 * n2 * 0.35;   // Eₙ ∝ n²

Not linear. That quadratic spacing is why the two states beat against each other, and why the beat gets faster the further apart you set them.

02

Evaluate the exact solution

const f1 = Math.sin(n1 * Math.PI * u), f2 = Math.sin(n2 * Math.PI * u);
const re = a * f1 * Math.cos(-E1 * t) + b * f2 * Math.cos(-E2 * t);
const im = a * f1 * Math.sin(-E1 * t) + b * f2 * Math.sin(-E2 * t);
reP.push([x, base - re * A]); imP.push([x, base - im * A]);
pr.push([x, base - (re * re + im * im) * A * 1.5]);

No integration: the closed form is known, so each point is computed directly. Real and imaginary parts are tracked separately because the phase between them is the whole physics.

03

Only the squared modulus is observable

line(reP, 0.35, 1);          // parte real
line(imP, 0.22, 1);          // parte imaginaria
line(pr, 0.95, 1.6);         // |ψ|², lo único observable

Re and Im are drawn faint on purpose. No measurement returns them: what you can actually detect is the sum of their squares, drawn bright on top.

Why it matters

This is the simplest system where quantisation falls out of the maths instead of being assumed. The walls are what force it: confinement plus a wave equation gives discrete energies, and that is the origin of every atomic spectrum ever measured.

The whole thing

Everything above, in one piece. This is the entire experiment as it lives in lab.js.

FULL

lab.js · quantum

{ id: 'quantum',
      name: L('Particle in a box', 'Partícula en una caja'),
      note: L('Two states added together. The probability sloshes and never settles.',
              'Dos estados sumados. La probabilidad chapotea y nunca se asienta.'),
      params: [
        { key: 'n1',  label: L('State n₁', 'Estado n₁'), min: 1, max: 6, step: 1, def: 1 },
        { key: 'n2',  label: L('State n₂', 'Estado n₂'), min: 1, max: 8, step: 1, def: 2 },
        { key: 'mix', label: L('Mix', 'Mezcla'),         min: 0, max: 1, step: 0.01, def: 0.5 }
      ],
      make() {
        return {
          step(ctx, w, h, t, acc, P, M) {
            const pad = w * 0.08, L = w - pad * 2, base = h * 0.55, A = h * 0.16;
            const mix = M.in ? Math.min(1, Math.max(0, M.x / w)) : P.mix;
            const a = Math.sqrt(1 - mix), b = Math.sqrt(mix);
            const n1 = P.n1 | 0, n2 = P.n2 | 0;
            const E1 = n1 * n1 * 0.35, E2 = n2 * n2 * 0.35;   // Eₙ ∝ n²

            // Paredes del pozo
            ctx.strokeStyle = `rgba(${acc},0.35)`; ctx.lineWidth = 1;
            ctx.beginPath();
            ctx.moveTo(pad, h * 0.12); ctx.lineTo(pad, h * 0.9);
            ctx.moveTo(pad + L, h * 0.12); ctx.lineTo(pad + L, h * 0.9);
            ctx.stroke();

            let reP = [], imP = [], pr = [];
            for (let i = 0; i <= 220; i++) {
              const u = i / 220, x = pad + u * L;
              const f1 = Math.sin(n1 * Math.PI * u), f2 = Math.sin(n2 * Math.PI * u);
              const re = a * f1 * Math.cos(-E1 * t) + b * f2 * Math.cos(-E2 * t);
              const im = a * f1 * Math.sin(-E1 * t) + b * f2 * Math.sin(-E2 * t);
              reP.push([x, base - re * A]); imP.push([x, base - im * A]);
              pr.push([x, base - (re * re + im * im) * A * 1.5]);
            }
            const line = (pts, alpha, lw) => {
              ctx.strokeStyle = `rgba(${acc},${alpha})`; ctx.lineWidth = lw;
              ctx.beginPath(); pts.forEach((p, k) => k ? ctx.lineTo(p[0], p[1]) : ctx.moveTo(p[0], p[1]));
              ctx.stroke();
            };
            line(reP, 0.35, 1);          // parte real
            line(imP, 0.22, 1);          // parte imaginaria
            line(pr, 0.95, 1.6);         // |ψ|², lo único observable
            ctx.fillStyle = `rgba(${acc},0.8)`; ctx.font = '11px monospace';
            ctx.fillText('|ψ|²  n₁=' + n1 + '  n₂=' + n2, 12, 20);
          }
        };
      }
    },