<title>Ulam Spirals</title>
font: 11px 'Lucida Grande', sans-serif;
background: rgba(0,0,0,0.5);
<canvas id="canvas" width=960 height=500></canvas>
<footer>Ulam Spirals, adapted from <a href="https://www.bigblueboo.com/prime/">Prime Explorer</a></footer>
<script src="dat.gui.js"></script>
window.requestAnimFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.setTimeout(callback, 1000 / 60);
window.onload = function() {
var canvas = document.getElementById("canvas");
var width = canvas.width = document.body.clientWidth,
height = canvas.height = document.body.clientHeight,
numcols = Math.floor(width / size),
numrows = Math.floor(height / size),
colors = ["#ffc344", "#000000"];
var ctx = canvas.getContext("2d");
ctx.fillStyle = colors[0];
ctx.clearRect(0,0,width,height);
ctx.translate(width/2,height/2);
if (integer > max) return false;
// our work was interrupted
if (latest > current) return false;
// transform the geometry
runLength = maxRunLength;
ctx.rotate(Math.PI * 2 / n);
// perform tests on the integer
var prime = isPrime(integer + start - 1);
// render primes differently
ctx.fillStyle = integer == 1 ? "red" : colors[0];
ctx.globalAlpha = 4 / Math.log(integer);
ctx.fillRect(-size/2, -size/2, size, size);
var ii = max; // number of integers to render at once
requestAnimFrame(nextPlot);
// https://www.javascripter.net/faq/numberisprime.htm
if (isNaN(n) || !isFinite(n)) return NaN;
if (n%1 || n*n<2) return 1;
for (var i=7;i<=m;i+=30) {
if (n%(i+4)==0) return i+4;
if (n%(i+6)==0) return i+6;
if (n%(i+10)==0) return i+10;
if (n%(i+12)==0) return i+12;
if (n%(i+16)==0) return i+16;
if (n%(i+22)==0) return i+22;
if (n%(i+24)==0) return i+24;
if (isNaN(n) || !isFinite(n) || n%1 || n<2) return false;
if (n==leastFactor(n)) return true;
/* Interactive Parameters with Dat.Gui*/
var ulam = gui.add(field, 'ulam', 3, 10).step(1);
var sizer = gui.add(field, 'size', 0.5, 6).step(0.5);
var maxer = gui.add(field, 'max', 500, 1.5e5).step(500);
var starter = gui.add(field, 'start', 1, 1e5).step(1).listen();
var speeder = gui.add(field, 'speed', 0, 10).step(1).listen();
var color1 = gui.addColor(field, 'color1');
var color2 = gui.addColor(field, 'color2');
ulam.onChange(function(v) { ulamN = v; plotUlam(ulamN); });
sizer.onChange(function(v) { size = v; plotUlam(ulamN); });
maxer.onChange(function(v) { max = v; plotUlam(ulamN); });
starter.onChange(function(v) { start = v; plotUlam(ulamN); });
speeder.onChange(function(v) { speed = v; plotUlam(ulamN); });
color1.onChange(function(v) { colors[0] = v; plotUlam(ulamN); });
color2.onChange(function(v) { colors[1] = v; document.body.style.background = colors[1]; });