A JS App Under 1k: Evolving Flies
JS1k.com ran a contest involving HTML5 Canvas and 1k of JavaScript. I decided to give it a try and learned a lot about JavaScript minification as well as some sneaky techniques I hope I don't repeat ;) Below is all about the app I created as well as some minification techniques I used to shrink my JS to a kilobyte.
What does 1k of JavaScript look like?
var w=500,j,k,M=Math,a=[],F="#ff88",v=document.body.childNodes[1 ];v.width=v.height=w;c=v.getContext("2d");v.onmousedown=function (A){j=A.clientX;k=A.clientY};for(i=0;i<16;)a[i++]=new z(p(32),p( 232),p(32),2*o(),16*o()+16);setInterval(function d(){g=c.createL inearGradient(0,0,0,w);g.addColorStop(0,F+32);g.addColorStop(1,F +96);c.fillStyle=g;c.fillRect(0,0,w,w);for(i in a){u=a[i];e=u.e; y=u.y;x=u.x;if(!u.A||j>x&&j<x+e&&k>y&&k<y+e||u.a>u.q)u.A=0,u.y+= 9;else m(x,u.h,e),x=P,m(y,u.h,e),y=P;c.fillStyle="rgb("+u.r+","+ u.g+","+u.b+")";c.fillRect(x,y,e,e);if(y>w)f(),s=a[S],a[i]=new z (p(s.r),p(s.g),p(s.b),l(s.h+n()*o()),l(s.e+n()*o()));u.a++}j=k=0 },32);function z(R,G,B,H,E){Q=this;Q.r=R;Q.g=G;Q.b=B;Q.h=H;Q.e=E ;Q.a=0;Q.q=999-E*H;Q.A=1;Q.x=o()*w;Q.y=o()*w;Q.s=E*9}function f( ){L=-255;for(I in a){U=a[I],Z=U.s+U.a*U.A;if(Z>L)S=I;L=Z}}functi on p(A){A=~~l(A+n()*o()*32);return A>255?255:A}function m(A,B,C) {P=l(A+n()*o()*B);P=P>w-C?w-C:P}function o(){return M.random()}f unction n(){return o()>0.5?-1:1}function l(A){return M.abs(A)};
About the App
They live; they reproduce; they die. Sit back and watch them evolve based on the rules of their world, or take a more active role in their evolution by killing them. Kill the bigger flies, they evolve to be smaller. Kill the blue ones, they'll mutate to a different color. Try killing everything, they will tend to become harder to click (e.g. grow smaller, vibrate more, and colored like the background).
Rules of Fly Town
- Bigger flies have a better chance of reproducing.
- Older flies have a better chance of reproducing.
- Use less energy, live longer.
- Vibrating requires energy.
- Size requires energy.
Properties of a Fly
- Vibration
- Amount the fly wiggles around
- Size
- How big the fly is
- Color
- What color the fly is
- Age
- Current age in fractions of seconds
- Longevity
- Max age the fly can live
- Aliveness
- If the fly is alive or dead
- Position
- Where on the canvas the fly is
Browser Compatibility
The contest requires browser compatiblity for: Firefox (3.6.8), Safari (5.0.1), Chrome (5.0.375.99), Opera (10.60). No Internet Explorer.
Demo
The code (with comments and whitespace yay!)
Go Global: First we declare some global variables.
var w = 500, // Canvas width and height j, k, // Click x,y M = Math, // Math Object a = [], // Fly array F = "#ff88";
HTML5 Canvas: Next we find the HTML5 canvas, assign its width and height, grab its context so we can draw all over it.
// Do canvas things v = document.body.childNodes[1]; // Get canvas v.width = v.height = w; // Set canvas dimensions c = v.getContext("2d"); // Get context
User Input: If the user clicks on the canvas, save it and when the canvas gets redrawn we will do the dirty work. This will not handle page scrolling.
// Click events v.onmousedown = function(A) { j = A.clientX; // Click X Position k = A.clientY // Click Y Position }
Dawn of Time: Create the first flies, each slightly different (thats what those o
and p
functions do).
// Create the first flies for(i=0;i<16;) a[i++]=new z(p(32), p(232), p(32), 2*o(), 16*o()+16)
Framerate: This function redraws everything we see. The setInterval
calls the function at an interval of one fly year, because we will age the fly each time it is called. Each time the background and the flies are redrawn on the canvas. If a fly has been clicked it is killed.
setInterval( // Redraw function function d() { // Draw background gradient g = c.createLinearGradient(0, 0, 0, w); g.addColorStop(0, F+32); g.addColorStop(1, F+96); c.fillStyle = g; c.fillRect(0, 0, w, w) // For each fly for(i in a) { u = a[i]; e = u.e; y = u.y; x = u.x; // Check if the fly died if(!u.A || j > x && j < x + e && k > y && k < y + e || u.a > u.q) u.A=0, // Mark fly as dead u.y += 9 // Fly begins to drop else m(x,u.h,e),x=P, // Move fly x position m(y,u.h,e),y=P;// Move fly y position // Draw fly c.fillStyle = "rgb(" + u.r + "," + u.g + "," + u.b + ")"; c.fillRect(x, y, e, e) // Draw the fly // The fly has dropped entirely off the canvas if(y > w) // Delete fly f(), s = a[S], // Choose fly to reproduce a[i] = new z(p(s.r), p(s.g), p(s.b), l(s.h + n() * o()), l(s.e + n() * o())); u.a++ // Age the fly } j = k = 0 // Clear mouse click location } ,32) // Redraw frequency aka one fly year (in milliseconds)
Fly Reproduction: The z
function creates a new fly. The f
function decides which fly gets to reproduce.
// ---------------------------- Fly Reproduction // Create a new fly function z(R, G, B, H, E) { Q=this; Q.r = R; // Red Q.g = G; // Green Q.b = B; // Blue Q.h = H; // Vibration Amount Q.e = E; // Size Q.a = 0; // Age Q.q = 999 - E*H; // Maximum Age (Vibration and Size take energy which shortens lifetime) Q.A = 1; // Alive Boolean Q.x = o() * w; // X Position Q.y = o() * w; // Y Position Q.s = E*9 // Macheesmo (probability of reproducing) } // Update a global index of the fly that gets to reproduce function f() { L=-255 // Largest macheesmo for(I in a) { // For each fly U=a[I], Z=U.s + U.a*U.A // Calculate the probability of reproduction if(Z > L) // If it is bigger than all the other flies S=I; // Update the index of the lucky fly L=Z // Update a 'local' variable } }
Mutate: The p
function changes the color component by a random amount from zero to an eight. The m
function returns a random position within the range of the canvas.
// ---------------------------- Mutation // Returns a color randomly changed function p(A) { A = ~~l(A + n() * o() * 32) // Move the color component randomly up or down return A > 255 ? 255 : A // Ensure the color is with the rgb of range 0-255 } // Updates a global to new random position within the boundries of the canvas function m(A,B,C){ P = l(A + n() * o() * B); // Move position in a random amount (up to vibrate amount) P = P > w - C ? w - C : P // Ensure position is on the canvas }
Math: Create functions to save space!
// ---------------------------- Math // Return random number function o() { return M.random() // Return random decimal 0.0 to 1.0 } // Returns positive or negative function n() { return o() > .5 ? -1 : 1 // Randomly return -1 or 1 } // Return absolue value of parameter function l(A) { return M.abs(A) // Return the absolute value of parameter }
Source Code
Resources
- The demo contest that started this
- JS1k, 1k Javascript demo contest
- @kuvos who is running the contest has a great list of minification techniques
- Minification tricks
- "Cowboy" Ben Alman has some great js minification techniques on his post describing his js1k entry
- Organ1k, my JS1k contest entry
- Dreaming In JavaScript provides insight into JavaScript bitwise operators
- ~, !, +, & -
- Bitwise operators cover over at Mozilla Developer Center
- Bitwise Operators
- Javascript Minifier at Google Code
- Closure Compiler
- Yahoo!'s Minifier
- YUI compressor