Blog & Insights

Thoughts, tutorials, and explorations at the intersection of mathematics, visualization, and web development.

Visualizing Prime Number Patterns

Published: March 15, 2025Category: Mathematics

Visualizing Prime Number Patterns

Prime numbers—those mysterious integers divisible only by 1 and themselves—have fascinated mathematicians for millennia. While their distribution appears random at first glance, visualizing prime numbers in various ways reveals surprising patterns and structures. In this article, we'll explore different methods for visualizing prime numbers and the insights these visualizations provide.

The Fundamental Nature of Primes

Prime numbers are the building blocks of our number system. Every positive integer can be uniquely expressed as a product of primes, making them fundamental to number theory and cryptography.

The largest known prime number (as of 2025) is 2^82,589,933 - 1, which has 24,862,048 digits. It was discovered by the Great Internet Mersenne Prime Search (GIMPS) project.

The Sieve of Eratosthenes

Before diving into visualizations, let's start with a classic algorithm for finding primes: the Sieve of Eratosthenes. This ancient algorithm efficiently identifies all primes up to a given limit.

1function sieveOfEratosthenes(limit) {
2  // Create an array of booleans, all initially true
3  const isPrime = new Array(limit + 1).fill(true);
4  
5  // 0 and 1 are not prime
6  isPrime[0] = isPrime[1] = false;
7  
8  // Start with the first prime number, 2
9  for (let i = 2; i * i <= limit; i++) {
10    // If i is prime, mark all its multiples as non-prime
11    if (isPrime[i]) {
12      for (let j = i * i; j <= limit; j += i) {
13        isPrime[j] = false;
14      }
15    }
16  }
17  
18  // Collect all prime numbers
19  const primes = [];
20  for (let i = 2; i <= limit; i++) {
21    if (isPrime[i]) {
22      primes.push(i);
23    }
24  }
25  
26  return primes;
27}
28
29// Get all primes up to 100
30const primes = sieveOfEratosthenes(100);
31console.log(primes);
32// [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
33

Visual Representations of Prime Numbers

1. The Number Line

The simplest visualization is marking primes on a number line:

10   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15
2    |   *   *   |   *   |   *   |   |   |   *   |   *   |   |
3    
416  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31
5|   *   |   *   |   |   |   *   |   |   |   |   |   *   |   *
6
7* = prime number
8| = composite number
9

Even in this basic representation, we can observe that primes become less frequent as numbers increase, and they never appear in a completely regular pattern.

2. The Ulam Spiral

In 1963, mathematician Stanisław Ulam discovered an intriguing pattern while doodling during a boring meeting. He arranged integers in a spiral and marked the primes:

117 16 15 14 13
218  5  4  3 12
319  6  1  2 11
420  7  8  9 10
521 22 23 24 25
6

When extended to larger numbers and with primes highlighted, diagonal lines and other patterns emerge. This suggests that certain quadratic formulas generate an unusually high number of primes.

Here's an interactive visualization of the Ulam Spiral. The blue dots represent prime numbers, and you can see the fascinating patterns that emerge:

Let's implement the Ulam Spiral:

1function createUlamSpiral(size) {
2  // Size should be odd to have a center
3  if (size % 2 === 0) size++;
4  
5  // Create a 2D array filled with zeros
6  const spiral = Array(size).fill().map(() => Array(size).fill(0));
7  
8  // Find the center
9  const center = Math.floor(size / 2);
10  
11  // Start at the center with 1
12  let x = center;
13  let y = center;
14  spiral[y][x] = 1;
15  
16  // Direction vectors: right, up, left, down
17  const dx = [1, 0, -1, 0];
18  const dy = [0, -1, 0, 1];
19  
20  let direction = 0; // Start going right
21  let steps = 1;     // Steps to take in current direction
22  let stepCount = 0; // Steps taken in current direction
23  let num = 1;       // Current number
24  
25  // Fill the spiral
26  while (num < size * size) {
27    // Move in current direction
28    x += dx[direction];
29    y += dy[direction];
30    num++;
31    
32    // Check if out of bounds
33    if (x < 0 || x >= size || y < 0 || y >= size) break;
34    
35    // Place the number
36    spiral[y][x] = num;
37    
38    // Increment step count
39    stepCount++;
40    
41    // Change direction if needed
42    if (stepCount === steps) {
43      stepCount = 0;
44      direction = (direction + 1) % 4;
45      
46      // Increase steps after going up or down
47      if (direction === 0 || direction === 2) {
48        steps++;
49      }
50    }
51  }
52  
53  return spiral;
54}
55
56// Generate primes up to size*size
57const primes = new Set(sieveOfEratosthenes(size * size));
58
59// Mark primes in the spiral
60function markPrimesInSpiral(spiral, primes) {
61  const markedSpiral = spiral.map(row => 
62    row.map(num => primes.has(num) ? '*' : ' ')
63  );
64  return markedSpiral;
65}
66

