MAKE THE MATHEMATICS YOURSELF
Ulam spiral in C
A complete program. Standard C, a compiler, and the rule behind the shape.

FROM SOURCE TO SHAPE
Run it in three steps.
- Save the source.
Download ulam.c into a folder on your computer.
- Compile it.
In that folder, run this with GCC or Clang:
cc -std=c11 -O2 ulam.c -lm -o ulam - Make the image.
./ulamOpen
ulam.svgin a browser to see the result.
On Windows with GCC, name the executable ulam.exe and run it from the same folder.
THE RULE IN THE PROGRAM
How the picture is built
Prime integers on the anticlockwise square spiral, starting 1 at the origin and 2 to the right.
Make it your own
terms=22801
Diagonal patterns describe a finite prime sample; a visible line is not a proof of infinitely many primes. Each run produces one image; use Graphic mode for the interactive animation.
/* Arithmos: ulam
* Compile: cc -std=c11 -O2 ulam.c -lm -o ulam
* Run: ./ulam
* Output: ulam.svg (open this file in a browser)
* Optional output path: ./ulam my-image.svg
* Edit the constants in draw() to explore another case.
*/
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static FILE *nt_out;
#define NT_PI 3.14159265358979323846
/* The small SVG writer keeps this program free of graphics dependencies.
* Coordinates are pixels on a 1000 x 700 drawing surface.
* t runs from 0 to 1 through mint, blue, rose, and gold.
*/
static inline void nt_color(double t, char hex[8]) {
const double stops[4][3] = {
{91,227,201}, {128,146,240}, {218,138,220}, {244,200,127}
};
t = fmax(0.0, fmin(1.0,t)) * 3.0;
int band = (int)fmin(2.0,floor(t));
double blend = t - band;
int r[3];
for (int k=0;k<3;k++) r[k]=(int)lround(stops[band][k]*(1.0-blend)+stops[band+1][k]*blend);
snprintf(hex,8,"#%02x%02x%02x",r[0],r[1],r[2]);
}
static inline void nt_line(double x,double y,double X,double Y,double t,double alpha,double width) {
char color[8];nt_color(t,color);
fprintf(nt_out,"<path d=\"M%.3f %.3f L%.3f %.3f\" fill=\"none\" stroke=\"%s\" stroke-opacity=\"%.3f\" stroke-width=\"%.3f\"/>\n",x,y,X,Y,color,alpha,width);
}
static inline void nt_dot(double x,double y,double r,double t,double alpha) {
char color[8];nt_color(t,color);
fprintf(nt_out,"<circle cx=\"%.3f\" cy=\"%.3f\" r=\"%.3f\" fill=\"%s\" fill-opacity=\"%.3f\"/>\n",x,y,r,color,alpha);
}
static inline void nt_circle(double x,double y,double r,double t,double alpha,double width) {
char color[8];nt_color(t,color);
fprintf(nt_out,"<circle cx=\"%.3f\" cy=\"%.3f\" r=\"%.3f\" fill=\"none\" stroke=\"%s\" stroke-opacity=\"%.3f\" stroke-width=\"%.3f\"/>\n",x,y,r,color,alpha,width);
}
static inline void nt_rect(double x,double y,double w,double h,double t,double alpha) {
char color[8];nt_color(t,color);
fprintf(nt_out,"<rect x=\"%.3f\" y=\"%.3f\" width=\"%.3f\" height=\"%.3f\" fill=\"%s\" fill-opacity=\"%.3f\"/>\n",x,y,w,h,color,alpha);
}
static inline void nt_text(double x,double y,const char *text) {
fprintf(nt_out,"<text x=\"%.3f\" y=\"%.3f\" fill=\"#ededf3\" font-family=\"monospace\" font-size=\"16\">",x,y);
for (;*text;text++) {
if (*text=='&') fputs("&",nt_out);
else if (*text=='<') fputs("<",nt_out);
else if (*text=='>') fputs(">",nt_out);
else fputc(*text,nt_out);
}
fputs("</text>\n",nt_out);
}
static inline int nt_gcd(int a,int b) {a=abs(a);b=abs(b);while(b){int r=a%b;a=b;b=r;}return a;}
static inline int nt_prime(int n) {if(n<2)return 0;for(int d=2;d<=n/d;d++)if(n%d==0)return 0;return 1;}
/* Consecutive integers on the square spiral; primes glow. */
static void draw(void) {
const int terms = 22801; /* Editable: 151 squared fills a square. */
const double scale = 580.0 / (ceil(sqrt((double)terms)) + 2.0);
int x=0, y=0, dx=1, dy=0, run=1, steps=0, turns=0;
for (int n=1; n<=terms; ++n) {
double px=500+x*scale, py=345-y*scale;
if (nt_prime(n)) nt_dot(px,py,fmax(.8,scale*.22),(double)n/terms,1);
else nt_dot(px,py,.45,.3,.12);
x+=dx; y+=dy;
if (++steps==run) {
int old_dx=dx; dx=-dy; dy=old_dx; steps=0;
if (++turns%2==0) ++run;
}
}
nt_text(45,675,"Square spiral: prime positions glow; color follows integer size.");
}
int main(int argc, char **argv) {
if (argc > 2) {
fprintf(stderr, "Usage: %s [OUTPUT.svg]\n", argv[0]);
return EXIT_FAILURE;
}
const char *filename = argc == 2 ? argv[1] : "ulam.svg";
nt_out = fopen(filename, "wb");
if (!nt_out) { perror(filename); return EXIT_FAILURE; }
fputs("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1000\" height=\"700\" viewBox=\"0 0 1000 700\">\n"
"<rect width=\"1000\" height=\"700\" fill=\"#171721\"/>\n", nt_out);
draw();
fputs("</svg>\n", nt_out);
int failed = ferror(nt_out);
if (fclose(nt_out) != 0) failed = 1;
if (failed) { fputs("Could not finish writing the image.\n", stderr); return EXIT_FAILURE; }
printf("Wrote %s\n", filename);
return EXIT_SUCCESS;
}
All 40 explorations, ready to compile.Download all C examples ↓