A book I was recently reading mentioned a mathematical curiosity I haven't seen before - Tupper's self-referential formula. There are some resources about it online, but this post is my attempt to explain how it works - along with an interactive implementation you can try in the browser.

Tupper's formula

Here is the formula:

\[\frac{1}{2}< \left \lfloor mod\left ( \left \lfloor \frac{y}{17}\right \rfloor 2^{-17\lfloor x \rfloor - mod(\lfloor y \rfloor, 17)}, 2 \right ) \right \rfloor\]

We want to plot this formula, but how?

For this purpose, it's more useful to think of Tupper's formula not as a function but as a relation, in the mathematical sense. In Tupper's paper this is a relation on \mathbb{R}, meaning that it's a set of pairs in \mathbb{R} \times \mathbb{R} that satisfy the inequality.

For our task we'll use discrete indices for x and y, so the relation is on \mathbb{N}. We'll plot the relation by using a dark pixel (or square) for a x,y coordinate where the inequality holds and a light pixel for a coordinate where it doesn't hold.

The "mind-blowing" fact about Tupper's formula is that when plotted for a certain range of x and y, it produces this:

Tupper's formula own plot

Note that while x runs in the inclusive range of 0-105 on the plot, y starts at a mysterious K and ends at K+16. For the plot above, K needs to be:


The amazement subsides slightly when we discover that for a different K [1], we get a different plot:

Tupper's formula producing a pacman plot

And, in fact, this formula can produce any 2D grid of 106x17 pixels, given the right coordinates. Since the formula itself is so simple, it is quite apparent that the value of K is the key here; these are huge numbers with hundreds of digits, so clearly they encode the image information somehow. Read on to see how this actually works.

A JavaScript demo

I've implemented a simple online demo of plotting the Tupper formula - available at https://eliben.github.io/tupperformula/ (with source code on GitHub). It was used to produce the images shown above. The code is fairly straightforward, so I'll just focus on the interesting part.

The core of the code is a 2D grid that's plotted for x running from 0 to 105 and y from K to K+16 (both ranges inclusive). The grid is populated every time the number changes:

const GridWidth = 106;
const GridHeight = 17;
let K = BigInt(Knum.value);

