Imagine each small triangle as a room with three walls. Next we do a trick that I do not know how to motivate: treat each RG edge as a door. Here’s what we observe:
In other words, we can identify RGB triangles as the rooms with an odd number of doors. Sperner’s Lemma, then, can be restated in a more visual way:
If Sperner’s condition holds, there is at least one room with an odd number of doors.
We’ll prove a related version of this statement.
Picture all the possible paths that can be formed by stepping through these RG doors — the black lines in the diagram represent all of these paths.
The yellow triangles have paths that enter through one door and exit through another — like hallways.
The RGB triangles have only one door, so they are endpoints — there’s no way to pass through.
Using simple logic, we can deduce the following:
No two paths intersect.
Some paths form closed loops.
Paths that do not form loops must have two endpoints, so the total number of endpoints is even.
The only possible endpoints are:
An RG door on the outer boundary of the big triangle
An RGB triangle
This is where Sperner’s condition comes in — it controls which boundary edges are allowed.
Putting this all together gives us a key result:
Theorem 1 The sum of the number of RG boundary edges, and the number of RGB triangles must be even.
From this, we immediately get:
Theorem 2 The number of RGB triangles is odd if and only if the number of RG edges on the boundary is odd.
So to conclude the proof, all that remains is to show: The number of RG boundary edges is odd. This final step uses induction. The proof itself is short, but there’s a subtle trick — it’s easy to overlook unless you write it out carefully. Give it a shot!
viewof M =slider({min:1,max:25,step:1,value:5,width:500,title:"Number of subdivisions"})
viewof plot = {const colors =Array.from({length: M +1}, (_, i) => {if (i ===0) return"red";if (i === M) return"green";returnMath.random() <0.5?"red":"green"; });const x = d3.scaleLinear().domain([0, M]).range([20,580]);const svg = d3.create("svg").attr("width",600).attr("height",100);let boldCount =0;for (let i =0; i < M; i++) {const c1 = colors[i], c2 = colors[i +1];const bold = c1 !== c2;if (bold) boldCount++; svg.append("line").attr("x1",x(i)).attr("x2",x(i +1)).attr("y1",50).attr("y2",50).attr("stroke","black").attr("stroke-width", bold ?6:1); } svg.selectAll("circle").data(colors).join("circle").attr("cx", (_, i) =>x(i)).attr("cy",50).attr("r",5).attr("fill", d => d).attr("stroke","black"); svg.append("text").attr("x",150).attr("y",15).text(`Number of RG edges: ${boldCount}`).attr("font-size",20);return svg.node();}
Questions
The app at the top of this page demonstrates the proof by dividing a triangle uniformly into smaller triangles. However, Sperner’s Lemma is much more general: it applies to any triangulation of a triangle, as long as the vertices are colored according to Sperner’s condition. The proof itself never relies on the triangulation being uniform — so it holds for any triangulation. That said, the app is harder to generalize for arbitrary triangulations.
How does one generate random triangulations?
One natural approach is to use Delaunay triangulations, but how random are these triangulations? Once you have such a triangulation, the next step is:
How do you efficiently loop through all the triangles?
While this isn’t a research question, implementing a general Sperner’s Lemma checker on arbitrary triangulations would be a neat exercise, combining computational geometry with combinatorics.