ET - Elastic Trail of Stars that Follow Cursor

The JavaScript on this page causes a trail of stars to follow the cursor around.  The stars are connected by an invisible elastic band, and are controlled by gravity and friction.

Requirements

Requires CC Common Code and 

The following HTML creates the objects that will be floated around the screen:
<body leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
<div id=
"ETdot0" style="position: absolute; visibility: hidden; height: 15; width: 13;"><img src="star.gif" height=13 width=15></div>
<div id=
"ETdot1" style="position: absolute; height: 15; width: 13;"><img src="star.gif" height=13 width=15></div>
<div id=
"ETdot2" style="position: absolute; height: 15; width: 13;"><img src="star.gif" height=13 width=15></div>
<div id=
"ETdot3" style="position: absolute; height: 15; width: 13;"><img src="star.gif" height=13 width=15></div>
<div id=
"ETdot4" style="position: absolute; height: 15; width: 13;"><img src="star.gif" height=13 width=15></div>
<div id=
"ETdot5" style="position: absolute; height: 15; width: 13;"><img src="star.gif" height=13 width=15></div>
<div id=
"ETdot6" style="position: absolute; height: 15; width: 13;"><img src="star.gif" height=13 width=15></div>
/*
Elastic Trail script (By Philip Winston @ pwinston@yahoo.com, URL:http://members.xoom.com/ebullets)
Script featured on Dynamicdrive.com
For this and 100's more DHTML scripts, visit http://dynamicdrive.com
*/

// Thanks to Troels Jakobsen <tjak@get2net.dk>
// for fix which makes it work when the page is scrolled

// Style settings specific to Elastic Trail.  (Also include common style settings)
// (none)
// Variables specific to Elastic Trail.  (Also include common variables)
var ETnDots = 7;
var ETdeltaT = .01; // fixed time step, no relation to real time
var ETsegLen = 10; // size of one spring in pixels
var ETspringK = 10; // spring constant, stiffness of springs
var ETmass = 1;
var ETgravity = 50;
var ETresistance = 10;
// stopping criteria to prevent endless jittering doesn't work when sitting on bottom since floor
// doesn't push back so acceleration always as big as gravity
var ETstopVel = 0.1;
var ETstopAcc = 0.1;
var ETdotSize = 11;
var ETbounce = 0.75; // percent of velocity retained when bouncing off a wall
var ETfollowMouse = true; // always on for now, could be played with to let dots fall to bottom, get thrown, etc.
var ETdots = new Array();
// Initialization.  The ETinit() function is called here, and follows immediately.
ETinit();
function ETinit(){
   var i = 0;
   for (i = 0; i < ETnDots; i++) {
      ETdots[i] = new ETdot(i);
      }

   if (!isNetscape) {
      // I only know how to read the locations of the
      // <LI> items in IE
      //skip this for now
      // ETsetInitPositions(ETdots)
      }

   // set their positions
   for (i = 0; i < ETnDots; i++) {
      ETdots[i].obj.left = ETdots[i].X;
      ETdots[i].obj.top = ETdots[i].Y;
      }

   if (isNetscape) {
      // start right away since they are positioned
      // at 0, 0
      ETstartanimate();}
   else {
      // let dots sit there for a few seconds
      // since they're hiding on the real bullets
      setTimeout("ETstartanimate()", 2000);}
   }

// Constructor functions
// a "ETdot" obj is the "div" object created above, and has additional
// properties of location and velocity: X, Y, dx, and dy.
function ETdot(i) {
   this.X = Xmouse;
   this.Y = Ymouse;
   this.dx = 0;
   this.dy = 0;
   if (isNetscape) {
      this.obj = eval("document.ETdot" + i);}
   else {
      this.obj = eval("ETdot" + i + ".style");}
   }
// ETstartanimate() calls ETanimate() every 20/100 sec.
function ETstartanimate() {
   setInterval("ETanimate()", 20);}
