- Demos/
- Quantum Computing/
Introducing the Surface Code
Introducing the Surface Code
Published: December 16, 2025. Last updated: January 21, 2026.
The surface code is the gold standard when it comes to quantum error correction (QEC). Despite its early inception in the 90s, it is still relevant in many modern quantum computing architectures today. During that timespan, it has evolved quite a bit. In this demo, we will give an overview of the inner workings of the surface code to perform quantum error correction. These principles are ubiquitous in modern QEC codes, so this demo should serve as a good starting point to get into QEC in 2026.
Introduction
We are going to take a look at the planar, two-dimensional, rotated surface code. This is a modern variant of the Kitaev surface code [1] [2] and not to be confused with the toric code or one of the many other variants that generalize it.
The rotated surface code consists of alternating X (blue) and Z (orange) squares that make up a lattice. The so-called data qubits sit on the vertices (highlighted in pink). These will be the qubits that encode the quantum information of a \(d \times d\) surface code patch, that is making up a single logical qubit: (we will use \(d=5\) throughout for simplicity)
We additionally have so-called syndrome qubits (in light gray) that sit in the middle of the squares. These syndrome qubits are used to continuously perform measurements on the surrounding data qubits in a non-destructive way. These measurements are called stabilizers and make up the backbone of almost all modern QEC codes. In the rotated surface code, they are alternating squares with a product of four \(X\) or \(Z\) operators. Additionally, there are weight-2 \(X\) and \(Z\) arches on the edges (more on that later).
These stabilizers on the data qubits are measured indirectly via the syndrome qubits. This is done by entangling the data qubits with the corresponding syndrome qubit and then measuring that (see Fig. 1 in [3] for the detailed circuits). The measurement result (\(\pm 1\)) of a stabilizer measurement indicates whether or not an error has occurred. A \(d \times d\) surface code qubit can detect up to \(d-1\) errors, and correct up to \(\left\lfloor \tfrac{d-1}{2} \right\rfloor\). When more errors occur, they may go unnoticed or get corrected in the wrong way, as we will see later.
In this demo, we are going to go into more detail and expand on each of these components. Before that, we want to stress the difference between the rotated surface code (left) to the original planar surface code (right):
In the central image we see their correspondences. Note that the solid pink lines are merely a guide to the eye and do not represent physical connectivity. They are typically used in this way to discern face plaquette operators of \(Z\) stabilizers, and vertex or star operators of \(X\) stabilizers. They are very similar, but the rotated surface code effectively halves the required qubits from \(4d^2 - 4d + 1\) to \(2d^2-1\) for a given code-distance \(d\).
For the purpose of this demo, we shall refer to it just as the surface code.
Stabilizers
The surface code is a stabilizer code, which means that quantum information is encoded in the joint \(+1\) eigenspace of a set of commuting observables \(\mathcal{S}\) called stabilizers. These stabilizers \(S_i \in \mathcal{S}\) are faces of four or two \(Z\) or \(X\) observables, arranged in a checkerboard formation: (the stabilizer operators are their product)
All these stabilizers commute with each other - that is one of their defining properties.
That all stabilizers in the surface code depicted here commute can be seen from the fact that generally two
Pauli words commute iff they anticommute on an even number of sites.
Take for example the most simple case, two sites [0, 1], such that we have \([X_0 X_1, Z_0 Z_1] = 0\).
The measurement outcome of such a stabilizer is binary \(\pm 1\) and we assume that the underlying quantum state \(|\psi\rangle\) that is encoded by the qubit of the surface code is in the so-called code (sub)space - the joint \(+1\) eigenspace of all stabilizer measurements. That means that the action of any stabilizer \(S_i \in \mathcal{S}\) on a state \(|\psi\rangle\) in the code space is equal to the identity, \(S_i |\psi\rangle = + 1 |\psi\rangle\).
By continuously measuring all stabilizers we can ensure that the state is not leaving the code space. If, however, we do measure \(-1\) somewhere, we know that an error has occurred. In that case, we need to perform error correction, which we are discussing later.
Logical operators: Z and X edges
You may have noticed that we have weight-2 stabilizers (arches) at the edges of our surface code patch. These are crucial for defining logical operators on the surface code patch.
Logical operators \(Z_L\) or \(X_L\) need to commute with all stabilizers so they don’t move our state outside the code space. At the same time, they must not be stabilizers (or products thereof) themselves. On top of that, they of course need to satisfy the fundamental anti-commutation relation \(X_L Z_L = -Z_L X_L\).
On the rotated surface code, a logical \(X_L\) operator is a string of measurements of data qubits that connects the two edges with \(X\) arches (left and right here). And vice versa for a logical \(Z_L\) operator, as indicated below.
Multiplying a logical operator by a stabilizer does not change the logical state, so the string on the right hand side is an equivalent logical operator (recall that \(Z^2=\mathbb{1}\)).
We can continue to deform the string to arrive at a logical \(Z_L\) operator that goes along the right edge.
This is why the left and right edge are called \(Z\) edges, which may be confusing because they contain \(X\) arches. The opposite is true for the top and bottom \(X\) edges with \(Z\) arches.
These arches define the logical operators, but they are also crucial from a mathematical point of view and restrict our \(d \times d\) patch to exactly one logical qubit. That is because the number of encoded qubits \(k\) is given by the difference of data qubits \(n\) and independent stabilizers \(s\), so \(k = n - s\). For the \(5 \times 5\) patch we are considering, we have \(n=25\) data qubits and \(16\) weight-4 stabilizers. Together with the \(8\) arches we get \(k = 25 - 16 - 8 = 1\).
The arches are also important for coverage of all possible errors. E.g., if a \(X\) error occurred on the top left data qubit, only the top left \(Z\) arch would catch it.
Quantum computation via lattice surgery
There are different variants of how to perform quantum computation with the surface code. Braiding is an older approach [4], but most modern approaches use lattice surgery [6] [5]. The concept is relatively simple: To measure \(Z_L \otimes Z_L\) between two surface code qubits, simply connect them via their \(Z\) edge (lattice merging), perform \(d\) rounds of measuring all stabilizers, including the intermediary ones, and finally destructively measure in between the two patches to split them again (lattice splitting). The logical \(Z_L \otimes Z_L\) measurement is indirectly determined via the product of the stabilizers that have been measured during the intermediate rounds of error correction. Note that this is different from terminal measurements where data qubits are measured directly.
This is important because most modern surface code constructions are targeting Pauli based computation, where all logical operations can be reduced to such Pauli product measurements.
In fact, in the Game of Surface Codes [7], a popular framework for thinking about fault tolerant quantum computers, we forget about everything but the \(X\) and \(Z\) edges of our qubit patches. This results in rectangular boxes with solid (\(Z\)) and dotted (\(X\)) edges. The same \(Z_L \otimes Z_L\) measurement from above can be portrayed as
This diagram simply says, we measure qubits \(|q_1\rangle\) and \(|q_2\rangle\) along their \(Z\) edges via an intermediate auxiliary qubit region, indicated by the blue connection. So overall, this is just the joint measurement of \(Z_{q_1} \otimes Z_{q_2}\) via their \(Z\) edges.
Error correction
Let us first consider what actually happens if a single \(Z\) error occurs on one of the data qubits. The story works equivalently for \(X\) errors. Before the \(Z\) error, each stabilizer measurement returns \(+1\), confirming the underlying state is in the correct code space. Now let us assume the central data qubit experiences a \(Z\) error. The surrounding \(Z\) stabilizers are unaffected by it, but the two \(X\) stabilizers yield a \(-1\) measurement - a defect, indicated by \(-1\) on the stabilizer square.
The tricky part about error correction is that we are only ever given the information of the syndrome measurements and do not know what actually has physically happened. The same error syndromes could have also occurred due to, e.g., the following error pattern.
This scenario is, however, exponentially more unlikely. A common decoding algorithm is minimum-weight perfect matching (MWPM), that looks for the shortest (and thus most likely) error string and corrects that. In this scenario, the (by far) most likely error string is simply the central \(Z\) error.
Consider the following situation, where two different weight-2 error strings lead to the same error syndrome.
Here, both errors have the same minimum distance, so the choice for an MWPM decoder is ambiguous. Luckily, it does not matter which error we correct, as they are logically equivalent: they are the same error up to a \(Z\) stabilizer, in particular the one on the surface between the two defects.
In the following scenario, however, we will run into a real problem. Both error strings again lead to the same defect syndrome.
Now, if the (less likely) error with three errors occurs, but we correct for the (more likely) second scenario, we overall perform a logical \(Z_L\) operation, and introduce an undetected error in our computation. This is a manifestation of the fact that a distance \(d=5\) rotated surface code qubit can only correct
errors deterministically.
Intuitively, this makes sense. In a code with distance \(d\), logical operators are (at least) of weight \(d\). Logical operators change the logical state of the qubit without being noticed by any stabilizer. So the best we can do is detect errors up to \(d-1\). And we can only deterministically correct errors up to half the distance, because a wrong correction will make the total operation (error + correction) a logical operator that goes unnoticed.
Error correction is continuously performed during computation with one clock cycle corresponding to measuring all \(\mathcal{O}(d^2)\) stabilizers once. It is worth noting that the actual error correction typically happens in software, and no correction terms are actively applied. Instead, one typically tracks all detected errors based on their syndromes and then multiplies them retrospectively with the final measurement results at the end of the computation.
Conclusion
In this demo, we have learned about the basics of modern rotated surface codes from qubit definition, stabilizers, logical operators, computation to error decoding. Most modern quantum error correction codes such as qLDPC codes work under the same principles, so you should be well-prepared for continuing your QEC journey down this path.
References
About the author
Korbinian Kottmann
Korbinian likes simulating quantum systems, whether it be via tensor network methods during his PhD, or with quantum computers at Xanadu. Currently, he works on quantum compilation to make simulation algorithms go brrr.