Thomas Verhave

Writing · Build Notes

Generative art from a real quantum computer: true randomness, and what survives the noise

Your computer can't actually generate a random number.

Your computer can't actually generate a random number. It fakes it — a deterministic algorithm seeded well enough that you can't tell. A quantum computer can do the real thing: measure a qubit in superposition and the outcome is random by the laws of physics, not by a clever function.

I wanted to see that, touch it, and make something with it. So I ran circuits on a real IBM quantum processor, pulled genuinely-random bits off the hardware to drive generative art, and — the more sobering part — watched how much of the ideal quantum behaviour actually survives contact with a noisy real device.

Everything below runs on Qiskit 2.4 + qiskit-ibm-runtime on Python 3.14, against IBM's Open Plan (free, ~10 minutes of QPU time a month — which turns out to be plenty).

A true quantum random-number generator

The cleanest possible quantum program: put 8 qubits into an equal superposition with Hadamard gates, then measure. Each shot collapses to a uniformly-random 8-bit string — one random byte, sampled from physics.

from qiskit import QuantumCircuit

NQ = 8                        # 8 qubits -> 8 random bits = 1 byte per shot
qc = QuantumCircuit(NQ)
qc.h(range(NQ))               # equal superposition: P(any 8-bit string) = 1/256
qc.measure_all()

The neat trick: Qiskit's BitArray hands those measurement shots back already packed into bytes, so the measurement record is the raw pixel stream. No transformation — the QPU's collapses go straight into an image.

W = H = 180
SHOTS = W * H * 3             # one byte per shot, RGB
# ...run the circuit for SHOTS shots...
pixels = result.data.meas.array.reshape(H, W, 3)   # measurement bytes -> image

Run it on the local simulator and you get pseudo-random noise. Run it with --hardware and every pixel's colour was decided by a physical quantum measurement on a chip in an IBM data centre. It looks like TV static — but it's honest static, and that distinction is the whole point.

The "hello world": a Bell state on real silicon

Before the art, the canonical two-qubit experiment: entangle two qubits so that measuring one instantly tells you the other.

qc = QuantumCircuit(2)
qc.h(0)          # qubit 0 into superposition
qc.cx(0, 1)      # entangle: CNOT with 0 as control
qc.measure_all() # expect only 00 and 11 — never 01 or 10

On a perfect simulator: exactly 50% 00, 50% 11. On the real least-busy backend, you get mostly 00 and 11 — plus a few percent of 01 and 10 that shouldn't exist. Those forbidden outcomes are your first honest look at hardware noise: decoherence and gate error, quantified, staring back at you.

The lesson the hardware teaches: depth is the enemy

I also implemented Grover's search (an oracle that marks a target state, then a diffuser that amplifies it) and ran it both ideal and on hardware. Comparing them is where quantum computing stops being magic and becomes engineering:

That's the real curriculum of today's quantum machines (the "NISQ" era): every extra gate is a chance for the circuit to decohere before it finishes. You don't design for elegance, you design for shortness.

Making it something you can poke

The physics is only convincing if you can play with it, so the project page has two live, in-browser demos — no account, no install:

Those are exact simulators — real Qiskit can't run in a browser, and an IBM API key must never ship to a client — but the numbers they show line up with the theory, and the hardware results on the page come from the actual QPU runs.

What I actually took away

A build-story · see the live project → · more writing →