// ETsetInitPositions(dots) sets the initial positions of the dot object in the array called dots[]
// This is to line up the bullets with actual LI tags on the page
// Had to add -ETdotSize to X and 2*ETdotSize to Y for IE 5, not sure why
// Still doesn't work great
function ETsetInitPositions(dots) {
   // initialize dot positions to be on top
   // of the bullets in the <ul>
   var startloc = document.all.tags("LI");
   var i = 0;
   for (i = 0; i < startloc.length && i < (dots.length - 1); i++) {
      dots[i+1].X = startloc[i].offsetLeft
      startloc[i].offsetParent.offsetLeft - ETdotSize;
      dots[i+1].Y = startloc[i].offsetTop +
      startloc[i].offsetParent.offsetTop + 2*ETdotSize;}
   // put 0th dot above 1st (it is hidden)
   dots[0].X = dots[1].X;
   dots[0].Y = dots[1].Y - ETsegLen;}
// These functions capture information when events occur.  (Also include common Timer and Event Handling routines)
// (none)
// These functions model the physical forces that move the dots around:
// adds force in X and Y to spring for ETdots[i] on ETdots[j]
function ETspringForce(i, j, spring) {
   var dx = (ETdots[i].X - ETdots[j].X);
   var dy = (ETdots[i].Y - ETdots[j].Y);
   var len = Math.sqrt(dx*dx + dy*dy);
   if (len > ETsegLen) {
      var springF = ETspringK * (len - ETsegLen);
      spring.X += (dx / len) * springF;
      spring.Y += (dy / len) * springF;}
   }

function ETanimate() {
   // ETdots[0] follows the mouse, though no dot is drawn there
   var start = 0;
   if (ETfollowMouse) {
      ETdots[0].X = Xmouse;
      ETdots[0].Y = Ymouse;
      start = 1;}

   for (i = start ; i < ETnDots; i++ ) {
      var spring = new vector(0, 0);
      if (i > 0) {
         ETspringForce(i-1, i, spring);}
      if (i < (ETnDots - 1)) {
         ETspringForce(i+1, i, spring);}

      // air resisitance/friction
      var resist = new vector(-ETdots[i].dx * ETresistance,
         -ETdots[i].dy * ETresistance);

      // compute new accel, including gravity
      var accel = new vector((spring.X + resist.X)/ ETmass,
         (spring.Y + resist.Y)/ ETmass + ETgravity);

      // compute new velocity
      ETdots[i].dx += (ETdeltaT * accel.X);
      ETdots[i].dy += (ETdeltaT * accel.Y);

      // stop dead so it doesn't jitter when nearly still
      if (Math.abs(ETdots[i].dx) < ETstopVel &&
         Math.abs(ETdots[i].dy) < ETstopVel &&
         Math.abs(accel.X) < ETstopAcc &&
         Math.abs(accel.Y) < ETstopAcc) {
         ETdots[i].dx = 0;
         ETdots[i].dy = 0;}

      // move to new position
      ETdots[i].X += ETdots[i].dx;
      ETdots[i].Y += ETdots[i].dy;

      // get size of window
      var height, width;
      if (isNetscape) {
         height = window.innerHeight + document.scrollTop;
         width = window.innerWidth + document.scrollLeft;}
      else {
         height = document.body.clientHeight + document.body.scrollTop;
         width = document.body.clientWidth + document.body.scrollLeft;}

      // bounce of 3 walls (leave ceiling open)
      if (ETdots[i].Y >= height - ETdotSize - 1) {
         if (ETdots[i].dy > 0) {
            ETdots[i].dy = ETbounce * -ETdots[i].dy;}
         ETdots[i].Y = height - ETdotSize - 1;}
      if (ETdots[i].X >= width - ETdotSize) {
         if (ETdots[i].dx > 0) {
            ETdots[i].dx = ETbounce * -ETdots[i].dx;}
         ETdots[i].X = width - ETdotSize - 1;}
      if (ETdots[i].X < 0) {
         if (ETdots[i].dx < 0) {
            ETdots[i].dx = ETbounce * -ETdots[i].dx;}
         ETdots[i].X = 0;}

      // move img to new position
      ETdots[i].obj.left = ETdots[i].X;
      ETdots[i].obj.top = ETdots[i].Y;

      }
   }