for (let x = 0; x < GridWidth; x++) {
    for (let y = 0; y < GridHeight; y++) {
        Grid.setCell(x, y, tupperFormula(BigInt(x), K + BigInt(y)));

Note the use of JavaScript's BigInt types here - very handy when dealing with such huge numbers. Here is tupperFormula:

function tupperFormula(x, y) {
    let d = (y / 17n) >> (17n * x + y % 17n);
    return d % 2n == 1n;

It looks quite different from the mathematical formula at the top of this post; why? Because - as mentioned before - while Tupper's original formula works on real numbers, our program only needs the discrete integer range of x in [0, 105] and y in [K, K+16]. When we deal with discrete numbers, the formula can be simplified greatly.

Let's start with the original formula and simplify it step by step:

\[\frac{1}{2}< \left \lfloor mod\left ( \left \lfloor \frac{y}{17}\right \rfloor 2^{-17\lfloor x \rfloor - mod(\lfloor y \rfloor, 17)}, 2 \right ) \right \rfloor\]

First of all, since x and y are natural numbers, the floor operations on them don't do anything, so we can drop them (including on the division by 17, if we just assume integer division that rounds down by default):

\[\frac{1}{2}< \left \lfloor mod\left ( \left ( \frac{y}{17}\right ) 2^{-17x - mod(y, 17)}, 2 \right ) \right \rfloor\]

Next, since the result of the mod(N,2) operation for a natural N is either 0 or 1, the comparison to half is just a fancy way of saying "equals 1"; we can replace the inequality by:

\[mod\left ( \left ( \frac{y}{17}\right ) 2^{-17x - mod(y, 17)}, 2 \right )=1\]

Note the negative power of 2; multiplying by it is the same as dividing by its positive counterpart. Another way to express division by 2^p for natural numbers is a bit shift right by p bits. So we get the code of the tupperFormula function shown above:

let d = (y / 17n) >> (17n * x + y % 17n);
return d % 2n == 1n;

How the Tupper formula works

The distillation of the Tupper to JS code already peels off a few layers of mystery. Let's now remove the rest of the curtain on its inner workings.

I'll start by explaining how to take an image we want the formula to produce and encode it into K. Here are the first three columns of the Tupper formula plot:

Closeup of tupper plot with encoding of pixels

Each pixel in the plot is converted to a bit (0 for light, 1 for dark). We start at the bottom left corner (x=0 and y=K), which is the LSB (least-significant bit) and move up through the first column; when we reach the top (x=0 and y=K+16), we continue from the bottom of the next column (x=1 and y=K). In the example above, the first bits (from lowest to highest) of the number are:

00110010101000100 00101010101111100 ...

Once we're done with the whole number (106x17 = 1802 bits), we convert it to decimal - let's call this number IMG, and multiply by 17. The result is K.

Now back to tupperFormula, looking at how it decodes the image back from x and y (recall that y runs from K to K+16). Let's work through the first coordinate in detail:

For x=0 and y=K, in tupperFormula we get:

d = (y/17) >> (17x + y%17)
substitute x=0, y=K (and recall that K = IMG * 17)
d = IMG >> 0

In other words, d is the lowest bit of IMG - the lowest bit of our image! We can continue for x=0 and y=K+1:

d = (y/17) >> (17x + y%17)
substitute x=0, y=K+1 (and recall that K = IMG * 17)
d = IMG >> 1

Here d is the second lowest bit of IMG. The pattern should be clear by now.

d = (y/17) >> (17x + y%17)
x=0  y=K+2:  IMG >> (0 + 2)
x=0  y=K+3:  IMG >> (0 + 3)
x=0  y=K+16  IMG >> (0 + 16)
x=1  y=K:    IMG >> (17 + 0)
x=1  y=K+1:  IMG >> (17 + 1)
x=1  y=K+2:  IMG >> (17 + 2)

The formula simply calculates the correct bit of IMG given x and y, using a modular arithmetic trick to "fold" the 2D x and y into a 1D sequence (this is just customary column-major layout).

This is why the formula can plot any 106x17 grid, given the right K. In the formula, 17 is not some piece of magic - it's just the height of the grid. As an exercise, you can modify the formula and code to plot larger or smaller grids.

As a bonus, the JavaScript demo can also encode a grid back to its representative K; here's the code for it:

// Calculate K value from the grid.
function encodeGridToK() {
    let kval = BigInt(0);

    // Build up K from MSB to LSB, scanning from the top-right corner down and
    // then moving left by column.
    for (let x = GridWidth - 1; x >= 0; x--) {
        for (let y = GridHeight - 1; y >= 0; y--) {
            kval = 2n * kval + BigInt(Grid.getCell(x, y));
    return kval * 17n;

It constructs K starting with the MSB, but otherwise the code is straightforward to follow.


The formula was first describe by Jeff Tupper in a 2001 paper titled "Reliable Two-Dimensional Graphing Methods for Mathematical Formulae with Two Free Variables". The paper itself focuses on methods of precisely graphing relations and presents several algorithms to do so. This formula is described in passing in section 12, and presented as follows:

Screenshot from Tupper's paper describing the formula

And Figure 13 is:

Screenshot from Tupper's paper showing the formula itself

Interestingly, the K provided by Tupper's paper renders the formula flipped on both the x and y axes using the standard grid used in this post [2]. This is why my JavaScript demo has flip toggles that let you flip the axes of any plot.

[1]This would be
[2]I can totally see why the y axis would be flipped: in computer programs the concept of the y axis is represented as rows in a grid which typically count from 0 on top and downwards. It's less clear to me how the inversion on the x axis came to be.