js1k demo: Night SceneTags: javascript, web — 11th of September 2010

Heard about this "js1k" competition from reddit and was excited seeing all the great demos that people have submitted, so I gave it a shot myself. I wanted to make something similar to the background I created for PriceCheck and I really learned a lot doing it.

Screenshot of js1k demo, Night Scene

The artwork

Tree & Grass

The Tree & Grass are simple recursive box generator. It uses the translate/scale/rotate function in canvas to move around where the boxes should be drawn.
Got the idea for this from Context Free Art where you can create artwork using only primitive objects.
var scale = 1;
function execute(y, size, rotate) {	
	canvas_context.save();
	canvas_context.rotate(rotate);
	canvas_context.translate(0, -y);
	canvas_context.scale(size, size);
	
	scale *= size;
	tree();

	canvas_context.restore();

	scale /= size;
}

function tree() {
	// If the size of the current rendering state is too small, stop rendering
	if (scale > 0.04) {
		// Once in a while split the branches in 2
		if (rnd(1) < 0.04) {
			execute(0, 0.7, -0.15);
			execute(0, 0.7, 0.15);
		}
		else {
			// Draw our tree using a box
			canvas_context.fillRect(0, 0, 9, 9);
			
			// Keep moving the tree further infront
			execute(4, 1, (rnd(1) < 0.5) ? 0.08 : -0.08);
		}
	}
}

The moon

Its just one big radial gradient with 3 points. This adds the most to the scene.

Shooting stars

To achieve shootings stars we need 3 canvases.
  • Stars: Draw the tip of the stars here randomly
  • Stars_Tmp: Storage position to help move canvas 1 pixels diagonally and save back to canvas 1 every frame.
  • Display: Draw the "Stars" canvas every frame & applies a very transparent black fill over the whole thing, this makes the previous positions of the stars fade out and gives it a trailing effect
// Give the display a black background
display.fillStyle = "#000";
display.fillRect(0,0,screen_width, screen_height);

setInterval(function () {

	// Copy Stars to the tmp
	stars_tmp.clearRect(0,0,screen_width,screen_height);
	stars_tmp.drawImage(stars.canvas, 0, 0);

	// Copy stars_tmp back to Stars, but with a difference in position of the stars
	stars.clearRect(0,0,screen_width,screen_height);
	stars.drawImage(stars_tmp.canvas, -1, 1);

	// Draw a slight transparent black screen to fade, and then draw the stars
	display.fillStyle = "rgba(0,0,0,0.1)"
	display.fillRect(0,0,screen_width, screen_height);
	display.drawImage(stars.canvas, 0, 0);

	// Add new stars
	if (Math.random() < 0.1) {
		stars.fillStyle = "#FFF";
		stars.fillRect(Math.random()*screen_width, 0, 3, 3);
	}
}, 10);

Parallax effect

The moon, stars, trees & shooting stars are all on their own canvas objects, so every frame those canvases are just stitched together. To give the parallax effect I had to make each canvas object with a different width, this allows for moving one faster then the other using their own width as the speed property.

Tricks for minification

  • Google Closure Compiler & jscrush: Use it.
  • After using Google Closure Compiler, go through the code and minify yourself, no software is as creative as you, you can do better.
  • Property Shortening: Iterate through a objects property and give each property a new shortened name.
    for (property in cavas_context)
    {
    	canvas_context[(property[7] || property[0]) + (property[11] || property[2])] = canvas_context[property];
    }
    
    I had to go through many iterations till I found an algorithm with least amount of collisions. This code had these property names:
    • createLinearGradient == al
    • canvas == cn
    • drawImage == ga
    • clearRect == ce
    • translate == ta
    Unfortunately there is always a conflict, and fillRect had a conflict with another property, so you can't always get lucky. So for that I did canvas_context.f = canvas_context.fillRect.
  • If you don't use jscrush, doing a search and replace for "function" and ");" and replacing it with a letter helps.
  • Decimal numbers doesn't need a 0 in front of them. So 0.1 can be changed to .1
  • 'var' keyword, you can go without it. Just assign a value to a variable name before you look it up and it will be created in the global scope.
  • Really need a local variable? Use the function parameters to define it.
  • Most importantly, reuse your code, make every character count.

My favorite demo entries

I went through each and every demo, and there were some demos that I kept wanting to see over and over again so I'd like to share them.