November 04, 2010 17:44 / 7 comments / javascript starfield

The canvas element is awesome. JavaScript is fast enough that you can run some pretty computationally intensive stuff (I've seen 3D games, a NES emulator, and much more all done with JS!). This script shouldn't push your CPU to the limit, but it does show how easy it is to create cool effects with just a small amount of code.

The Code

This code bears probably only a small amount of explanation. It sets the origin in the center of the canvas then whenever a star is added it calculates the slope of the line between the star and the origin, and moves it along that line. The width the star increases as its "depth" decreases, and the speed at which it accelerates is increased by 1/10th every iteration. A simple check tells whether the stars are still visible and when they aren't a new star gets created.

function StarField(canvas_id, width, height, num_stars) {
  var width = width ? width : 600,
      height = height ? height : 600,
      origin_x = width / 2,
      origin_y = height / 2,
      stars = [],
      num_stars = num_stars ? num_stars : 50,
      canvas_id = canvas_id;

  function create_star() {
    var star = {};
    star.x = Math.random() * width - origin_x;
    star.y = Math.random() * height - origin_y;
    star.z = star.max_depth = Math.max(width, height);

    var xcoeff = star.x > 0 ? 1 : -1;
    var ycoeff = star.y > 0 ? 1 : -1;

    if (Math.abs(star.x) > Math.abs(star.y)) {
      star.dx = 1.0;
      star.dy = Math.abs(star.y / star.x);
    } else {
      star.dx = Math.abs(star.x / star.y);
      star.dy = 1.0;
    }

    star.dx *= xcoeff;
    star.dy *= ycoeff;
    star.dz = -1;

    star.ddx = .1 * star.dx;
    star.ddy = .1 * star.dy;

    star.width = 2;
    return star;
  }

  function move(star) {
    star.x += star.dx;
    star.y += star.dy;
    star.z += star.dz;

    star.dx += star.ddx;
    star.dy += star.ddy;

    star.width = 2 + ((star.max_depth - star.z) * .1);
  }

  function update_stars(ctx) {
    ctx.fillStyle = '#fff';
    for (var i = 0; i < stars.length; i++) {
      move(stars[i]);
      if (stars[i].x < -origin_x || stars[i].x > origin_x ||
          stars[i].y < -origin_y || stars[i].y > origin_y) {
        // remove
        stars[i] = create_star();
      } else {
        ctx.fillRect(
          stars[i].x + origin_x,
          stars[i].y + origin_y,
          stars[i].width,
          stars[i].width
        );
      }
    }
  }

  // drawing routine
  this.draw = function() {

    // get reference to drawing area
    var canvas = document.getElementById(canvas_id);
    if (canvas.getContext){

      // create drawing context
      var ctx = canvas.getContext('2d');

      // fill black
      ctx.fillStyle = "#000";
      ctx.fillRect(0, 0, width, height);

      update_stars(ctx);
    }

    // repeat
    t = setTimeout("draw()", 30);
  }

  for (var i=0; i < num_stars; i++) {
    stars.push(create_star());
  }

  return this;
}

Comments (7)

remcoder | nov 2010, at 09:26am

Great work! A while back I made a starfield too and before I knew it I was hooked on programming canvas stuff. The code looks very similar http://www.xs4all.nl/~mrblack/starfield2/

Charles Leifer | nov 2010, at 09:31am

Great stuff, man! Some interesting tidbits -- didn't know you do a .translate() to switch the origin, that's handy. I think the way you're handling motion blur is especially clever. Thanks for sharing!

oivoodoo | nov 2010, at 03:45am

Canvas seems very good.

Thanks for this example!!!

Buyog | nov 2010, at 07:51am

Nice tutorial for a cool effect, thanks for sharing. :)

One thing I noticed that could potentially impact your code performance is that you're calling getElementById and getContext inside the drawing loop. You'd likely save CPU cycles by just calling them each once and keeping a reference to the resulting objects at the document level.

Charles | nov 2010, at 10:11am

Buyog - you're totally correct, thanks for pointing that out!

cambol | nov 2010, at 10:14am

Groovy - oh sweet memories.

Wrote mine about 4 years ago using <div> containers only. Regarding the basic principle there seem not much evolution going on :).

cambol | nov 2010, at 10:15am

Whooops... I forgot the link: http://filenew.org/lab/stars/


Commenting has been closed, but please feel free to contact me