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 | November 2010, at 09:26

    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 | November 2010, at 09:31

    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 | November 2010, at 03:45

    Canvas seems very good.

    Thanks for this example!!!


  • Buyog | November 2010, at 07:51

    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 | November 2010, at 10:11

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


  • cambol | November 2010, at 10:14

    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 | November 2010, at 10:15

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


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