The first 4D demo on JS1k! Move your mouse to start the demo and move the camera. Use the buttons to customize and animate.
for(_='on~e=R-1.5)QQ*(PPo-O30N0N000M0+60*L0)/20KinputIroundHbutt~G11F=(E0>o?DD1.5BB+(A212@=t),idthtylePTPk+9)+)*p/J?1:2,=+f[i;R1>;c.stroke-2*r.page ~I=<G ~click=p===d&&(;R3>-33[0,1,1,0][t]F2valuea.w><I typR =2>c.lineb.s.backgH=)*(o-15KK:15D color =#?:3-,==d?J-p:J;h=1;d=g||(d=Rh=k=o=n=p=l=m=0,"#2",g=f="MN002@FN0N3@22@@F12F100000312200N0N0N03222221M".split(""),document.write("<center range s=w:6emk=/5-92ffffff id=C><p>11X11X22Y22Y33Z33Z</G><p>h=1-h>pauseif(J<2048)p*=2,J*slowif(4<J)p/=2,J/fast"),J=256,~mousemovRfuncti~(r){lEX)/50;mE350Y)/N;n=1});n&&o++;h&&(pEp+J-1)%J);+=0;for(i=f.length;i-=3;)S],y+1],T+2],uSvywT17D(S=3AN>i?.01:0):5AuO3u:u+(S-u,y=7B:9AvO7v:v+(y-v,T=FB:13AwOFw:w+(T-w):(tE[S,S,y,T][d]+e)%4,uSvywT1u2v3wS+Eu-S,y+Ev-y,T+Ew-T),To(/2-10LS-(Sl*(TQ,8Ly-(ym*(TQ);W=99>o?35-o/3:2;Cap=Join="H"S=C.();setTimeout(_,N)';g=/[^ -?CJS-}]/.exec(_);)with(_.split(g))_=join(shift());eval(_) <3
Zm9yKF89J29ufmU9Ui0xLjUpUVEqKFBQby1PMzBOME4wMDBNMCs2MCpMMCkvMjBLaW5wdXRJcm91bmRIYnV0dH5HMTFGPShFMD5vP0REMS41QkIrKEEyMTJAPXQpLB9pZHRoHnR5bGUdUFRQays5KSscKSpwL0obPzE6MiwaPStmW2kZO1IxPhg7Yy5zdHJva2UXLTIqci5wYWdlFiB+ST0VPEcgfmNsaWNrPRQUcD0TPT1kJiYoEjtSMz4tETMzEFswLDEsMSwwXVt0XQ9GMg52YWx1ZQxhLnceCz48SSB0eXBSCT0yPghjLmxpbmUHYi5zHS5iYWNrZ0g9BikqKG8tMTVLBUs6MTVEBAljb2xvciAMPSMDPw86My0PLAI9PWQ/Si1wOko7aD0xO2Q9AWd8fChkPVJoPWs9bz1uPXA9bD1tPTAsBiIjDjIQIixnPWY9Ik1OMBAwMkBGTjBOM0AyMkBARg4OMTIORjEwMDAwMDMOMTIyMBAQEDBOME4QME4QMBAzMjIyMjIOMU0iLnNwbGl0KCIiKSxkb2N1bWVudC53cml0ZSgiPGNlbnRlcglyYW5nZSBzHT13Hjo2ZW0Vaz0MLzUtOQMOMhAVBgwDZmZmZmZmIGlkPUM+PHA+EzEBMRhYEzEBMRFYEzIBMhhZEzIBMhFZEzMBMxhaEzMBMxFaPC9HPjxwPhRoPTEtaD5wYXVzZRRpZihKPDIwNDgpcCo9MixKKghzbG93FGlmKDQ8SilwLz0yLEovCGZhc3QiKSxKPTI1Nix+bW91c2Vtb3ZSZnVuY3RpfihyKXtsRQsWWCkvNTA7bUUzNTAWWSkvTjtuPTF9KTtuJiZvKys7aCYmKHBFcCtKLTEpJUopOwsrPTA7Zm9yKGk9Zi5sZW5ndGg7aS09MzspUxldLHkZKzFdLFQZKzJdLHUIUxp2CHkadwhUGjE3RChTPTNBTj5pPy4wMTowKTo1QXVPMwR1OnUrKFMtdQUseT03Qjo5QXZPNwR2OnYrKHktdgUsVD1GQjoxM0F3T0YEdzp3KyhULXcFKToodEVbUyxTLHksVF1bZF0rZSklNCx1CFMCdgh5AncIVAIxEnUfMhJ2HzMSdx9TK0V1LVMbLHkrRXYteRssVCtFdy1UGyksB1RvKAsvMi0xMExTLShTHGwqKFRRLDhMeS0oeRxtKihUUSk7B1cePTk5Pm8/MzUtby8zOjI7B0NhcD0HSm9pbj0iSCIXUx09Qy4MFygpO3NldFRpbWVvdXQoXyxOKSc7Zz0vW14gLT9DSlMtfV0vLmV4ZWMoXyk7KXdpdGgoXy5zcGxpdChnKSlfPWpvaW4oc2hpZnQoKSk7ZXZhbChfKSA8Mw==
// Golf & build process
// ====================
// The content of the following loop function above is:
// * golfed
// * minified with Closure Compiler
// * cleaned (bacause CC leaves unnecessary spaces)
// * RegPacked (NB: in RegPack settings, add "a b c _ X Y Z" to the list of unrenamable vars)
// and the result is submitted to js1k
// What happens in this demo
// =========================
// - When the demo loads, Regpack generates and evals a string: _.
// - It also leaves the variable g to null
// - When _ is eval'ed, it draws the current frame and sets a timeout to re-eval itself, and so on
// Variables
// =========
// a: canvas
// b: body
// c: canvas context2d
// d: which animation (0: none, 1: X, 2: Y, 3: Z)
// e: animation direction (1 = forward, 3 = backwards)
// f: array containing the coordinates of all the points of the tesseract (to read 3 by 3). They are arranged to form a hamiltonian path where all edges are used only once (see http://img.ctrlv.in/img/15/02/14/54dfca5f4c396.png)
// g: null at the beginning, then truthy while the demo plays
// h: rotation state (0: pause, 1: play)
// i: iterator
// j: lines color's input
// k: perspective (-10 to 10: 2.5d do exaggerated 3D)
// l: mouse X
// m: mouse Y
// n: demo state (0: pause, 1: play)
// o: demo frame counter
// p: rotation frame counter
// q: number of frames in an animation loop
// r: unused
// s: unused
// t: temp var
// u: X coordinate of each point projected on a cube (0 => 1, 1 => 1, 2 => 2, 3 => 2)
// v: Y coordinate of each point projected on a cube
// w: Z coordinate of each point projected on a cube
// x: X coordinate of each point in the tesseract
// y: Y coordinate of each point in the tesseract
// z: Z coordinate of each point in the tesseract
// This simulates the state of _ and g after unpacking.
_ = "";
g = null;
loop = function(){
// ==============
// RegPacked code
// ==============
// Initializations (only at the first frame, when g is null)
// ---------------
if(!g){
// Variables
d =
e =
h =
k =
o =
n =
p =
l =
m = 0;
// Body background
b.style.background = "#112233";
// Points coordinates (full version: [[0,0,0],[3,0,0],[3,3,0],[2,2,1],[2,1,1],[3,0,0],[3,0,3],[2,1,2],[2,2,2],[1,2,2],[1,2,1],[1,1,1],[2,1,1],[2,1,2],[1,1,2],[1,1,1],[0,0,0],[0,0,3],[1,1,2],[1,2,2],[0,3,3],[3,3,3],[3,0,3],[0,0,3],[0,3,3],[0,3,0],[3,3,0],[3,3,3],[2,2,2],[2,2,1],[1,2,1],[0,3,0],[0,0,0]] )
g = f = "030000300330221211300303212222122121111211212112111000003112122033333303003033030330333222221121030000".split("");
// Inputs & buttons
document.write("<center><input type=range style=width:6em oninput=k=value/5-9><input type=color value=#112233 oninput=b.style.background=value><input type=color value=#ffffff id=j><p><button onclick=p=1==d?q-p:q;h=1;d=1;e=1>X<button onclick=p=1==d?q-p:q;h=1;d=1;e=3>-X<button onclick=p=2==d?q-p:q;h=1;d=2;e=1>Y<button onclick=p=2==d?q-p:q;h=1;d=2;e=3>-Y<button onclick=p=3==d?q-p:q;h=1;d=3;e=1>Z<button onclick=p=3==d?q-p:q;h=1;d=3;e=3>-Z</button><p><button onclick=h=1-h>pause<button onclick=if(q<2048)p*=2,q*=2>slow<button onclick=if(4<q)p/=2,q/=2>fast");
q = 256;
// When mouse moves
onmousemove = function(e){
// Get X
l = (a.width - e.pageX * 2) / 50;
// Get Y
m = (350 - e.pageY * 2) / 30;
// Start animation
n = 1;
};
}
// Main loop
// =========
// If demo has started, increment frame counter
if(n){
o++;
}
// If current animation is not in pause, play it. Reset it when it's finished (to loop)
if(h){
p = (p + q - 1) % q;
}
// Flush canvas (doesn't work on IE T_T )
a.width += 0;
// Loop on all the points
for(i = f.length; i-=3;){
// Get its "real" position (in the tesseract)
x = +f[i];
y = +f[i+1];
z = +f[i+2];
// Get its projected position on a cube
u = x < 2 ? 1 : 2;
v = y < 2 ? 1 : 2;
w = z < 2 ? 1 : 2;
// During the first 170 frames, animate from 0d (point) to 1d (line) to 2D (square) to 3D (cube) to 4d (tesseract)
if(o < 170){
x = (
o < 30 // if o < 30
? 1.5 + (i < 30 ? .01 : 0) // x = 1.5 + a little X delta to show the initial dot on non-Firefox browsers (T_T)
: o < 50 // else if o < 50
? 1.5 + (u - 1.5) * (o - 30) / 20 // x = a value between 1.5 and the closest position from the center
: o < 150 // else if o < 170
? u // x = the closest position
: u + (x - u) * (o - 150) / 20 // x = a value between the closest position and the final position
);
y = (
o < 70 // if o < 70
? 1.5 // y = 1.5
: o < 90 // else if o < 90
? 1.5 + (v - 1.5) * (o - 70) / 20 // y = a value between 1.5 and the closest position from the center
: o < 150 // else if o < 170
? v // y = the closest position
: v + (y - v) * (o - 150) / 20 // y = a value between the closest position and the final position
);
z = (
o < 110 // if o < 110
? 1.5 // z = 1.5
: o < 130 // else if o < 130
? 1.5 + (w - 1.5) * (o - 110) / 20 // z = a value between 1.5 and the closest position from the center
: o < 150 // else if o < 170
? w // z = the closest position
: w + (z - w) * (o - 150) / 20 // z = a value between the closest position and the final position
);
}
// After the introduction, when the buttons are clicked, 4D animations are performed
else {
// X / Y / Z animations
t = ([x,x,y,z][d] + e) % 4;
u = x < 2 ? [0,1,1,0][t] : 3 - [0,1,1,0][t];
v = y < 2 ? [0,1,1,0][t] : 3 - [0,1,1,0][t];
w = z < 2 ? [0,1,1,0][t] : 3 - [0,1,1,0][t];
if(d == 1){
u = t;
}
if(d == 2){
v = t;
}
if(d == 3){
w = t;
}
// x,y,z in-between
x = x + (u - x) * p / q;
y = y + (v - y) * p / q;
z = z + (w - z) * p / q;
}
c.lineTo(
a.width / 2 - 100 + x * 60 - (x - 1.5) * (z - 1.5) * (k + 9) + l * (z - 1.5),
80 + y * 60 - (y - 1.5) * (z - 1.5) * (k + 9) + m * (z - 1.5)
);
}
// Decrease line width progressively from 35 to 2.
c.lineWidth = o < 99 ? 35 - o / 3 : 2;
// Set lines style
c.lineCap = c.lineJoin = "round";
c.strokeStyle = j.value;
// Draw the lines
c.stroke();
// Draw the next frame in 30ms.
setTimeout(_,30);
// =====================
// End of RegPacked code
// =====================
};
// This simulates the timeout inside the packed code
loop();
setInterval(loop, 30);