Animated star field on a Javascript canvas

I was watching the film Pixels the other night.  Not the best film ever (by a long way), but it did get me reminiscing about all the cool stuff I programmed when I was a lad.  Back when I made text adventure games and simple animations in QBASIC.  Back when I programmed games on my TI-83 graphical calculator during History lessons.  Ah, those were the days.

So I wondered – how easy would it be to remake some of those programs in the technology I use today?  Specifically, can I recreate my horizontally animated star field that I made in QBASIC, only this time using Javascript?

As it turns out, yes I can.

First of all, we set up a basic HTML5 page, with just a canvas  element on it.  There’s also some CSS to make sure it will work in full-screen and has a black background.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Stars horizontal</title>
    <style>
        html, body {
            position: relative;
            margin: 0;
            padding: 0;
            height: 100%;
        }
        canvas {
            position: absolute;
            top: 0;
            left: 0;
            background: black;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
</body>
</html>

The Javascript is where it gets fun.  And I’m not using jQuery or any other helper libraries.

We set up the canvas width and height here rather than in the CSS, because otherwise it will scale rather than actually get bigger.

// Set up canvas with full screen width and height
var canvas = document.getElementById('canvas');
canvas.width=document.body.clientWidth;
canvas.height=document.body.clientHeight;
var ctx = canvas.getContext('2d');

Next we create a few variables to hold things like our stars and settings.  You can probably guess what they’ll be used for.

// Set the number of stars to draw
var stars=[];
var numStars=100;
var speed=10;

Here’s the function that creates a star.

// Reset a star
function makeStar() {
	return {
		x: Math.random(),
		y: Math.random(),
		distance: Math.sqrt(Math.random()),
		color: 'hsl('+Math.random()*40+',100%,'+(70+Math.random()*30)+'%)'
	};
}

We’re setting everything to be pretty random here.  Note that Math.random()  creates a number between 0 and 1, so we’ll need to put that into context later.  We Math.sqrt()  the random number for the distance, so that it’s weighted more towards closer stars; these will move faster, so we need more of them there.  The logic in the colour is simply giving us a star that is mostly white but with a slight tint between red and yellow.  It’s worth checking out hsl() and hsla() if you haven’t already, it’s pretty useful!

Now we can initialise our stars array.

// Initialise stars
for (i=0;i<numStars;i++) {
	stars[i]=makeStar();
}

Next we’ve got a function to draw the stars.  I’ve put it in a function because we’ll be calling it multiple times shortly.

// Draw stars
function updateStars() {
	// Clear canvas
	ctx.clearRect(0,0,canvas.width,canvas.height);
	// Draw each star
	for (i=0;i<numStars;i++) {
		// Move the star first
		stars[i].x-=Math.pow(stars[i].distance,2)/canvas.width*speed;
		// If it's off-screen, reset it
		if (stars[i].x<=0) {
			stars[i]=makeStar();
			stars[i].x=1;
		}
		// Draw the star
		ctx.beginPath();
		ctx.arc(stars[i].x*canvas.width,stars[i].y*canvas.height,stars[i].distance*2,0,2*Math.PI,false);
		ctx.lineWidth=stars[i].distance*4;
		ctx.strokeStyle='rgba(255,255,255,0.2)';
		ctx.stroke();
		ctx.fillStyle=stars[i].color;
		ctx.fill();
	}
}

First off we clear whatever might be on the canvas already.  Modern browsers handle output buffering quite well, so we don’t need to bother about manually double-buffering to avoid the flickering.

Before we draw each star, we move it.  How far it moves depends how close it is to us, and we’re using Math.pow()  to weight it correctly.  This is the inverse of the Math.sqrt()  we used when setting the distance of the star.  We’re also transforming that 0-1 value into something relative to the canvas width.  We also check whether the star has moved off-screen, and reset it if we need to.

And then we actually draw the star on the canvas.  I’ve given it a semi-transparent stroke, to simulate a blur effect.

Finally, we call the updateStars()  function every 30 milliseconds so we get a nice smooth animation.

// Redraw the stars every 30 milliseconds
setInterval(function(){
	updateStars();
},30);

Here’s the final result.  You can easily change the number of stars and the speed using the variables at the top.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.