3. The Sacks Spiral

The Sacks spiral (or polar coordinate representation) arranges integers in a spiral where the angle is proportional to the logarithm of the number. This creates a different perspective on prime distribution:

1function generateSacksCoordinates(n) {
2  const coordinates = [];
3  
4  for (let i = 1; i <= n; i++) {
5    // Calculate polar coordinates
6    const radius = Math.sqrt(i);
7    const theta = 2 * Math.PI * Math.sqrt(i);
8    
9    // Convert to Cartesian coordinates
10    const x = radius * Math.cos(theta);
11    const y = radius * Math.sin(theta);
12    
13    coordinates.push({ number: i, x, y, isPrime: isPrime(i) });
14  }
15  
16  return coordinates;
17}
18

When plotted, the Sacks spiral reveals striking patterns of primes along certain spiral arms, suggesting underlying structures in their distribution.

4. Prime Number Heatmap

A heatmap can visualize the density of primes in different regions:

1function createPrimeHeatmap(width, height, maxNum) {
2  const heatmap = Array(height).fill().map(() => Array(width).fill(0));
3  const primes = new Set(sieveOfEratosthenes(maxNum));
4  
5  for (let y = 0; y < height; y++) {
6    for (let x = 0; x < width; x++) {
7      const num = y * width + x + 1;
8      if (num <= maxNum && primes.has(num)) {
9        // Mark surrounding cells to create a heat effect
10        for (let dy = -2; dy <= 2; dy++) {
11          for (let dx = -2; dx <= 2; dx++) {
12            const ny = y + dy;
13            const nx = x + dx;
14            if (ny >= 0 && ny < height && nx >= 0 && nx < width) {
15              // Add heat based on distance
16              const distance = Math.sqrt(dx*dx + dy*dy);
17              const heat = 1 / (1 + distance);
18              heatmap[ny][nx] += heat;
19            }
20          }
21        }
22      }
23    }
24  }
25  
26  return heatmap;
27}
28

5. Modular Patterns

Primes exhibit interesting patterns when viewed through modular arithmetic. For example, plotting primes modulo 4:

1function categorizeByMod(primes, modulus) {
2  const categories = {};
3  
4  for (let i = 0; i < modulus; i++) {
5    categories[i] = [];
6  }
7  
8  for (const prime of primes) {
9    const remainder = prime % modulus;
10    categories[remainder].push(prime);
11  }
12  
13  return categories;
14}
15
16// Categorize primes by remainder when divided by 4
17const primesByMod4 = categorizeByMod(primes, 4);
18console.log(primesByMod4);
19// {
20//   0: [],           // No primes are divisible by 4
21//   1: [5, 13, 17, 29, 37, 41, ...],
22//   2: [2],          // Only 2 is even and prime
23//   3: [3, 7, 11, 19, 23, 31, ...]
24// }
25

This reveals that except for 2, all primes are either 1 or 3 modulo 4, which is a well-known result in number theory.

Interactive Visualizations with Web Technologies

Modern web technologies allow us to create interactive visualizations of prime patterns. Here's how you might implement an interactive Ulam spiral using HTML Canvas and JavaScript:

