An animated visualization of number theory: each integer is plotted as a point on a canvas and connected to all of its factors by arcs. Colors follow a spring rainbow; prime numbers are drawn in white because they have no factors.
| Author | Pablo Caro Martín |
|---|---|
| Website | pcaro.es |
| Competition | classic |
| Year | 2013 (Spring theme) |
| Bytes | 1019 / 1024 |
| Compression | Custom Python JSCompressor (based on JSCrush by Aivo Paas) |
Click anywhere on the canvas to activate TURBO MODE, which speeds up the animation dramatically.
This entry is notable for including the full, heavily-commented original source on its js1k details page — a rarity in the competition. The original source even includes a variable name legend and three "cool facts" about the code.
S(), each placed at an equal horizontal spacing that shrinks as more numbers appearn+2, the code checks every smaller number to find factors; matching indices are stored in I[n]I[n] is empty the number is prime — a count p and last-prime tracker l are updatedsetTransform to stretch a unit circle into an ellipse connecting each number to its factorsC(x) function maps an index 0–95 to an RGB rainbow, cycling through red → green → blue → redcreateLinearGradient between the colors of the two connected pointsr runs 0→v each step; setTimeout drives the smooth entry of each new arc#1 — Without leading spaces, every line in the description paragraph is exactly 70 characters long. 70's factors are 35, 14, 10, 7, 5, and 2.
#2 — Primes only connect to points on their right (their multiples), never to the left, because they have no factors besides 1 and themselves.
#3 — The most-used character in the uncompressed source is i with 74 appearances, followed by ( and ) tied at 73 each.
// Control characters (compression tokens) shown as \xNN _='\x1cm\x1dginN\x1coverflow="hidden";w=c.width\x16Width;h=c.h\x1e\x16H\x1e;Afont="7px \x1dial";I=[];e=w\x1f;n=-1;p=r=f=d=i=kNv=17;C\x18x%=96;return "rgb("+(\x19O||\x1a\x1b64\x17-64+V\x197\x177EO\x04\x1948B<O\x17V\x1964\x1764E48\x04\x19\x1b80\x1796-V\x1a7\x17-7+\x05)"};S\x18d=e-w/(\x10n+2_I\x06([]_if\x07>0) `\x07+\x11\x1f;i\x10)if(\x07J%MJ\x010) \x02n]\x06M_rN0\x01\x02nX\x04(p\x10,l=n+2_u\x03;u\x18e-=d/v;\x10r<v?\x0eTimeout(u,\x11:S\x03;\x0eInterval(~jSD="#000";jRect(0Fw,h_Y$(kNk<\x02iX;k\x10){AmoveTo(ZK_Asave(_A\x0eTrans$m(M-^)\x1fF0,(^J\x1f,e*M+^J\x1f,KUA\x1dc(0FeFM%2?1:-\x11*3.14*M\x01n?r/v:\x11,i%2\x01\x12Arestore(_Q*(^+\x11FZ\x12z0,C(^\x13M)_\x14SD=g;\x14\x03QFe*n,\x12`n;i+=O)zi/n,CM\x13\x07)UY0<qg;j(UY0\x01q"#fff";j(_@n\x15\x07J\x0f4\x12@Primes\x15p+" Last\x15l\x0f8\x123<v\x04@Click $ turbo!"\x0fh-O)},4\x12c.onclick\x18v=v>3?3:17};S()q\x02iX\x04A\x1dc(ZK,4F8_jSD\x18function(x){zg.addColorStop(`$MNi<jAfillU_AbeginPath(_Vx)*O:Qg=AcreateLine\x1dGradient(e^\x02i][k]_);X].lengthY`=n;i\x10)Ze*M+\x11,DtyleE-\x05,"+(\x1aF,0,@jText("Aa.B?255:xM(iN=0;O16J+2)K3*h/5732$for\x14Astroke\x15: "+\x16=inner\x17?(\x10++\x111)\x120_\x13)_z1,C\x1cb.sD.\x1dar\x1eeight\x1f/2\x18=~\x19x<\x1ax>=\x1b80B>=\x04&&\x05V0)+"\x06.push\x07(n\x01==\x02I[\x03()}\x0eset\x0f,O,';for(Y=0;$="\x0f\x0e\x03\x02\x01\x07\x06\x05\x04\x1b\x1a\x19\x18\x1f\x1e\x1d\x1c\x13\x12\x11\x10\x17\x16\x15\x14$7KJONMBA@FEDZYX_^QVUj`z~q"[Y++];)with(_.split($))_=join(pop());eval(_)
/***************************************************************************
Color Factors - js1k 2013-Spring
Pablo Caro Martín - pcaro.es
Welcome to Color Factors!
In this world, each point represents a number, starting from 2, to the
infinite. Each one of these number is connected with its factors by an
arc. The colors of the points come from a spring-like rainbow, and the
arcs have a gradient that comes from the colors of the points they are
connecting. However, all primes are white, just because they are cool.
The "n" in the top-left corner indicates the last number, "primes" the
number of prime numbers found, and "last" the last prime number found.
If you click anywhere in the screen, the TURBO MODE will be activated!
Cool fact #1: Without the leading spaces, all the lines in the above
paragraph are 70 chars long. 70's factors are 35, 14, 10, 7, 5 and 2.
Obvious examples:
- 10 is connected with 5, 2, and with its multiples (20, 30, 40...)
- 20 is connected to 10, 5 and 2 (and its multiples)
- 30 is connected to 10, 6, 5, 3 and 2 (and its multiples)
- 31 is only connected with it multiples, because it's prime.
The demo has been tested in OS X, in the following browsers:
- Chrome: Works nicely.
- Safari: Works nicely.
- Firefox: Works, but really slow after a few numbers are added.
- Opera: Works well, after applying a workaround to avoid a fix
regarding the transformation of arcs.
In order to minify the code, I wrote my own "JSCompressor" in Python,
based on the original JSCrush by Aivo Paas (www.iteral.com/jscrush), but
with some functionalities that help when trying to find an optimal
minimizable code, obtaining an even more reduced output. Everything
else has been done manually, with some help from my JSCompressor script.
After the compression, the code length is 1019 characters, which is a
prime number. Nice.
Cool fact #2: The obvious property of prime numbers (they don't have
factors besides of 1 and themselves) leads to the fact that they are
only connected with points in their right side, never in their left
side.
Names used (and where can that character be found):
a: 2d context
b: document body
c: canvas element
C: color function (addColorStop)
d: dinc (innerWidth)
e: inc (innerHeight)
f: ninc (function)
g: gradient (innerHeight)
h: height (innerWidth)
i: loop index (innerWidth)
I: list of factors (setInterval)
k: loop index (strokeStyle)
l: last prime found (fillStyle)
n: numbers (innerWidth)
p: primes found (addColorStop)
r: rinc (innerWidth)
S: add_point function (addColorStop)
u: movement function (function)
v: movement increment
w: width (innerWidth)
x: aux (fillText)
Cool fact #3: The most used character in this script before compression is
"i", with 74 appearances; followed by "(" and ")", with 73 appearances
each; and by "=" and ";", tied with 59 appearances each (without
counting the comments!).
***************************************************************************/
// No margins. No scroll. Stay cool.
b.style.margin=0;
b.style.overflow="hidden";
w=c.width=innerWidth;
h=c.height=innerHeight;
a.font="32px arial";
// Variable inicialization
I=[];
e=w/2;
n=-1;
p=r=f=d=i=k=0;
v=132;
// Rainbow color!
C=function(x){
x%=96;
return "rgb("
+(x<16||x>=80?255:x>=64?(-64+x)*16:x<32?(32-x)*16:0)+","
+(x>=16&&x<48?255:x<16?(x)*16:x<64?(64-x)*16:0)+","
+(x>=48&&x<80?255:x>=80?(96-x)*16:x>=32?(-32+x)*16:0)+")"
};
// Add a new value
S=function(x){
d=e-w/(++n+2);
I.push([]);
if(n>0) // Everyone loves 2
for(i=0;i<(n+1)/2;i++)
if((n+2)%(i+2)==0) // Join with every factor
// if((n+2)%(i+2)==0&&I[i].length==0) // Join with primes only
I[n].push(i);
r=0;
0==I[n].length&&(p++,l=n+2);
u()
};
// Movement
u=function(x){
e-=d/v;
++r<v?setTimeout(u,1):S()
};
// Drawing function
setInterval(function(x){
a.fillStyle="#000";
a.fillRect(0,0,w,h);
// Arcs
for(i=0;i<=n;i++)
for(k=0;k<I[i].length;k++){
a.moveTo(e*(i+1),3*h/5);
a.save();
a.setTransform(
(i-I[i][k])/2,0,0,
(I[i][k]+2)/2,e*(i+I[i][k]+2)/2,3*h/5);
a.beginPath();
a.arc(0,0,e,0,(i%2?1:-1)*3.14*(i==n?r/v:1),i%2==0);
a.restore();
g=a.createLinearGradient(e*(I[i][k]+1),0,e*(i+1),0);
g.addColorStop(0,C(I[i][k]));
g.addColorStop(1,C(i));
a.strokeStyle=g;
a.stroke()
}
g=a.createLinearGradient(e,0,e*n,0);
for(i=0;i<n;i+=16)
g.addColorStop(i/n,C(i));
g.addColorStop(1,C(n));
// Color points (non primes)
a.beginPath();
for(i=0;i<=n;i++)
0<I[i].length&&a.arc(e*(i+1),3*h/5,4,0,8);
a.fillStyle=g;
a.fill();
// White points (primes)
a.beginPath();
for(i=0;i<=n;i++)
0==I[i].length&&a.arc(e*(i+1),3*h/5,4,0,8);
a.fillStyle="#fff";
a.fill();
a.fillText("n: "+(n+2),16,40);
a.fillText("Primes: "+p+" Last: "+l,16,80);
3<v&&a.fillText("Click for turbo!",16,h-16)
},40);
c.onclick=function(x){v=v>3?3:132};
S()
</script>.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
* { margin: 0; padding: 0; box-sizing: border-box }
body { background: #000 }
canvas { display: block; position: absolute; top: 0; left: 0 }
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
var c = document.getElementById('c')
var b = document.body
var a = c.getContext('2d')
</script>
<!-- tab source injected here as a <script> tag after 3 seconds -->
</body>
</html>