SVG starfield animation

This little project was the result of an email request, which I'm sharing here because I thought it was quite interesting. The question was, how could you make a starfield animation (like the old screensaver) with SVG.

Drawing

The first thing we need is a star. I drew one in Inkscape. Since we want lots of the same shape, I put it a def element, which means we define the shape for later use.

<defs>
    <path id="star" fill="#ee2" d="M-4 -17.2L-8.7 -3.6 -23.1 -3.3 -11.6 5.4 -15.8 19.2 -4 11 7.9 19.2 3.7 5.4 15.2 -3.3 0.8 -3.6z"/>
</defs>

Then I created a background and a group which will contain the stars:

<rect width="450" height="450" fill="#228" />
<g id="star-group"></g>

Scripting

Everything else is done with ECMAScript (basically Javascript in SVG).

First we add the script element and define some variables:

<script type="text/ecmascript">
<![CDATA[
    var svgDocument;
    var svgNS = "http://www.w3.org/2000/svg";
    var xlinkNS = "http://www.w3.org/1999/xlink";

    var centerX, centerY, maxD;
    var nStars = 32;
    var angles = [];
    var distances = [];
]]>
</script>

The first three variables are required so we can work with the SVG. centerX, centerY and maxD will be defined later. nStars is the number of stars we'll have on screen at any time, angles and distances will contain the angle and distance of each star from the center of the screen.

function init(evt) {
    if (window.svgDocument == null) {
        svgDocument = evt.target.ownerDocument;
    }
            
    centerX = svgDocument.childNodes[0].getAttributeNS(null, 'width')/2;
    centerY = svgDocument.childNodes[0].getAttributeNS(null, 'height')/2;
    maxD = Math.sqrt(centerX * centerX + centerY * centerY) + 40;
}

Next we add an init function which finds the dimensions of the SVG and uses them to find the center of the screen. maxD is the maximum distance a star can be from the center of the screen before we remove it.

Creating and drawing stars

Now we need a function to create stars:

function createStars(n) {
  for (var i=1; i<=n; i++) {
    var star = document.createElementNS(svgNS, "use");
    star.setAttributeNS(null, "id", "star" + i);	
    star.setAttributeNS(xlinkNS, "href", "#star");			
    document.getElementById("star-group").appendChild(star);
    angles.push(Math.PI * 2 * Math.random());
    distances.push(maxD * Math.random());
  }
}

The createStars function creates n use elements, each of which refers to the star def element and has a different id so we can manipulate them separately. The use elements are all added to the group with the id "star-group". A random angle and random distance is added to the respective arrays. These are used as polar coordinates to position the star elements.

function drawField() {
  for (var i=0; i<nStars; i++) {
    var star = svgDocument.getElementById('star' + (i+1));
    var d = distances[i];
    var x = centerX + d * Math.sin(angles[i]);
    var y = centerY + d * Math.cos(angles[i]);
    var scale = 2 * (1 - (maxD / (d + maxD)));
                
    star.setAttributeNS(null, 'transform', 'translate(' + x + ',' + y + ') scale(' + scale*scale + ')');
  }
}

The drawField function adds a transform attribute to each star. It uses the angles and distances array to find the position the star should be and translates it to that position. It also scales the star based on its distance from the center of the screen. The scaling isn't quite right, but it's close enough.

Animating the stars

Every tick of the animation we increase the distance of the each star from the center of the screen. Again, it's probably not right to increase the distance linearly, but you get the idea. If the distance is so great that the star is off the screen, we reset the distance to 0. We could also pick a new random angle.

function updateImage() {
    for (var i=0; i<nStars; i++) {
        distances[i]++;
        if (distances[i] > maxD) {
            distances[i] = 0;
        }
    }
    drawField();
}

Then we need to repeatedly call this function and for this we can use setInterval, which allows us to call it once every 10 ms.

function beginAnimation() {
    var timeout = setInterval(updateImage, 10);
}

Finally, to the init function we add:

createStars(nStars);
beginAnimation();

This creates the stars and starts the animation.

AttachmentSize
starfield.svg2.65 KB

Post new comment

The content of this field is kept private and will not be shown publicly.