1function drawUlamSpiral(canvasId, size, maxNum) {
2  const canvas = document.getElementById(canvasId);
3  const ctx = canvas.getContext('2d');
4  const width = canvas.width;
5  const height = canvas.height;
6  
7  // Clear canvas
8  ctx.fillStyle = 'white';
9  ctx.fillRect(0, 0, width, height);
10  
11  // Generate primes
12  const primes = new Set(sieveOfEratosthenes(maxNum));
13  
14  // Calculate cell size
15  const cellSize = Math.min(width, height) / size;
16  
17  // Create spiral
18  const spiral = createUlamSpiral(size);
19  
20  // Draw spiral
21  for (let y = 0; y < size; y++) {
22    for (let x = 0; x < size; x++) {
23      const num = spiral[y][x];
24      if (num > 0 && num <= maxNum) {
25        const isPrime = primes.has(num);
26        
27        if (isPrime) {
28          ctx.fillStyle = 'blue';
29          ctx.fillRect(
30            x * cellSize,
31            y * cellSize,
32            cellSize,
33            cellSize
34          );
35        }
36      }
37    }
38  }
39}
40
41// Usage
42drawUlamSpiral('ulamCanvas', 101, 10000);
43

Mathematical Insights from Visualizations

These visualizations aren't just aesthetically pleasing—they provide genuine mathematical insights:

Prime Number Theorem

The prime number theorem describes the asymptotic distribution of primes. Visualizations help us understand how the density of primes decreases logarithmically as numbers increase.

Twin Primes and Other Patterns

Visualizations highlight patterns like twin primes (pairs of primes that differ by 2, like 11 and 13) and prime quadruplets (four primes in close proximity).

Dirichlet's Theorem

Dirichlet's theorem states that for any two coprime integers a and d, there are infinitely many primes of the form a + nd. Modular visualizations help illustrate this concept.

Advanced Visualizations

Riemann Zeta Function

The Riemann zeta function is intimately connected to the distribution of primes. Visualizing its zeros provides insights into prime number patterns:

1function riemannZeta(s, terms = 1000) {
2  let sum = 0;
3  
4  for (let n = 1; n <= terms; n++) {
5    sum += 1 / Math.pow(n, s);
6  }
7  
8  return sum;
9}
10
11function plotRiemannZeta(canvasId, xMin, xMax, yMin, yMax, resolution) {
12  const canvas = document.getElementById(canvasId);
13  const ctx = canvas.getContext('2d');
14  const width = canvas.width;
15  const height = canvas.height;
16  
17  // Clear canvas
18  ctx.fillStyle = 'black';
19  ctx.fillRect(0, 0, width, height);
20  
21  // Plot points
22  for (let px = 0; px < width; px += resolution) {
23    for (let py = 0; py < height; py += resolution) {
24      // Convert pixel coordinates to complex plane
25      const x = xMin + (xMax - xMin) * (px / width);
26      const y = yMin + (yMax - yMin) * (py / height);
27      
28      // Calculate zeta function for complex number s = x + iy
29      const real = x;
30      const imag = y;
31      
32      // This is a simplified approximation
33      const zeta = riemannZeta(real, 100);
34      
35      // Color based on value
36      const intensity = Math.min(255, Math.max(0, Math.abs(zeta) * 50));
37      ctx.fillStyle = `rgb(${intensity}, ${intensity / 2}, ${intensity / 4})`;
38      ctx.fillRect(px, py, resolution, resolution);
39    }
40  }
41}
42

Prime Gap Visualization

Prime gaps (the difference between consecutive primes) can reveal interesting patterns:

