Draw My Canvas / studio

Ulam Spiral

Write 1 in the middle of a grid, then keep counting in a square spiral outward: 2 to its right, 3 above that, 4 and 5 to the left, and so on. Now light only the primes. They should look scattered, and mostly they do, but they also line up in diagonal streaks. This plate counts the spiral live, starting with cells big enough to read and zooming out until the whole stage is one field of primes. The coral diagonal is 4n² + 12n + 7, which is never a multiple of 2, 3 or 5.

Plate 43number-theory

Waiting for the animation above to load…

What lands in the file, and what the width does

One click writes whatever the animation is drawing at that moment to a PNG, with the drawmycanvas.com mark drawn into the picture rather than laid over it. Leave the width box empty and you get the stage exactly as your browser rasterised it — your window’s width times its device pixel ratio, which is about 1,600 px across from a 1280‑px window on a HiDPI laptop and about 353 px from a 390‑px phone.

Type a width instead, or take a preset, and the frame is redrawn into a canvas that wide: the height follows the stage’s own shape and the mark scales with it. The stage is 16:9 on a wide window and 4:3 below 560 px, so a width of 1200 saves 1200×675 on a laptop and 1200×900 on a phone. 1200 px is the width Open Graph and X link cards are cut from — the canonical card is 1200×630, and a card crops the extra height rather than letterboxing it.

Honest limits. Asking for more pixels than the stage was drawn at resamples pixels that were never drawn: a 1920‑px file exported from a 353‑px phone stage is bigger, not sharper. For a big file that is sharp, use Save as wallpaper: it draws the plate again from scratch at exactly 1179×2556 or 1290×2796 (phones), 1920×1080, 2560×1440 or 3840×2160 (4K), so every line is rasterised at that size and the shape is the screen’s, never stretched. Because the plate restarts, a wallpaper is a fresh run of it rather than the exact frame on screen; it is given as long as the stage has been running, up to 20 seconds, to develop. A very large width is a real memory allocation and a browser is allowed to refuse it; when that happens the line above says so plainly and nothing else on the page changes. Stills are PNG only — no JPEG, no WebP. On browsers that can record video, Record a clip saves 5, 10 or 20 seconds of the running animation as MP4 or WebM (whichever this browser can encode) at the same width, with the mark in every frame. A clip is not a seamless loop, it has no audio, and a width bigger than the stage is resampled rather than sharper — only the wallpaper is redrawn at its size. Where a browser cannot record video the button never appears and a PNG is the only export. And nothing is uploaded: the frame or clip is assembled in your own browser, so no frame of this plate ever reaches us.

Live on an HTML canvas · vanilla JavaScript · no dependencies Open fullscreen

About this piece

The count starts at 1 in the centre and spirals outward. While the cells are big you can read every number: primes are filled squares with the number cut out of them, other numbers are written in faint ink, and a thin line follows the order of the count. The cells shrink as the spiral grows, from at most 44 to 3 pixels, and the numbers and the path drop out when they no longer fit. The ring being counted right now lights its primes in the text ink, so you can see the front move; behind it they settle into cobalt, slightly deeper towards the rim. The count takes 16 seconds, the finished spiral is held for 5, and then it starts again.

How far it counts depends on the stage: as many square rings as fit at 3 pixels a cell. In a 1280×720 window that is 92 rings, a 185×185 square, so the count stops at 185² = 34,225, of which 3,656 (10.7%) are prime. The line along the bottom keeps that tally live. The line at the top names the coral diagonal, lists its latest values as the count reaches them and says how many of them are prime. With reduced motion switched on you get one still frame: the finished spiral.

How the spiral is numbered

Start at 1 and walk: right 1, up 1, left 2, down 2, right 3, up 3, and so on, writing the next number in every cell you step on. Each pair of legs is one longer than the last, so ring k (the square of side 2k + 1 around the centre) is finished at the number (2k + 1)². That is why the odd squares 9, 25, 49, 81, … sit in a line running from 1 to the bottom-right corner, and the even squares 4, 16, 36, 64, … in a line running up and to the left.

A second pattern falls out of the walk. Every step moves one cell, and changes both the count and x + y by one, so the odd numbers sit on one colour of a checkerboard and the even numbers on the other. Every prime but 2 is odd, so every prime but 2 sits on the odd colour. Along a diagonal, x + y changes by 0 or 2 per step, so each diagonal is either all odd or all even. Half of all diagonals can never hold a prime except 2, which is the first reason the picture looks streaky.

Why the diagonals appear

Walk out along any diagonal and every step crosses into the next ring, which is 8 cells longer than the one before. So the gaps between neighbours on a diagonal grow by 8 each step, and the numbers on it are given by a quadratic 4n² + bn + c. Some of those quadratics can never be divisible by small primes, while others are divisible by 3 or 5 on a fixed share of their values. A number that cannot be a multiple of 3 or 5 is much more likely to be prime than an ordinary odd number of the same size, so those diagonals come out bright.

Worked example, the coral diagonal. 4n² + 12n + 7 gives 7, 23, 47, 79, 119, 167, 223, …, on the cells running from 7 (just below and left of 1) to the bottom-right. It is odd for every n. Divided by 3 it always leaves the same remainder as n² + 1, which is 1 or 2, never 0. Divided by 5 it leaves only 2, 3 or 4. So 2, 3 and 5 never divide it. 7 does: 119 = 7 × 17 is the first composite on it, the ringed square on screen. For n = 0 to 99 (values up to 40,399) it is prime 42 times. Among all odd numbers from 7 to 40,399, about 21% are prime, so the coral diagonal is lit about twice as often.

The four rays that leave 1 itself show the contrast. Counted over n = 0 to 99, the same span as above:

