An interactive essay
Quantum computing,
slowly.
A quantum computer is not a parallel-universe oracle. It is a machine that steers probability amplitudes — numbers a lot like probabilities, but allowed to be negative — so that wrong answers cancel out and right answers add. Everything else, from Shor's algorithm to error correction, is choreography on that one idea.
Two amplitudes for the same outcome can cancel. Probabilities can't. That is the whole story, the rest is detail.
2 · What is a qubit?
A classical bit is a switch: 0 or 1. A qubit is
something stranger — an arrow inside a sphere. The arrow can
point at the north pole (call it $|0\rangle$), the south pole ($|1\rangle$),
or anywhere on the surface in between. The surface holds every possible
in‑between state a single qubit can be in.
Mathematically a pure qubit state is a unit vector in $\mathbb{C}^2$: $$|\psi\rangle = \alpha|0\rangle + \beta|1\rangle, \qquad |\alpha|^2 + |\beta|^2 = 1.$$ Two complex numbers, four real parameters, minus normalization, minus a physically‑unobservable global phase, leaves two real angles — a polar $\theta$ and an azimuthal $\varphi$ — mapping every pure state to one point on a sphere: $$|\psi\rangle = \cos\tfrac{\theta}{2}|0\rangle + e^{i\varphi}\sin\tfrac{\theta}{2}|1\rangle.$$
The factor of $\theta/2$ rather than $\theta$ is the geometric subtlety that the Bloch sphere demands. Orthogonal qubit states — $|0\rangle$ and $|1\rangle$ — sit on opposite poles, 180° apart, not 90° apart. Drag the arrow below and watch the amplitudes update.
Recap: the north pole is $|0\rangle$, the south pole is $|1\rangle$, the equator holds equal‑weight superpositions, and the only thing a measurement returns is one bit.
3 · Superposition & interference
The word superposition gets misused constantly. A qubit in state $\tfrac{1}{\sqrt 2}(|0\rangle + |1\rangle)$ is not “both 0 and 1 at once”. It is a definite state whose amplitudes — the two complex numbers attached to the two basis labels — happen to be equal in magnitude.
Amplitudes look a lot like probabilities, with one crucial difference: they are allowed to be negative, or complex. And when two different amplitudes contribute to the same outcome, they add. Equal-magnitude opposite‑sign amplitudes cancel. Probabilities can never do this.
The cleanest example: apply the Hadamard gate twice to $|0\rangle$. $$H|0\rangle = \tfrac{1}{\sqrt 2}(|0\rangle + |1\rangle),\qquad H\cdot H|0\rangle = |0\rangle.$$ Naively you might think the second Hadamard makes things even “more uncertain”. It doesn't. The two paths from $|0\rangle$ to $|1\rangle$ carry amplitudes $+\tfrac12$ and $-\tfrac12$. They cancel. The two paths to $|0\rangle$ both carry $+\tfrac12$. They add to 1.
Recap: in quantum mode, the $|1\rangle$ outcome ends up with amplitude $\tfrac12 - \tfrac12 = 0$, probability 0. In classical mode the same probabilities just add, $\tfrac14 + \tfrac14 = \tfrac12$, and you can never cancel anything. That difference is the engine of every quantum speedup.
4 · Measurement: same state, different basis, different bits
The Born rule is the contract between the quantum world and the classical one: a state $|\psi\rangle = \sum_x \alpha_x|x\rangle$, measured in the basis $\{|x\rangle\}$, yields outcome $x$ with probability $|\alpha_x|^2$, and afterwards the state is $|x\rangle$. One bit out per qubit, no peeking at the amplitudes themselves.
The choice of basis matters more than people expect. $|+\rangle$ measured in the $Z$ basis gives 0 or 1 with equal probability. The same state measured in the $X$ basis gives 0 every single time. Same physical state, different question, different statistics.
Recap: the histogram is what the Born rule actually predicts, sampled finitely. Slide $\theta$ to $\pi/2$ — that's $|+\rangle$ — and notice how it behaves drastically differently in $Z$ versus $X$.
5 · Entanglement: information that lives in a relationship
A two‑qubit state is separable if you can write it as $|a\rangle\otimes|b\rangle$ — one definite single‑qubit state for each qubit. Most two‑qubit states aren't separable. The famous Bell state $$|\Phi^+\rangle = \tfrac{1}{\sqrt 2}(|00\rangle + |11\rangle)$$ has perfect $00/11$ correlations, and yet looking at either qubit alone gives you a featureless 50/50 mixture. The information lives in the relationship, not in either party.
Building one is a two‑gate recipe: $H$ on qubit 0, then $\text{CNOT}$ from qubit 0 to qubit 1. Below, the circuit runs in your browser — amplitudes, probabilities, and 1000 sampled shots all computed live.
Recap: only $|00\rangle$ and $|11\rangle$ appear in the histogram — perfectly correlated when measured in the Z basis. A Bell-inequality test would also confirm correlations in rotated bases, but that requires a basis selector this widget doesn't expose. The non-classical fingerprint is that the entangled correlations survive a basis change while classical coin‑flip correlations don't — which is what Bell's inequalities measure.
Bell test: the $2\sqrt 2$ violation
There is a sharp, experimental way to prove the correlations above are genuinely non‑classical. Alice and Bob share the Bell pair $|\Phi^+\rangle$, but instead of always measuring in $Z$, each of them picks (randomly, on each shot) between two measurement angles: Alice picks $a_0$ or $a_1$, Bob picks $b_0$ or $b_1$. Each measurement returns $\pm 1$. Repeat thousands of times and estimate the four correlations $E(a_i, b_j) = \langle a_i b_j\rangle$.
The CHSH statistic is the combination $$S = E(a_0, b_0) - E(a_0, b_1) + E(a_1, b_0) + E(a_1, b_1).$$ Any local hidden-variable theory — any model where the outcomes were predetermined and just revealed by measurement — satisfies $|S|\leq 2$. Quantum mechanics, applied to $|\Phi^+\rangle$ with the correlation $E(\theta_A, \theta_B) = \cos(\theta_A - \theta_B)$, can reach $|S| = 2\sqrt 2 \approx 2.828$ (Tsirelson's bound), achieved when Alice picks $0$ and $\pi/2$ and Bob picks $\pi/4$ and $3\pi/4$ — one of several geometrically equivalent optimal configurations.
Recap: at the optimal angles, $S = 2\sqrt 2 \approx 2.828$. The classical bound is $2$. No local hidden-variable theory can explain that. It's been verified experimentally since the 1980s, most stringently in the 2015 loophole-free tests at Delft, NIST, and Vienna.
6 · Gates and circuits
A quantum gate is a small, fixed transformation on one or two qubits. Each gate is a unitary matrix — norm-preserving, reversible. A circuit is a sequence of gates: read it left to right like sheet music, each gate applied in turn, ending in a measurement.
The first set you should know by name and shape: $X$ (NOT, north-pole to south-pole), $Z$ (flip the phase of $|1\rangle$), $H$ (build a superposition), $S$ and $T$ (small phase rotations), and $\text{CNOT}$ (the workhorse two-qubit gate that creates entanglement).
A profound fact: $\{\text{CNOT}, H, T\}$ — just three gates — is universal. Every unitary can be approximated to arbitrary accuracy by sequences from this set (Solovay–Kitaev). Click gates below to build a 2-qubit circuit and watch the amplitude vector update live.
Recap: gates are unitaries, circuits compose them by multiplication, and a tiny universal set spans the whole space. The Bell circuit above is the simplest non‑trivial example: two gates, one entangled pair.
7 · Phase kickback — how oracles encode answers as phases
Every oracle-based quantum algorithm — Deutsch–Jozsa, Grover, Shor's order-finding — uses the same trick. The oracle $U_f$ implements $$U_f \,|x\rangle|y\rangle \;\mapsto\; |x\rangle\,|y \oplus f(x)\rangle.$$ By itself that just XOR‑flips a target bit. But if you prepare the target in the state $|-\rangle = (|0\rangle - |1\rangle)/\sqrt 2$, the XOR flips the sign of the whole expression when $f(x)=1$, and the sign gets kicked back onto the input register.
Compute it directly: $$U_f \,|x\rangle |{-}\rangle = \tfrac{1}{\sqrt 2}\bigl(|x\rangle|0 \oplus f(x)\rangle - |x\rangle|1 \oplus f(x)\rangle\bigr) = (-1)^{f(x)}\,|x\rangle\,|{-}\rangle.$$ The ancilla is untouched. The input register picked up a phase that depends on $f(x)$. That phase is invisible to any single measurement, but the second wall of Hadamards turns the pattern of phases into interference: amplitudes for one outcome add, amplitudes for another cancel. Phase kickback is how a quantum oracle injects information into the amplitude vector.
The widget below lets you define the oracle by clicking. Pick any Boolean function $f: \{0,1\}^4 \to \{0,1\}$ — click squares to toggle $f(x)$ between $0$ (blue) and $1$ (orange). Then step through the Deutsch–Jozsa circuit: Hadamard wall, oracle, Hadamard wall. Watch the amplitudes; if you started with a constant $f$, all amplitude lands on $|0000\rangle$. If $f$ is balanced (exactly 8 of 16 inputs marked), the amplitude on $|0000\rangle$ is exactly zero. One quantum query distinguishes the two; classically you might need nine.
Recap: when $f$ is constant, all 16 amplitudes carry the same sign and the second Hadamard wall constructively interferes them onto $|0000\rangle$ with amplitude $\pm 1$. When $f$ is balanced, the signs are split exactly half‑and‑half and the $|0000\rangle$ amplitude is the inner product of $(+1,\ldots,+1)$ with a vector of equal parts $+1$ and $-1$ — zero. That is the Deutsch–Jozsa algorithm. One quantum query, two possible outcomes, perfect discrimination of constant vs. balanced.
8 · Grover's algorithm: a rotation, not an amplifier
Suppose you have an unsorted list of $N$ items and a black-box function $f$ that returns 1 on the one item you want and 0 on the rest. Classically you have to try roughly $N/2$ items on average. Grover's algorithm finds it in about $\tfrac{\pi}{4}\sqrt N$ queries — a quadratic speedup, provably optimal for any quantum oracle algorithm.
The picture is geometric. The state lives in a 2D plane spanned by “the marked answer” and “everything else”. Each Grover iterate $G = D\cdot O$ rotates the state by a small angle $2\theta$ toward the marked axis, with $\sin\theta = \sqrt{1/N}$. After $k$ steps the marked probability is $\sin^2((2k+1)\theta)$. Iterate too few times, you haven't reached the answer; iterate too many, you spin past it.
The widget below runs the real Grover circuit on $N=16$ items in your
browser — it computes the oracle $U_\omega = I - 2|m\rangle\langle m|$
and the diffusion operator $D = 2|s\rangle\langle s| - I$ directly on the
16‑amplitude state vector. The numbers match the Python toy
03_grover_search.py to four decimal places.
Recap: probability rises, peaks at $k=3$ around 96.13%, then falls. Grover is a rotation, not a one-way amplifier. Knowing when to stop is part of the algorithm.
9 · The Quantum Fourier Transform: making periodicity visible
Don't think of the QFT as “an FFT, but faster”. You can never read out the output coefficients individually — you only get to measure, and that gives you one Fourier-basis state. The job the QFT is good at is this: given a quantum state with a periodic amplitude pattern, concentrate all of the amplitude onto the multiples of $N/r$ where $r$ is the period. Periodicity, in $O(\log^2 N)$ gates, becomes measurable.
The widget below builds a “comb” state on a 64-position register with adjustable period $r$, then applies the $64\times 64$ DFT in your browser. For $r = 4$, the output is concentrated on $\{0, 16, 32, 48\}$; for $r = 8$, on $\{0, 8, 16, \ldots\}$. The peaks are real, and they are exactly $N/r$ apart.
Recap: the QFT does not give you the Fourier spectrum, it gives you a state whose measurement statistics reveal periodicity. Combine that with continued fractions and you have the engine of Shor's algorithm.
Note: this widget computes the forward DFT. The inverse QFT used in Shor's algorithm has the conjugate phase but produces the same peak structure for real-valued input combs.
10 · Shor: from factoring to a comb in a register
The 1994 paper that woke the world up. To factor $N$, Shor reduces the problem to finding the order $r$ of a random base $a$ modulo $N$ — the smallest $r>0$ with $a^r \equiv 1 \pmod N$. Once you know $r$ (and as long as $r$ is even and $a^{r/2} \not\equiv -1$), the factors fall out of $\gcd(a^{r/2}\pm 1,\, N)$.
The function $f(x) = a^x \bmod N$ is classically easy to evaluate but its period is hard to find when $N$ is huge. A quantum computer prepares a uniform superposition on the input register, applies the modular-exponentiation oracle to entangle input with output, measures the output (which collapses the input to a comb with spacing $r$), and applies the inverse QFT. The peaks of the output are at multiples of $M/r$ where $M = 2^n$ is the input-register size. Continued fractions on $k/M$ recovers $r$.
The widget below runs the full pipeline live in your browser for
$N = 15$, $a = 7$. The QFT, the measurement collapse, and the
continued-fraction post-processing are all computed in JS — the
numbers track the Python toy 04_period_finding.py exactly
(period $r=4$, peaks at $\{0, 64, 128, 192\}$, factors 3 and 5).
Recap: factoring 15 is small enough that the “quantum” part runs in milliseconds on your laptop. The real wins come at $N$ with hundreds of digits, where the modular exponentiation step is the bottleneck and classical algorithms still take subexponential time.
11 · Build it from scratch A Karpathy-style notebook thread: a working quantum simulator in ~400 lines.
Six cells. The first five compute their own numbers live in your browser
— they are real ports of the Python in code/workshop/,
not pre‑recorded output. The last cell shows the output of the
256×256 inverse QFT for Shor on $N=15$ (static, because the QFT matrix
is heavy enough to defer). Click Run on each cell to watch the
state vector evolve, the amplitudes interfere, and the algorithms turn
into ordinary NumPy.
A qubit is just a vector. Two complex numbers, unit norm. Gates are 2×2 unitary matrices — you apply them by multiplication. Apply $H$ once to $|0\rangle$ and watch the amplitudes.
import numpy as np
KET_0 = np.array([1+0j, 0+0j])
KET_1 = np.array([0+0j, 1+0j])
I = np.eye(2, dtype=complex)
X = np.array([[0, 1], [1, 0]], dtype=complex)
Z = np.array([[1, 0], [0, -1]], dtype=complex)
H = (1/np.sqrt(2)) * np.array([[1, 1], [1, -1]], dtype=complex)
def apply(gate, state):
return gate @ state
state = KET_0.copy()
print("Before H: ", state)
state = apply(H, state)
print("After H: ", state)
One $H$ makes a superposition. The amplitudes $(+0.707,\,+0.707)$ are equal — the qubit is on the equator of the Bloch sphere, 50/50 under a $Z$‑measurement.
Two Hadamards cancel. Apply $H$ a second time and the $|1\rangle$ amplitude vanishes — not because of randomness, but because two paths to $|1\rangle$ carry equal-magnitude, opposite-sign amplitudes and add to zero.
state = KET_0.copy()
state = apply(H, state) # |0> -> (|0> + |1>)/sqrt(2)
state = apply(H, state) # second H: |1> amplitudes cancel
# What just happened:
# |0> --H--> (+1/sqrt2)|0> --H--> (+1/2)|0> + (+1/2)|1>
# |0> --H--> (+1/sqrt2)|1> --H--> (+1/2)|0> + (-1/2)|1>
# sum |1>: +1/2 + (-1/2) = 0 # interference!
print("After HH:", state)
This is interference. Probabilities can't do this — they can only add, never cancel. This is the engine of every quantum speedup.
A Bell pair from $H$ and CNOT. Take two qubits, apply $H$ to the first, then CNOT with the first as control. The result has amplitude only on $|00\rangle$ and $|11\rangle$ — perfect correlation.
def kron(*ops):
out = ops[0]
for op in ops[1:]:
out = np.kron(out, op)
return out
I2 = np.eye(2, dtype=complex)
CNOT = np.array([[1,0,0,0],
[0,1,0,0],
[0,0,0,1],
[0,0,1,0]], dtype=complex)
state = kron(KET_0, KET_0) # |00>
state = kron(H, I2) @ state # H on qubit 0
state = CNOT @ state # entangle
print("state =", state) # (|00> + |11>) / sqrt(2)
# Sample 1000 shots in the Z basis:
p = np.abs(state)**2
counts = np.random.multinomial(1000, p)
for label, c in zip(["|00>","|01>","|10>","|11>"], counts):
print(f" P({label}) ~ {c/1000:.3f} ({c} shots)")
Two qubits, perfectly correlated, individually uncertain. That's entanglement. Notice that $|01\rangle$ and $|10\rangle$ never appear.
The reduced density matrix shows the entanglement. Trace out qubit 1 from the Bell pair and look at qubit 0 on its own. If the result is $I/2$, qubit 0 alone is a fair coin — all the information lives in the relationship.
def reduced_qubit0(state):
# state is length 4; reshape to (2,2): axis 0 = qubit 0, axis 1 = qubit 1.
psi = state.reshape(2, 2)
return psi @ psi.conj().T
rho_0 = reduced_qubit0(state)
print("rho_0 =")
print(rho_0.real)
print("eigenvalues:", np.linalg.eigvalsh(rho_0).round(3))
print("purity tr(rho_0^2) =", np.trace(rho_0 @ rho_0).real)
Each qubit alone is maximally mixed (purity $1/2$). The joint state is pure (purity $1$). The information is in the entanglement, not in either qubit.
Grover, in eight lines of NumPy. Phase-flip the marked
amplitude; reflect about the uniform superposition; repeat. Print
$P(\text{marked})$ after each iteration and watch it rise, peak, and
overshoot.
The Python below relies on
the helpers defined in earlier cells (the $H$ matrix and the
apply_H_all function). Copying just this snippet into a fresh
Python file won't run — see code/workshop/03_grover_search.py
for the standalone version.
n, N, marked = 4, 16, 11
def apply_H_all(state):
H_all = H
for _ in range(n-1):
H_all = np.kron(H_all, H)
return H_all @ state
def oracle(state, m): # phase-flip marked basis state
s = state.copy(); s[m] *= -1; return s
def diffuser(state): # H_all, flip-sign-of-zero, H_all
s = apply_H_all(state)
s = -s; s[0] = -s[0]
return apply_H_all(s)
state = apply_H_all(np.eye(N, dtype=complex)[0])
for k in range(7):
p_m = abs(state[marked])**2
print(f"iter {k}: P(marked) = {p_m:.4f}")
state = diffuser(oracle(state, marked))
Grover is a rotation. At $k=3$ we hit $0.9613$; at $k=6$ we are back to $0.0204$. Stop at the right step or you spin past the answer.
Period finding for $N=15$. Build the comb of $7^x \bmod 15$, apply the $256\times 256$ inverse QFT, measure, run continued fractions, extract the factors. This cell is static — the QFT matrix is heavy enough that we show the numbers the real Python prints. The four equal peaks at multiples of $64$ are the period $r=4$ made visible.
import math
from fractions import Fraction
N, A = 15, 7
n_in = 8; M = 2**n_in # input register: 256 values
# 1. Build the joint state in (M, M_out) shape and apply U_f.
joint = np.zeros((M, 16), dtype=complex)
for x in range(M):
joint[x, pow(A, x, N)] = 1.0 / np.sqrt(M)
# 2. (For analysis) measure the output register -> input collapses to a comb.
y = np.random.choice(16, p=(np.abs(joint)**2).sum(axis=0))
comb = joint[:, y]; comb = comb / np.linalg.norm(comb)
# 3. Inverse QFT on the input register.
omega = np.exp(2j * np.pi / M)
QFT = omega ** (np.arange(M)[:,None] * np.arange(M)[None,:]) / np.sqrt(M)
after = QFT.conj().T @ comb
# 4. Top outcomes:
probs = np.abs(after)**2
for k in np.argsort(probs)[::-1][:4]:
print(f" k = {k:3d} P = {probs[k]:.4f} k/M = {k/M:.4f}")
# 5. Continued fractions + factor extraction.
k = 192
r = Fraction(k, M).limit_denominator(N).denominator
x = pow(A, r//2, N)
print(f"r = {r}; 7^(r/2) mod 15 = {x}")
print(f"factors: {math.gcd(x-1, N)} and {math.gcd(x+1, N)}")
Period 4 $\to$ factors 3 and 5. The quantum part: finding that period via the QFT. The classical part: continued fractions and gcd. Together: Shor's algorithm on $N=15$.
12 · Anatomy — how the parts fit
Three field-guide plates of the central concepts.
Anatomy of a Qubit
Anatomy of an Entangled Pair
Anatomy of Shor's Algorithm
13 · Error correction in action — the surface code, live
Physical qubits are noisy. Each gate has a $0.1$–$1\%$ chance of an error. With billions of operations in a Shor‑scale computation, raw qubits would degrade to noise in microseconds. The fix is not better hardware alone — it's encoding. We spread each logical qubit across many physical qubits in a careful entanglement pattern, then continually measure parities of small groups (syndromes) to spot errors without disturbing the encoded data.
The surface code is the leading approach today — a 2D lattice of data qubits with two kinds of stabilizer ancillas interleaved between them. Google's Willow chip demonstrated this below threshold in 2024: increasing the code's distance suppressed the logical error rate by $2.14\times$ per $+2$ increment in distance — the first hardware where adding more physical qubits made the encoded qubit cleaner, not noisier.
Below is a distance-3 surface code drawn on a $3\times 3$ grid of data qubits. Two kinds of ancillas, drawn as diamonds, sit between them: Z‑stabilizers measure $Z\!Z\!Z\!Z$ on their four data‑neighbours (and fire when an $X$ error has flipped one of those neighbours), while X‑stabilizers measure $X\!X\!X\!X$ (and fire on $Z$ errors). A single $X$, $Z$, or $Y$ error on any data qubit lights up exactly the neighbouring stabilizers of the right type — a syndrome the decoder can match to a unique inferred correction.
Click any data qubit to inject an $X$, $Z$, or $Y$ error. Watch the syndromes light up. Press Decode to run a minimum‑weight matching on the syndrome and see the inferred correction. Try injecting two errors far apart, then three — eventually you'll see the decoder fail with a logical error: a string of errors long enough to be confused with a logical $X$ or $Z$ operator on the encoded qubit. That is the code distance being exceeded.
Recap: when the physical error rate is below the code's threshold, increasing the code distance suppresses the logical error rate exponentially. Google's 2024 Willow chip demonstrated this empirically with a distance-7 surface code achieving the $2.14\times$ suppression per $+2$ distance increment. Error correction is no longer a theory result — it is a working hardware primitive.
14 · Where we are, in 2026
The first watershed moment was Shor's paper in 1994; the second was Grover's in 1996. Then a long, often discouraging climb — 30 years of decoherence, error‑correction theorems, and platform shoot‑outs — until December 2024, when Google's Willow chip became the first hardware to show error-corrected logical qubits clearly outperforming their physical counterparts. 2025 and 2026 saw the first multi-dozen logical qubit demonstrations.
Click a dot to read the milestone.
Where things stand
In 2026 the field has changed shape. Logical qubits are real but few; fault-tolerant resource estimates for breaking RSA-2048 have come down to roughly $2 \times 10^7$ physical qubits and a few hours of wall time — still many years from existing hardware, but no longer an open-ended fantasy. Post‑quantum cryptography (Kyber, Dilithium, SPHINCS+) was standardized by NIST in 2024 and is being rolled out into TLS and PKI now, mostly out of fear of harvest-now-decrypt-later attacks on long-lived secrets.
What we still don't know: which hardware modality wins, what the first useful application is (chemistry? optimization? something nobody has written down yet?), and whether NISQ devices — quantum machines too small to error-correct — have any path to advantage at all. The honest answer is that we are at the bottom of a long climb, and the most interesting decade for the field is the next one.