1function visualizePrimeGaps(canvasId, maxNum) {
2  const canvas = document.getElementById(canvasId);
3  const ctx = canvas.getContext('2d');
4  const width = canvas.width;
5  const height = canvas.height;
6  
7  // Generate primes
8  const primes = sieveOfEratosthenes(maxNum);
9  
10  // Calculate gaps
11  const gaps = [];
12  for (let i = 1; i < primes.length; i++) {
13    gaps.push(primes[i] - primes[i-1]);
14  }
15  
16  // Find max gap for scaling
17  const maxGap = Math.max(...gaps);
18  
19  // Clear canvas
20  ctx.fillStyle = 'white';
21  ctx.fillRect(0, 0, width, height);
22  
23  // Draw gaps
24  ctx.beginPath();
25  ctx.strokeStyle = 'blue';
26  ctx.lineWidth = 2;
27  
28  for (let i = 0; i < gaps.length; i++) {
29    const x = (i / gaps.length) * width;
30    const y = height - (gaps[i] / maxGap) * height;
31    
32    if (i === 0) {
33      ctx.moveTo(x, y);
34    } else {
35      ctx.lineTo(x, y);
36    }
37  }
38  
39  ctx.stroke();
40}
41

Practical Applications

Cryptography

Prime numbers are the foundation of many cryptographic algorithms, including RSA. Visualizations help cryptographers understand prime distributions and identify potential weaknesses.

Random Number Generation

Prime-based patterns can be used to generate pseudo-random sequences with specific properties.

Computational Art

Prime visualizations have inspired a genre of mathematical art that combines aesthetic appeal with number theory.

Creating Your Own Prime Visualizations

Here's a simple framework for experimenting with your own prime visualizations:

1class PrimeVisualizer {
2  constructor(canvasId, maxNum) {
3    this.canvas = document.getElementById(canvasId);
4    this.ctx = this.canvas.getContext('2d');
5    this.width = this.canvas.width;
6    this.height = this.canvas.height;
7    this.maxNum = maxNum;
8    this.primes = new Set(this.generatePrimes(maxNum));
9  }
10  
11  generatePrimes(limit) {
12    // Sieve of Eratosthenes implementation
13    // ...
14  }
15  
16  clear() {
17    this.ctx.fillStyle = 'white';
18    this.ctx.fillRect(0, 0, this.width, this.height);
19  }
20  
21  drawPoint(x, y, color = 'blue', size = 2) {
22    this.ctx.fillStyle = color;
23    this.ctx.beginPath();
24    this.ctx.arc(x, y, size, 0, 2 * Math.PI);
25    this.ctx.fill();
26  }
27  
28  mapToCanvas(x, y, xMin, xMax, yMin, yMax) {
29    const canvasX = ((x - xMin) / (xMax - xMin)) * this.width;
30    const canvasY = this.height - ((y - yMin) / (yMax - yMin)) * this.height;
31    return { x: canvasX, y: canvasY };
32  }
33  
34  visualizeUlamSpiral() {
35    // Ulam spiral implementation
36    // ...
37  }
38  
39  visualizeSacksSpiral() {
40    // Sacks spiral implementation
41    // ...
42  }
43  
44  visualizeCustom(transformFunc) {
45    this.clear();
46    
47    for (let i = 1; i <= this.maxNum; i++) {
48      const { x, y } = transformFunc(i);
49      const point = this.mapToCanvas(x, y, -10, 10, -10, 10);
50      
51      if (this.primes.has(i)) {
52        this.drawPoint(point.x, point.y, 'blue', 2);
53      }
54    }
55  }
56}
57
58// Usage
59const visualizer = new PrimeVisualizer('primeCanvas', 10000);
60
61// Custom visualization
62visualizer.visualizeCustom(n => {
63  // Try different transformations
64  const angle = n * 0.1;
65  const radius = Math.sqrt(n);
66  return {
67    x: radius * Math.cos(angle),
68    y: radius * Math.sin(angle)
69  };
70});
71

Conclusion

Visualizing prime numbers reveals that these fundamental mathematical objects, while seemingly random, exhibit fascinating patterns and structures. These visualizations not only provide aesthetic pleasure but also offer insights into the deep mathematical properties of primes.

Try creating your own prime visualizations! Experiment with different representations and transformations to discover new patterns and insights.

As computing power increases and visualization techniques advance, we can expect even more beautiful and informative representations of prime numbers in the future, potentially leading to new mathematical discoveries about these enigmatic numbers.