Ray from 1QuadraticStartsPrimes in 100Never a multiple of (2, 3, 5, 7)
down-right4n²+4n+11, 9, 25, 4902
down-left4n²+2n+11, 7, 21, 43252, 5
up-right4n²−2n+11, 3, 13, 31322, 5
up-left4n²+11, 5, 17, 37332, 3, 7
coral, from 74n²+12n+77, 23, 47, 79422, 3, 5

The down-right ray is the odd squares, (2n + 1)², so it has no primes at all and shows as a dark line. Each n = 0 row also counts 1, which is not prime. Most of the lines you see in the picture are short runs of this kind of bias rather than single perfect diagonals.

Where it comes from, and what is not known

Stanisław Ulam drew the spiral in 1963, doodling on a grid during a dull talk, and Martin Gardner made it famous in his Mathematical Games column in Scientific American in March 1964. Long before that, Euler had noticed that n² + n + 41 is prime for every n from 0 to 39. If you start the spiral at 41 instead of 1, those 40 primes fall on one diagonal.

What the picture suggests is still an open problem. Nobody has proved that any quadratic such as n² + 1 takes infinitely many prime values (that is Bunyakovsky’s conjecture, open since 1857). Hardy and Littlewood’s Conjecture F, from 1923, predicts how dense the primes on each such quadratic should be, and the counts match it well, but it is a conjecture, not a theorem. The spiral shows the bias; it does not explain it.

How it is computed

The plate walks the spiral leg by leg (it never uses a formula for a cell’s position) and tests every number up to the last by trial division by 2, 3 and then 6k ± 1. Both are done once per stage size, so a frame only redraws: how far the count has got is a pure function of the plate’s age, and the frame rate never changes what is shown. The math lives in one block of code, used by the gallery tile, the embed and a node module that the test suite loads. The suite walks the spiral to 10,000 and checks every cell against the closed-form position formula written separately in the test. It checks the prime test against a sieve of Eratosthenes for every number up to 1,000,000, and checks that every value of 4n² + 12n + 7 on screen lies on the line x + y = −2. It also checks the table above against the code. A planted run that marks the composite 119 as prime turns the suite red.

A 30-line version

The snippet below draws the finished spiral in the site’s palette, 3 pixels a number, with the coral diagonal, in 27 lines of HTML and plain JavaScript with no library. It is an Ulam spiral generator in its simplest form: walk, test, fill. It draws once and does not animate, so it needs no reduced-motion handling. The test suite runs it against a stub canvas and checks that it lights exactly the plate’s primes, in the plate’s cells. It leaves out the count, the zoom, the numbers, sharp drawing on high-density screens (it draws in CSS pixels) and the reaction to theme changes. Its plain trial division is fine up to a few hundred thousand numbers; past that, use a sieve.

Things worth changing: C = 1 shows the classic one-pixel look with far more rings. Starting the count at 41 instead of 1 (write n + 40 into isPrime) puts Euler’s 40 primes on one diagonal.

<canvas id="ulam" style="position:fixed; inset:0"></canvas>
<script>
const dark = matchMedia('(prefers-color-scheme: dark)').matches;
const [ground, ink, accent] = dark ? ['#0B0D12', '#96B4FF', '#FF7854']
                                   : ['#E7E2D5', '#2848CD', '#BE441C'];  // the site's palette
const cv = document.getElementById('ulam'), ctx = cv.getContext('2d');
const W = cv.width = innerWidth, H = cv.height = innerHeight;
const C = 3;                                         // pixels per number
const K = Math.floor((Math.min(W, H) / C - 1) / 2);  // rings that fit on screen
const N = (2 * K + 1) ** 2;                          // so the count stops at an odd square
function isPrime(n) { for (let d = 2; d * d <= n; d++) if (n % d === 0) return false; return n > 1; }
const diag = new Set();                              // 4n² + 12n + 7: 7, 23, 47, 79, ...
for (let n = 0; 4 * n * n + 12 * n + 7 <= N; n++) diag.add(4 * n * n + 12 * n + 7);
ctx.fillStyle = ground; ctx.fillRect(0, 0, W, H);
let x = 0, y = 0, dx = 1, dy = 0, leg = 1, steps = 0, turns = 0;
for (let n = 1; n <= N; n++) {
  if (isPrime(n)) {
    ctx.fillStyle = diag.has(n) ? accent : ink;
    ctx.fillRect(W / 2 + x * C - C / 2, H / 2 - y * C - C / 2, C, C);  // y grows upward
  }
  x += dx; y += dy;
  if (++steps === leg) {                             // end of a leg: turn left
    steps = 0; [dx, dy] = [-dy, dx];
    if (++turns % 2 === 0) leg++;                    // legs run 1, 1, 2, 2, 3, 3, ...
  }
}
</script>
Paste it into an empty .html file.

The plates it sits nearest

  • Quantum Fibonacci also places numbered points one after another on a spiral, there at the golden angle, and patterns appear that nobody put in by hand.
  • Hilbert Weave is another way to walk every cell of a square grid in one unbroken path.
  • Sierpinski Chaos Game is another plate where a simple rule repeated thousands of times shows structure only once enough marks are down.

Honest limits

The streaks are a bias, not a law. No diagonal is all prime. Even the coral one is composite more often than not: 58 of its first 100 values are not prime.

The picture depends on where you start. Start at 41 or at 17 and different diagonals light up. The spiral layout makes quadratics visible as lines; it does not make the primes special.

The cells are 3 pixels at the end. That keeps the diagonals visible on a phone, but it means a 390×844 phone, full screen, counts to 13,689 rather than the hundreds of thousands a 1-pixel print would show. A 4K wallpaper counts further, up to 260 rings (521² = 271,441).

Curious how the loop and canvas fit together? Read how it works →

More from the gallery

All 44 animations in the gallery →