Introduction to  SMIL animation in SVG

(© 2006 - 2009 by David Dailey and the World Wide Web Consortium: W3C)

Overview of SMIL

Synchronized Multimedia Integration Language, or SMIL, is a language separate from, but closely integrated with SVG. It allows for various animation effects to be used in conjunction with SVG.

SMIL has been a W3C recommendation since 1998 when version 1.0 was adopted22. The SMIL working group has remained active since then with version 2.1 becoming a recommendation in December 2005.

SMIL's applications are not limited to the SVG environment, being appropriate for multimedia developments in a variety of other contexts, including HTML. The W3C has this to say about SMIL23:

"The Synchronized Multimedia Activity designed the Synchronized Multimedia Integration Language (SMIL, pronounced "smile") for choreographing multimedia presentations where audio, video, text and graphics are combined in real time. SMIL is a W3C Recommendation that enables authors to specify and control the precise time a sentence is spoken and make it coincide with the display of a given image."

This discussion of SMIL will be limited to its application within SVG, and even then, will cover only a fraction of this rather vast landscape.24For a broader consideration, there are numerous references available on the web, and several books including one by two members of the SMIL Working Group25.

SMIL is an example of what is known as declarative animation (related to declarative programming). Rather than specifying the details of how to do something (as in an imperative language), a declarative approach specifies what the end result is supposed to be and leaves the details of implementation up to the client software. Other examples of declarative approaches include HTML, PostScript, and SVG. With each, the programmer/developer describes something like a <circle> and lets the device implement it to the best of its ability. The developer does not need to worry about kerning of characters, placement of pixels, anti-aliasing, dithering and fill-region algorithms. In fact the entire field of vector graphics is sort of a case in point: one describes the thing to be drawn and then different devices (like a screen or a printer) will render the underlying concept in, perhaps, very different ways. 26

An example:

Progress of a simple SMIL animation

at start oval has rx=10

midway in size and in time

largest oval appears halfway through the time interval

Prior to clicking "GO" 1 (or 3) seconds elapsed 2 seconds elapsed
<ellipse cx="150" cy="75" rx="10" ry="40" fill="grey">
<animate attributeName="rx" begin="G.click" end="S.click" dur="4s"
values="10; 110; 10" repeatCount="indefinite"/>
</ellipse>
<g id="G">
<rect x="85" y="130" height="20" width="60" fill="#bbb"/>
<text x="90" y="148" font-size="20" fill="black">GO
</g>
<g id="S">
<rect x="150" y="130" height="20" width="60" fill="black"/>
<text x="155" y="148" font-size="20" fill="white">STOP
</g>
This example may be seen at http://srufaculty.sru.edu/david.dailey/svg/newstuff/SMIL1.svg

Above, we have two rectangles each inside <g>s named either "G" (for "GO") or "S" (for "STOP"). Unlike in HTML, neither "button" has an event handler directly associated with its code. Instead, the "arming" of the button is done by any of the things that will respond to it. The ellipse has had a special tag, <animate>, inserted into it. That's where the SMIL takes place.

Let's take a closer look:

The animate tag in SVG/SMIL
The code The meaning
<animate

The tag is embedded in the middle of the SVG object to be animated, in this case an ellipse.

attributeName="rx"

This identifies which attribute of the parent object (ellipse) will be modified by the animation.

begin="G.click"

This says that the animation will begin when the object with id="G" is clicked upon.

end="S.click

This says that the animation will finish when the object with id="S" is clicked upon.

dur="4s"

This says the animation will last 4 seconds.

values="10;110; 10"

This establishes the values of the animated attribute (rx), which will begin at 10 (pixels) increase incrementally to 110 (pixels) and finish back at 10 again. By default, 110 will be reached halfway between the beginning and end of the animation: namely after 2.0 seconds have elapsed.

repeatCount="indefinite"

This means the animation will keep going indefinitely. That it will continue repeating until "S" is clicked.

/>

This terminates the <animate> tag.

An important observation from the above, is that we do not tell this infinite loop how often it is to refresh the screen; we trust the browser to figure that out for us based on the description of what we want to have happen. Again the emphasis is on what is done rather than how to do it.

Another thing to note is that in this case we wish to have the ellipse grow and then shrink again (gradually in both directions). To do this, we specify the maximum value (110), place it in the middle of the two minima (10) at either end of the values list, and let the browser figure out that 110 will occur halfway through the 4 second animation.

Comparison with JavaScript animation

Before plunging into SMIL's capabilities and exploring the syntax of the <animate> and allied tags, we'll make a brief comparison to another prevalent approach to web-based animation. If you are not familiar with JavaScript-based animation, then you may wish to skip this section. For those with prior knowledge of JavaScript animation, it may help to put SMIL animation in a helpful cognitive framework. This section is not required for an understanding of SMIL; rather it is a justification for why you should learn SMIL! It is easier.

While this book discusses the IE/ASV, Firefox, Opera, Safari and Chrome browsers, only IE/ASV and Opera support most of SMIL at the current time, while Safari's support is somewhat fledgling, having just been introduced in Safari 4 beta in early 2009. JavaScript is supported in all five environments. Some might view this as reason to dismiss SMIL right here and now, but by the time this book reaches your hands, it is quite possible that Firefox and other browsers will be quite close to having SMIL implemented within their SVG suite.

In JavaScript, animation is usually created using the methods setTimeout() or setInterval() (associated with the window object). These methods allow repeated updates of the screen after changing certain attributes of the objects on the screen.

Suppose, for example, we wish to move an object around on the screen by changing the values of its x and y coordinates of a <div> that contains it. (Typically, the object will be a child of an absolutely positioned <div> tag.) With every refresh of the screen (happening every dt units of time) we move the <div> dx pixels horizontally and dy pixels vertically. The author of such an animation must guess the screen refresh rate of a typical visitor's client software and then adjust dt, dx, and dy accordingly so that dx and dy are kept as small as possible subject to the constraint that the browser must do all that it needs to in dt units of time. That is, the author must engage in guesswork and experimentation to determine what will produce a smooth animation. If we make dt too small then the CPU and screen of the user's machine may not be able to perform all the calculations that we require of it. If dt is too large, the object will appear to move very slowly, implying that we may wish to increase dx and dy to increase the speed. This however, is fraught with problems since values of dx and dy that are too large will result in apparent large jerking leaps across the screen.

Another complexity is involved in setting up multiple independent animations running in parallel. This has been rather notorious for the difficulty of managing the timing. Typically, one sets up a large central timing loop in which all animated objects are updated every dt units of time — the problem is that not all animations will look quite right if they are all running in integer multiples of the same dt, which is what happens with the central timing loop. Alternatively, one may try to set up multiple setTimeout loops, but the problem here (as oft reported) is that any synchrony associated with the separate loops tends to dissipate over time, particularly when the JavaScript application is taxing the CPU to begin with.

In contrast, the declarative animation of SMIL lets the browser software handle all these decisions since the locus of the animation is kept directly affiliated with the animated object.

Herewith, a side-by-side comparison of the two different approaches to setting up an oscillating ellipse as in the above example.

Oscillating ellipse — two approaches with similar results
SMIL animation JavaScript animation
<svg xmlns="http://www.w3.org/2000/svg">
<ellipse cx="150" cy="75"
rx="10" ry="40" fill="blue">
<animate attributeName="rx"
dur="4s" values="10;110;10"
repeatCount="indefinite"/>
</ellipse>
</svg>
<svg xmlns="http://www.w3.org/2000/svg"
onload="startup(evt)">
<script>
<![CDATA[
var xmlns="http://www.w3.org/2000/svg"
var E;
function startup(evt){
E=document.getElementById("E")
oscillate(10,110,10,1)
}
function oscillate(thin,wide,curx,dir){ E.setAttributeNS(null,"rx",curx); curx+=dir; if (curx>wide||curx<thin ) { s="oscillate(" window.settimeout(s,10); dir=-dir; } }
</script>
<ellipse id="E" cx="150" cy="75" rx="10" ry="40" fill="blue"/>
</svg>

Note that there is considerably less code to maintain, and considerably less complexity in the code with SMIL for this sort of animation27. It should also be stated that the behavior of these two approaches is not completely equivalent. On the Windows machine I am using today, the JavaScript approach runs slightly faster in Internet Explorer. An older machine will run it slower. This can be changed by adjusting the timing — currently it changes the radius of the ellipse one pixel every 10 milliseconds. In Opera, the JavaScript animation is not smooth (appearing to jerk just a bit), though the SMIL animation is quite smooth.

One pleasant feature about the SMIL approach is the close integration of the animation with the object itself. Note that the <animate> tag is nested directly within the <ellipse> rather than in a function located elsewhere and relying on at least some global variables.

Not all animated effects are best in SMIL. Many types of animation (like setting a variety of objects bouncing off of one another) are simply not feasible in SMIL. However, both SMIL and JavaScript-based animations are possible in SVG, so that the developer has both sets of capabilities at his or her disposal.

Most simple SMIL animations appear at least as smooth if not smoother than their JavaScript counterparts. At first glance, some SMIL animations may seem more sluggish than their JavaScript counterparts. In general, it seems that so long as the attributes being animated and the objects to which they belong remain relatively simple, SMIL animations will be just as robust and often smoother than JavaScript animations for the same purpose. There are examples28, however where the JavaScript animation will appear smoother than its SMIL counterpart. Many of these differences are likely to be browser-dependent.

In some tests of the relative priorities given to SMIL and JavaScript animation, when both are running and when the calculations or screen I/O overwhelm the processor, it was found that both the IE/ASV and Opera browsers give first priority to the SMIL animation, attempting to accomplish those tasks first before any JavaScript is attempted.29This means, on occasion, that applications which rely on both may end up with the JavaScript completely stalled.

Basic SMIL animation

Let's start with a sort of simplest case: a rectangle that moves across the screen from left to right.

<rect x="10%" y="20" height="100" width="50" fill="blue">
<animate attributeName="x" dur="4" values="10%;90%" />
</rect>

In this case we've specified exactly three attributes: the attribute being changed (attributeName), the duration of the effect (dur) and the starting and ending values of the animated attribute. This results in a blue rectangle that starts moving when the page is loaded, moves across the window (until it is 90% of the way across), stops for a moment's hesitation30, and then resets itself to its original, pre-animated, position. If we had begun with the assignment x="50%" instead of x="10%" we would see no difference in the appearance of the animation from beginning throughout its duration (since by the time the screen is rendered the animation has already begun), but at the end of the animation, the rectangle will reset to x="50%": hence a difference in how it terminates.

We may also make all these measures not in terms of relative screen coordinates like 95%, but in absolute pixel amounts like 120, if we so prefer.

If we wished to have the animation stop at its last position, instead of reverting to its first, we could add the attribute

fill="freeze"

If we wished for the animation to move smoothly rightward (until 90% of the way across the screen) and then return smoothly to the beginning, we simply add another value to the values list.

<rect x="10%" y="20" height="100" width="50" fill="blue">
<animate attributeName="x" dur="4" values="10%;90%;10%" />
</rect>
This example may be seen at http://srufaculty.sru.edu/david.dailey/svgopen2009/rect.svg

This succeeds in moving the rectangle all the way across the screen and back in the same four seconds used to move it just one direction in the earlier example. If we wished the last two animations to move at the same speed, then we would double the duration, since the distance to be traveled is twice what it was.

We note also that the animation will reach its maximum value of x (90%) exactly halfway through the four seconds, since 90% is at the midpoint (the median position) of the three values contained in the values list. That is to say:

values="10%;90%;10%"
and
values="10%;50%;90%;50%;10%"

are equivalent (since 50% is halfway between 10% and 90%), while

values="10%;90%;10%"
and
values="10%;30%;90%;30%;10%"

are not. The latter values list will cause the rectangle to move faster during its middle half (the second and third of the four intervals determined by the five endpoints) than during its beginning or end.

If we wish the animation not to stop but to run itself three times (at four seconds apiece), then we add an attribute:

repeatCount="3"

If we wish it not to stop but to keep going, then

repeatCount="indefinite"

will do the trick. It will keep going either until the page is closed, or the animation is stopped through some event, as discussed shortly.

Multiple animations and timing

It is important to realize that we may animate more than one element at a time:

<rect x="50%" y="20" height="5%" width="5%" fill="blue">
<animate attributeName="x" dur="2" values="10%;90%;10%" />
<animate attributeName="y" dur="2" values="10%;90%;10%" />
</rect>

This code succeeds in moving the rectangle back and forth diagonally across the screen, from upper left to lower right.

In this example, the timing of both the x and y attributes are the same, meaning that both values reach their maxima and minima at the same time (as in a typical single loop JavaScript animation). This need not be the case however. The following code creates an animation in which the circle bounces about the screen like a billiard ball:

<circle cx="50%" cy="20" r="5%" fill="blue">
<animate attributeName="cx" dur="2.7" values="5%;95%;5%"
repeatCount="indefinite" />
<animate attributeName="cy" dur="3" values="5%;95%;5%"
repeatCount="indefinite" />
</circle>

This particular animation will repeat itself every 27 seconds, since 27 is the smallest number that is evenly divisible by both 3 and 2.7 — a necessary condition for the periodicities of cx and cy to synchronize. Observe, also that the angles involved in the bouncing of this billiard ball are all in the neighborhood of 45 degrees (assuming a square screen) since the values 2.7 and 3.0 are close in magnitude. If we wanted the bouncing to be more vertical than horizontal, then we could merely decrease the duration of the cy variable to something like:

<animate attributeName="cy" dur="0.5" values="5%;95%;5%"
repeatCount="indefinite" />

while keeping the duration of cx unchanged. Alternatively, to the same end, we could also increase the number of key values for cy as follows:

<animate attributeName="cy" dur="3" repeatCount="indefinite" values="5%;95%;5%;95%;5%;95%;5%" />

It is well worth pointing out that the types of the values should match. For example, if we were to try to interpolate between the value "0" and the value "50%", the browsers can be expected to use discrete animation in such a case. There are good reasons for this. I will leave finding those reasons as an exercise for the reader. ☺

 

Examples of these simple animations may be seen at
http://srufaculty.sru.edu/david.dailey/svg/newstuff/SMIL3.svg
and
http://srufaculty.sru.edu/david.dailey/svg/newstuff/SMIL4.svg

keyTimes

In such animations, though we have succeeded in varying the horizontal and vertical components of the velocity independently, the apparent overall speed of the movement remains constant. We may vary the apparent speed through the use of the keyTimes attribute.

What keyTimes does for us is to allow the values to provide an uneven distribution over the time interval. Ordinarily, when we specify something like values="5%; 95%; 5%", those values for the animated variable correspond to the times 0%, 50% and 100% (as a percent of the way through the animation). The statement values="5%; 10%; 95%; 10%; 5%" would, by default, correspond to the times (0%; 25%; 50%; 75%; 100%) — that is, five "key times" associated with the four intervals between the beginning and end of the animation. Accordingly, the animation given by

<animate attributeName="cy" dur="0.5" values="5%;95%;5%" repeatCount="indefinite" />

is equivalent to the one specified by:

<animate attributeName="cy" dur="0.5" values="5%;95%;5%" keyTimes="0; .5; 1" repeatCount="indefinite" />

The keyTimes attribute, by default, breaks the animation duration into N equal intervals, where N is the number of values in the values attribute. keyTimes, like values, is a semi-colon delimited list that serves to determine when, in the course of the animation each of the values should be attained by the animated object. We may change the time at which the animation will reach intermediate values, by making unequal the intervals specified within keyTimes. The animation

<animate attributeName="cy" dur="10" values="5%;95%;5%"
keyTimes="0; .1; 1" repeatCount="indefinite" />

will make the attribute cy take on its value of 95% after one second (the duration, 10 seconds, multiplied by the second key time: 0.1). The ellipse will, however, take nine seconds to get back to the starting position, meaning that it moves considerably faster in the first second than in the remaining time.

keySplines

Thus far each of the animations we have considered (changing x and y coordinates) will result in animations for which both a) the paths traversed by the objects will be piecewise linear and b) the derivative of the velocity curve will be either constant or discontinuous. That is if there is to be any change of direction, speeding up or slowing down, then these changes will be sudden or discrete rather than gradual and continuous. There is, however, another timing control mechanism, known as keySplines which allows us to change the curve governing the rate of change over the keyTimes interval from 0 to 1 and thus produce gradual rates of change to an object's attributes.

If we specify that

calcMode="spline"

then we are in a position to be able to use keySplines to make transitions among the keyTimes follow cubic splines rather than simple component-wise linear chunks.

This topic is a bit complex, and since it is not crucial to one's understanding of SMIL animation, one might wish to skip ahead. Nevertheless, herewith is some discussion and examples of the use of keySplines.

A series of points traversed by a moving object using keySplines.

progress of an animation with keySplines

<circle cx="50%" cy="20" r="5%" fill="blue">

 <animate attributeName="cx" dur="3" values="5%; 95%;5%"
 repeatCount="indefinite" />
 <animate attributeName="cy" dur="6s" values="5%; 95%;5%" keyTimes="0;0.5; 1"
 calcMode="spline" keySplines="1 0 0 1; 0 0 1 1" repeatCount="indefinite" />
</circle>
Examples of non-linear motion induced through keySpines
http://srufaculty.sru.edu/david.dailey/svg/newstuff/SMIL5.svg
http://srufaculty.sru.edu/david.dailey/svg/newstuff/SMIL2a.svg

Here, the path followed during the first half of the animation is the path on which points 1,2,3,4,5, and 6 lie. After the midpoint of the animation, the blue circle returns to its initial position via the line 6, 7, 8, 9, 10. If calcMode="spline" were not invoked, the animation would follow the path (10,9,8,7,6,7,8,9,10) repeatedly. The keySplines attribute contains a series of two control points in the (timein, timeout) plane, or four integers for every inter-keyTimes interval: in other words, we will have 4(n-1) integers for n keyTimes. Each pair of control points defines an approach gradient between the associated pair of key values. In this example, we will begin at (t=0, y=5%) and follow the spline attracted first by (t=1, y=5%) (all the while x is increasing linearly); then attracted by (t=0, y=95%) and finally ending up (at the end of the first transition) at (t=1, y=95%) namely halfway through the animation at position #6. (since x with half the duration has already been reset to its endpoint, 5%). The animation will speed up considerably between positions #3 and #4 since that is when the time deformation is greatest. The animation continues back along the two lines, since the keySplines path "0 0 1 1" does not alter the time sequence in the second half of the animation.

One more example may help to clarify a bit.

<circle cx="50%" cy="20" r="5%" fill="blue">
 <animate attributeName="cx" dur="3" values="5%;50%;95%;50%;5%"
 keyTimes="0;0.25;0.5;0.75; 1" calcMode="spline"
 keySplines="0 0 1 1;0 .5 .5 1;0 0 1 1; 0 .5 .5 1"
 repeatCount="indefinite"
 />
 <animate attributeName="cy" dur="3s" values="50%;5%;50%;95%;50%"
 keyTimes="0;0.25;0.5;0.75; 1" calcMode="spline"
 keySplines="0 .5 .5 1;0 0 1 1; 0 .5 .5 1 ;0 0 1 1"
 repeatCount="indefinite"
 />
</circle>

In the above the blue circle will follow the path given, more simply, by the ellipse

<ellipse cx="50%" cy="50%" rx="45%" ry="45%" />

Were it not for the keySplines, the animation would follow a diamond connecting the four midpoints of the bounding rectangle of the screen. However since time runs linearly in the x direction while warped in the y direction and vice versa, the path ends up being bowed. The other thing about the above animation is that it speeds up considerably at the position of the key values; since that is where the time gradient changes most rapidly.

In case this seems a bit complex, it is. The W3C has this31to say on the subject:

When a keySplines attribute is used to adjust the pacing between values in an animation, the semantics can be thought of as changing the pace of time in the given interval. An equivalent model is that keySplines simply changes the pace at which interpolation progresses through the given interval. The two interpretations are equivalent mathematically, and the significant point is that the notion of "time" as defined for the animation function f(t) should not be construed as real world clock time. For the purposes of animation, "time" can behave quite differently from real world clock time.

The reader will perhaps be relieved to discover that there are simpler ways to get an object to travel along a particular path during animation than by warping the time-space continuum. We will turn to other varieties of animation very shortly.

Varieties of animation

Before moving to other aspects of SMIL, a couple of observations should be made. The author of an SVG document is not limited to one animated object, nor just a few per page. I have tested the embedding of several thousand independent objects each with independent SMIL animations, and generally (depending on the complexity of the features being animated), the browsers keep up fairly well.

We might also encourage ourselves to remember that almost anything can be animated. In reading the W3C recommendation, finding something that cannot be animated is rather rare32. Animation is a most interesting aspect of SVG. It also can be quite instructive to the learner: by animating an attribute, one can gain a very concrete sense of what exactly it is that that attribute controls. Here is a brief listing of some of the types of effects one can create -- some with rather stunning results:

Various animatable attributes of objects

The cx, cy, rx and ry of an ellipse

The height of an image

The opacity of an image

The scale of an feDisplacementMap

The focal points and radius of a reflected radial gradient

The offset and stop-opacity of a stop in a radial gradient

The baseFrequency or seed of an feTurbulence

The height of a rectangle contained in a pattern

The values of an feColorMatrix

We also have the capability to animate features which are multi-valued, path-based, transformational, non-numeric, or chromatic. We will discuss examples of each, in turn.

animation of multi-valued attributes

Thus far, all the animated effects we have investigated for SMIL have involved single-valued (or scalar) attributes: such things as the height of a rectangle, the radius of a circle. However in some cases, the value of an attribute may be a list (or vector) with multiple values in it. An obvious example is the d attribute of a path: a space delimited list of x and y coordinates. Well, it so turns out, that we can animate these sorts of things as well, provided the number of items in the lists associated with the animated attributes matches up. If a path has 17 points at the beginning of the animation it should have 17 points throughout the animation.

Then, (provided that the lists match up in quantity and are numeric), SMIL interpolates by generating intermediate frames. It's great for animating Bézier curves to give character to a contour or allow a complex shape to mutate gradually. Here's a simple example:

Gradually changing one control point in the "d" attribute of a cubic spline curve

animated bezier curve stage 1

animated bezier curve stage 2

animated bezier curve stage 3

Beginning (0 seconds)

C2=(100,400)

Middle of first half

(about 0.5 sec)

C2 ≈(100,250)

End of first half (1 sec) C2=(100,100)

<animate attributeName="d" dur="2s" repeatCount="indefinite" values=
"M 100 200
C 100 150 250 150 100 100
C 0 50 100 400 100 200
;
M 100 200
C 100 150 250 150 100 100
C 0 50 100 100 100 200
;
M 100 200
C 100 150 250 150 100 100
C 0 50 100 400 100 200
" />
This example may be seen at http://srufaculty.sru.edu/david.dailey/svg/newstuff/path10.svg

In this example, only one point has been changed, to make the example simple enough to read. But that is not at all a requirement. I've created animations of Béziers that contain several hundred points, and the animations appear to transition quite smoothly.

Following paths

One of the most convenient and pleasant aspects of SVG is the fact that we may build a path, and let an object follow it over time. Given the complexity of Bézier curves for example, being able to instruct an object "follow this path" without having to calculate, in your code, where the path actually is in (x,y) coordinates, is remarkably handy and user-friendly. It is not too tricky, so here's an example.

Using <animateMotion> to follow a <path>. Five stages of a repeating animation.

Five stages of an animation moving along a curve

<path id="curve" stroke="black" fill="none" stroke-width="5"
 d="M 110,200 C 300,130 400,350 450,150 500,-50 -90,270 110,200" />

<ellipse cx="7" cy="-5" rx="20" ry="14" fill="#aaa" stroke="#666"
 stroke-width="2" opacity=".8">
 <animateMotion dur="2s" rotate="auto" repeatCount="indefinite" >
 <mpath xlink:href="#curve"/>
 </animateMotion>
</ellipse>-
 
This example may be seen at http://srufaculty.sru.edu/david.dailey/svg/newstuff/SMIL7g.svg

In the above, the path (with id="curve") describes a smooth curve using two cubic transitions. It begins and ends at the same (x,y) coordinate, (110,200). The <ellipse> is instructed to follow "curve" through the use of an <animateMotion> containing an embedded reference to the path by its id. We specified rotate="auto" so that the ellipse will actually rotate as it moves around the path so that its orientation parallels that of the path. Another matter worth noting is the initial position of the ellipse. Observe that ellipse #2 is just slightly above the curve. This is because its initial position cy=-5 becomes translated into an offset relative to its ultimate position on the curve.33

Timing of the motion is uniform relative to the length of the curve. If we wish to vary the timing, and make it differential over the path, then we can apply keySplines as discussed above in the case of the <animate> tag. The following can be inserted into the above example, replacing the earlier <animateMotion> to change the motion so that it appears to hesitate twice in each traversal of its path

<animateMotion dur="2s" keyTimes="0;.6;1"
calcMode="spline" keySplines="0 0 1 0;0 0 1 0"
rotate="auto" repeatCount="indefinite" >
<mpath xlink:href="#curve"/>
</animateMotion>

Animation of transformations

Not all SVG objects share the same collection of attributes. A rectangle and an image share the attributes x, y, height and width, but an ellipse has cx, cy, rx, and ry to control its placement on the screen. A common way of moving all these objects about on the screen is a natural thing for a developer to want. Likewise we might wish to be able to change the size and rotation of objects without having to calculate the new coordinates and replot a new curve. We have seen (in Chapter 2) how to use the transform tag to rotate, translate, and scale objects, so it might appear obvious to try something like:

<someSVGtag transform="rotate(90)">
<animate attributeName="transform" dur="2s"
values="rotate(90);rotate(180)" />
</someSVGtag>

It doesn't work quite that way, but that's actually close to how it does work. You may recall then when we wanted to rotate a linearGradient we used something called <gradientTransform>. Likewise, to rotate an animation we use <animateTransform>. An example:

<ellipse cx="280" cy="175" rx="100" ry="50" fill="blue">
<animateTransform attributeName="transform" type="rotate" dur="2.5"
from="360,280,175" to="0,280,175" repeatCount="indefinite"/>
</ellipse>

This succeeds in drawing a blue ellipse and then rotating it 360 degrees counterclockwise every 2.5 seconds.

We discussed the impact that one transform will have on the next in Chapter 2 (section 4: "Multiple transformations and more"). Likewise during animation, we may be interested in, for example, keeping an object in the same relative screen location while changing its scale and rotating it, hence in applying more than one <animateTransform> to a given object.

<ellipse cx="216" cy="242" rx="160" ry="219" fill="#964">
<animateTransform attributeName="transform" type="translate" dur="4s"
values="0,0;-110,-140;0,0" repeatCount="indefinite"/>
<animateTransform attributeName="transform" additive="sum" type="scale"
dur="4s" values="1;1.5;1" repeatCount="indefinite"/>
<animateTransform attributeName="transform" additive="sum" type="rotate"
dur="7s" values="0,216 242;360 216 242" repeatCount="indefinite"/>
</ellipse>

Just as we had to adjust for the effect that one transform had on the object when applying another with static images, we likewise must do so with moving images. The above example will both rotate and resize an ellipse, but preserve its center.

In order to do this, we require that the attribute

additive="sum"

be set to prevent the previous animations from being ignored.

Animation of non-numeric attributes

While SMIL provides for smooth transitions between attributes with numeric values, these are not the only sorts of attributes that can be animated. A couple of examples are provided to encourage the reader to think broadly about SMIL's utility.

Various non-numeric but useful animations.
The xlink:href of an <image>. It creates an image "rollover" transitioning between 3 images, and showing each for 2 seconds apiece
<image x="25%" y="26%" xlink:href="p80.jpg" 
 mask="url(#Ma)" width="30%" height="50%">
<animate attributeName="xlink:href" dur="6s"
 values="p1.jpg;p12.jpg;p9.jpg" repeatCount="indefinite"/>
</image>
The fill of a <rect>. Color values are interpolated smoothly.
<rect x="29%" y="65%" fill="cyan" filter="url(#twoE)" 
 height="25%" width="20%">
 <animate attributeName="fill" dur="15s"
 values="green;yellow;magenta;white;cyan "
 repeatCount="indefinite"/>
</rect>
The flood-color of an <feFlood>. Color values are interpolated smoothly.
<animate attributeName="flood-color" dur="10s" 
 values="cyan;yellow;green;magenta;white;purple"
 repeatCount="indefinite"/>
The mode of an <feBlend>. Useful for seeing how <feBlend> modes actually work..
<filter id="sample">
 <feBlend mode="darken" in2="BackgroundImage"
 in="SourceGraphic">
 <animate attributeName="mode" dur="5s"
 values="screen;multiply;lighten;darken;"
 repeatCount="indefinite"/>
 </feBlend>
</filter>

If values of an attribute cannot be interpolated between, that is not a problem for SMIL. If intermediate values (as in the modes of an <feBlend> or the files associated with an <image> are the values chosen, then each value is simply kept for an appropriate fraction of the duration of the animation.

On the other hand, since the values of colors associated with the "fill" attribute can be interpolated, the browser will actually do so (at least IE/ASV and Opera where these effects can be observed).

SVG actually contains an <animateColor> tag, though it appears to offer little, if anything, that cannot be done with the animation of the fill, stroke, or flood-color attributes as in the above examples.

Starting an animation, with time or events, and using <set> to set the value of an attribute

A given SVG page may combine textual information with interesting graphics. That is clearly part of the appeal of SVG and the web in general (else we'd still all be using gopher34). Suppose that we would like our visitors to be able to read our information for a few seconds before certain animations begin — perhaps we fear that our graphics might be so compelling that they might overwhelm the message. We could tell our marketing division that if they are really worried then they should tone down the graphics a bit, or we could simply delay their onset.

Another scenario that makes more sense to this academic fellow is this: suppose I wish to have several objects following one another around a path — spaced out at equal intervals around the path. I that case, I can animate each but delay when it is that they begin. Each earlier one can be given a head start along the path. We may do that by specifying a begin attribute of, say begin="1.0s" to delay the onset of the animation of one of the things we wish to hold back.

Two ellipses following the same path; one is halfway behind the other
<path id="curve" stroke="black" fill="none" stroke-width="5"
d="M 110,150 C 300,80 400,300 450,100 500,-100 -90,220 110,150" />
<ellipse rx="20" ry="12" fill="#aaa" stroke="#666" stroke-width="2" opacity=".8">
 <animateMotion dur="2s" rotate="auto" repeatCount="indefinite" >
 <mpath xlink:href="#curve"/>
 </animateMotion>
</ellipse>

<ellipse rx="20" ry="12" fill="#aaa" stroke="#666" stroke-width="2" opacity=".8">
 <animateMotion dur="2s" begin="1" rotate="auto" repeatCount="indefinite" >
 <mpath xlink:href="#curve"/>
 </animateMotion>
</ellipse>

In the above, the two ellipses are identical except for the "begin='1'" assignment in the second. Since "1" = "1.0s" is half of dur="2s", the second animation will begin halfway through the other's traversal of the path.

The only problem with this approach is that during that first second while it is waiting to start, the second ellipse is rendered, but at its origin (0,0) (since neither cx or cy is specified, their default values are each zero). That may be undesirable, or, at best, odd. How might we keep the image invisible while it is waiting to start?

Before the second animation gets started.
(Note the ellipse waiting in the corner.)

newborn ellipse waiting to be animated

Well, like many things in SVG, there is more than one way, and each is instructive in its own way.

First, we might proceed as follows: instead of positioning the ellipse-in-waiting at (0,0) why don't we just place it on the curve? We know, for example, that the point (450,100) lies on the curve (it is the last of a cubic triplet in the d attribute) and it would seem to be about halfway along the curve. So, let's try the following:

<ellipse id="secondEllipse" cx="450" cy="100" ... >

There are three problems with this. The first, possibly minor issue, is that the second ellipse will appear in its initial and unrotated orientation. It will not be pointing in the same direction as the curve. The second is that the point (450,100) is probably not the exact midpoint of the curve. This is perhaps not a major issue either. The biggest problem, though, is that the positioning of the thing will be done relative not just to the curve but to its own offset relative to (0,0). Ultimately the underlying transformations (rotation, scale, and translation) will be amplified by its now large distance from the origin (and hence the curve itself). The second ellipse will not in fact follow the curve but some strangely distorted and amplified shadow of it (most of which exists off-screen in this case).

Alternatively, we could animate the opacity of the second ellipse, having it wait until after it has moved from its initial position (0,0) onto the curve, thereupon to become visible.

<ellipse cx="0" cy="0" rx="20" ry="12" fill="#aaa"
stroke="#666" stroke-width="2" opacity="0">
<animate attributeName="opacity" values="0;.8" dur="1" begin="1"
fill="freeze"/>
<animateMotion dur="2s" begin="1" rotate="auto"
repeatCount="indefinite" >
<mpath xlink:href="#curve"/>
</animateMotion>
</ellipse>

We had to remember to set the initial value of its opacity to zero, since the onset of the animation is delayed by a second, meaning that its initial value of zero under the animation will not be encountered until the animation stops. We also have to set fill="freeze" so that the opacity retains its final value and does not reset to the initial value.

The above succeeds in bringing the second ellipse onto the curve at 1.0 seconds, but at zero opacity. It then begins moving and gradually fading into view over the next second. This works just fine and is a somewhat pleasant effect.

This approach with fading an object in by adjusting its opacity is, however, a bit fancier than we may have intended for this relatively simple problem. The goal was to prevent the object from becoming visible until its animation started.

It turns out there is a simpler way to control such a thing in SVG — the <set>. In the above example, instead of the <animate attributeName="opacity" ...> tag, use this:

<set attributeName="opacity" to=".8" begin="1" />

It keeps the second ellipse invisible until one second after the animations begin (which is just at the time it is introduced onto the path).

A more modular solution which lets the onset of the <set> coincide directly with the beginning of the <animateMotion> (rather than having both rely on the same unit of measured time: 1.0s) is given by the following:

<ellipse cx="0" cy="0" rx="20" ry="12" fill="#aaa"
stroke="#666" stroke-width="2" opacity="0">
<set attributeName="opacity" to=".8" begin="M.begin" />
<animateMotion id="M" dur="2s" begin="1" rotate="auto"
repeatCount="indefinite" >
<mpath xlink:href="#curve"/>
</animateMotion>
</ellipse>

We give the <animateMotion> an id ("M") and then let the "begin" of the <set> be triggered by the "begin" of "M." My wife would call this a "puckish solution."

begin="M.begin"

The above is an example of an animation (in this case a set — a sort of a degenerate case of an animation) being triggered by an event. We'll discuss this in just a bit more detail now, since it is popular and natural to use a mouse-click to start an animation.

Animation triggered by mouse-click

stage 1: large font

stage 2: small font

stage 3: large font

Before beginAt beginAfter dur="6s"
<text x="35%" y="35%" font-size="30" fill="red">
 <animate attributeName="font-size" begin="click"
 values="8;50;8" dur="6" repeatCount="indefinite"/>
Click</text>

When we click on the text object (the parent of the <animate> and hence, the target of the click), the animation begins, looping from a size 8 font to a size 50 font every 6 seconds.

What is noteworthy about this (and may seem different from what the JavaScript programmer may be familiar with) is that the event handler is assigned to the object receiving the click by some other object, in this case the <animate>. This is interesting since we may have multiple objects whose animations are all triggered by a click on the <text> though their identities are more or less unknown to the developer who looks only at the source code associated with the <text> itself.

Let's extrapolate a bit and make the example above have a bit more behavior. It is common in the world of the web and in user-interface practice in general, to have things which act like buttons (by triggering events) also feel like buttons. That is, in the real world, a button tends to have a bit of give to it: brushing your hand against the toaster accidentally does not turn it on (either the toaster or the hand). We often make our virtual buttons responsive, in somewhat the same way, to let the user know that it is a button, and hence, that it may do something when clicked. A common trick for this is to give it a rollover effect. This can be achieved quite easily in SVG.

Button with rollover effect

resting state: text is grey

on mouseover: text is black

on mouseout: text is grey

Before beginWhen mouse entersWhen mouse exits
<text x="35%" y="35%" font-size="30" fill="grey">
 <animate attributeName="font-size" begin="click"
 values="8;50;8" dur="6" repeatCount="indefinite"/>
 <set attributeName="fill" to="black" begin="mouseover" />
 <set attributeName="fill" to="grey" begin="mouseout" />
Click</text>

The above text will still expand and shrink as before but we note that we have also affiliated with the text the ability to respond to two new events: the event of when the mouse moves over it and then another when the mouse departs.

As we saw in an earlier example in this section (where we transitioned the opacity of ellipses to follow a curve), objects need not be triggered by events on nearby objects (in the sense of proximity in the document). We saw how

<set attributeName="opacity" to=".8" begin="M.begin" />

relied on an event associated with an object named M. We can also delay the onset of the second to follow the onset of the first by one second:

begin="M.begin+1"

Suddenly SMIL's timing starts to take on a richer character altogether! Likewise we may start an animation of object A with a mouse-click on object B.

begin="B.click"

And we might also use something of the following type:

begin="0s;B.click"

This means begin (the <animation> or <set>) either at zero seconds (when the page loads) or when a button is clicked. Using either of two ways to start an animation doesn't make sense, though, until we first can figure out how to stop an animation.

Stopping an animation

There are several fairly straightforward ways to stop an animation.35

We might:

  1. Let it stop of its own accord, on the completion of its duration or its repeatCount;
  2. Put a statement of the form end="S.click" in the <animate> tag and then build a "stop button" whose id="S". Clearly end="S.mouseover" end="otherAnim.begin" etc. would all do the trick as well;
  3. Intervene with it through JavaScript (by removing the <animate> from the DOM, by modifying the time of its end attribute, or by issuing an animateId.endElement() statement);
  4. Use a JavaScript statement, pauseAnimations( ) to halt all SMIL animations in the entire SVG document.

For now, approaches 1 and 2 are relevant to SMIL, while the others await our discussion of scripting.

While approaches 1 and 2 above, should follow fairly directly from the above section (on starting an animation), an interesting problem emerges if we try to create an on/off button which both starts and stops a given animation. The problem and its solution are just instructive enough, that it may prove useful to examine it in some detail.

To have a single button on a page which both starts and stops an animation seems like a natural thing. To have two buttons, one that starts something and another that stops it is fairly straightforward:

First we build a couple of buttons. We'll put text ("go" or "stop") on top of rectangles and put the text and its rectangle in a group with an id (so that clicks on either the text or the rectangle behind it are responded to by the group):

A start and a stop button

green button marked 'go' and red one marked 'stop'

<g id="stop" fill="#f88">
 <rect id="button" x="250" y="200" height="30" width="75" fill="inherit"/>
 <text x="260" y="220" font-size="20" fill="red">STOP
</g>
<g id="go" transform="translate(-100,0)" fill="#8f8" >
 <use xlink:href="#button"/>
 <text x="270" y="220" font-size="20" fill="green">GO
</g>

Clearly we could make much fancier looking buttons, but here we're interested more in making the code legible than in making the buttons fancy.

Now, we want to make an animation which responds appropriately to clicks on the other button.

Let's make an animation which stops when "stop" is clicked.

An animation (an ellipse following a curve) that stops when the "stop" button is clicked

stop and go buttons and ellipse on curve

<ellipse rx="20" ry="12" fill="#aaa" stroke="#666" stroke-width="2" opacity=".8">
<animateMotion id="One" dur="2s" end="stop.click" fill="freeze"
rotate="auto" repeatCount="indefinite" >
<mpath xlink:href="#curve"/>
</animateMotion>
</ellipse>

The reason we include

fill="freeze"

above is that without it, the ellipse would revert to its initial position at cx=0; cy=0. This keeps it at its last position when the animation stops.

This works just fine. Now how do we get the "go" button to work as well? It would seem natural to use

begin="go.click"

inserted into the <animateMotion> tag. The problem is that then the animation never starts, until we push the "go" button. We wanted the animation to start when the page loads or when the go button is clicked. A solution:

begin="0s;go.click"

This means start the animation either at zero seconds (i.e., when the page loads), or when the "go" button is clicked. This does the trick.

As long as we're concentrating on what is becoming a rather lengthy piece of code, let's work a bit further in this direction. Let's get two ovals following one another around the curve, and have them both start and stop when the buttons are clicked. But let us still have the second one start a second later than the first. Here's how to get the second animation to start a second after the first.

<ellipse cx="0" cy="0" rx="20" ry="12" fill="#aaa" stroke="#666" stroke-
width="2" opacity="0">
<set attributeName="opacity" to=".8" begin="One.begin+1" />
<animateMotion dur="2s" begin="One.begin+1" id="M"
end="stop.click" fill="freeze" rotate="auto"
repeatCount="indefinite" >
<mpath xlink:href="#curve"/>
</animateMotion>
</ellipse>

Just like the first, animation, we stop this one based on a click on "stop." But instead of trying to have the animation begin at 1s (which indeed delays its beginning to one second after the loading of the page) we set its onset to

begin="One.begin+1"

The onset is, hence, relative to the beginning of the first animation, but delayed one second. That way, whether the first animation begins through loading of the page, or through a mouse click, the second animation follows a second behind.

But how might we make it so that one button does both starting and stopping. It is quite easy in JavaScript, but that topic awaits a later chapter. How to use SMIL to do this is the question.

The first way we'll consider is to actually put two buttons in the same location (mimicking one button) and letting a click on either hide itself and reveal the other.

<text id="start" x="35%" y="35%" font-size="30" fill="Green">
<set attributeName="display" to="none" begin="start.click" />
<set attributeName="display" to="inline" begin="stop.click " />
Start
</text>
<text id="stop" x="35%" y="35%" font-size="30" fill="red" display="none">
<set attributeName="display" to="inline" begin="start.click " />
<set attributeName="display" to="none" begin="stop.click" />
Stop
</text>

Then it is simply a matter of affiliating the cessation of the animation with the stop button (when it is visible) and the animation's begin with the start button.

<animateMotion dur="3s" begin="start.click" end="stop.click" ... " >

Clearly, the key here is to make it look as though the state of the button is changing, when in fact the entire button is being replaced by another.

To actually have one button that serves as both a start and stop button, here is a solution that does the trick (though the activity is triggered by mousedown and mouseup events rather than successive clicks):

One ring to bind them

green 'go' button before start

red 'stop' button after start

Before button is pushedWhile button is depressed.
<ellipse rx="20" ry="12" fill="#aaa" stroke="#666" stroke-width="2" opacity="0">
 <set attributeName="opacity" to=".8" begin="One.begin" />
 <animateMotion id="One" dur="2s" end="Bset.end" fill="freeze" begin="Bset.begin"
rotate="auto" repeatCount="indefinite" >
<mpath xlink:href="#curve"/>
 </animateMotion>
</ellipse>
<g id="B">
 <rect x="250" y="200" height="30" width="75" fill="#8f8">
<set attributeName="fill" to="#f88" begin="B.mousedown" end="B.mouseup"/>
 </rect>
 <text x="260" y="220" font-size="20" >
<tref id="label" fill="green" xlink:href="#go">
 <set attributeName="fill" to="red" begin="B.mousedown" end="B.mouseup"/>
<set id="Bset" attributeName="xlink:href" to="#stop" begin="B.mousedown"
end="B.mouseup"/>
</tref>
 </text>
</g>
This example can be seen at http://srufaculty.sru.edu/david.dailey/svg/newstuff/SMIL7h.svg

The mouse events change not only the fill attributes of the <text> and the <rect>, begin and stop the animation of the <ellipse> but also change the text displayed on the button. This last result is accomplished through the use of a <tref>, a child of a <text> node that allows the text itself to be animated.

In the following chapter (under SMIL to JavaScript event passing), we will find that SMIL events can be used to trigger JavaScript functions, extending its capabilities even further.

In conclusion, SMIL is a very compact and efficient way of directing complex events with minimal script. It is accessible to script, and hence, compatible with it. It is a type of choreography that seems to be commanding the attention of influential people. It is quite likely that we will see more of it and allied technologies popping up here and there in web development during the foreseeable future.

SMIL to JavaScript event passing

In a number of instances we may wish to have scripting and SMIL animation combined in the same document. Sometimes, it may even prove fruitful to have both JavaScript and SMIL based animation in the same document. For example, if certain objects have relatively simple oscillating behavior while others respond dynamically to distances from edges or mouse actions, then we may want to intermingle our varieties of animation.

Certain methods exist for instigating JavaScript functions from SMIL events and from activating SMIL animations from JavaScript functions. We conclude this chapter with some explanation and illustration of two such methods that can handle most of our needs in this area. We may use onend (or onbegin) associated with the end of SMIL animation to trigger a JavaScript function. From JavaScript we may use Animation.beginElement() (or endElement()) to start a SMIL animation.

onend (onbegin)

Within a SMIL animation the attribute onend="doIt()" will cause the JavaScript function doIt() to be performed as soon as the animation ends. A simple usage:

function stuff(evt){
	O=evt.target.parentNode
	O.setAttribute("fill","red")
}
<ellipse fill="lightgreen" cx="40" cy="100" rx="22"
ry="14" stroke="#804" stroke-width="5">
<animate attributeName="cx" dur="3s"
onend="stuff(evt)" values="40;400;40"/>

Here, the ellipse moves back and forth across the screen once horizontally. After completing its mission, the function is activated. The function determines what triggered it (the animation object), finds the parent of that object (the ellipse) and resets the fill pattern of the parent. This particular example, though illustrative of using SMIL to activate JavaScript, could, as seen in the chapter on SMIL, be performed without JavaScript using the <set> element of SMIL.

A small note about the use of onend is in order. If we attempt to build an animation dynamically using document.createElementNS(xmlns,"animate"), then attempts to create the onend attribute for that object seem to fail in IE/ASV. It appears to be a bug in the browser.

Additional notes

Two other things worth mentioning in this context: onrepeat can be used to with a counter to allow a function to be triggered after an animation has been repeated a certain number of times. This could be useful in allowing the animation to keep going, but to nevertheless spawn a new activity before it has actually ended.

Also, one may use script to synthesize events such as clicks on SVG elements, hence triggering associated animations. I'm hoping that a reader may provide a useful example of such a use.

beginElement() (and endElement())

The beginElement() function in SVG/JavaScript is a method applied to an animation object (<animate>, <animateTransform>, <animateMotion>, etc.). It can be used by a script to activate a SMIL animation. A simple usage is shown here.

function start(id){
	D.getElementById(id).beginElement()
}
<ellipse fill="lightpink" onclick="start('A')" cx="40" cy="140" rx="22" ry="14">
 <animate id="A" attributeName="cx" dur="3s" begin="indefinite" values="40;400;40"/>
</ellipse>

Note that the begin attribute of the animate tag has a value of "indefinite." This is necessary to allow the script to be able to determine the time at which the animation begins.

Likewise if an animation tag A contains the attribute end="indefinite" then A.endElement() will succeed in terminating that animation.

Combining beginElement() together in a script with the use of the onend attribute of a SMIL animation can yield some worthwhile results. In the following, we allow objects (<g> tags) to move across the screen following SMIL animation. When the animation is complete (onend) we trigger a JavaScript function which rewrites the text inside the animated group (adding one to a counter inside each object). The JavaScript then restarts the animation, apparently seamlessly.

In the example below, we allow the moving objects to follow an oscillating Bézier curve, which is itself animated. The process of doing this animation with JavaScript rather than SMIL would be quite painful, hence strongly motivating this use of SMIL.

Allowing uniquely numbered ovals to march across a Bézier curve.
<svg xmlns="http://www.w3.org/2000/svg"
 xmlns:xlink="http://www.w3.org/1999/xlink"
 onload="startup(evt)">
<script><![CDATA[
xmlns="http://www.w3.org/2000/svg"
xlinkns="http://www.w3.org/1999/xlink"
var D=document; var count=3
The standard beginning with a shortcut name for document and a counter.
function startup(evt){
 B=D.getElementById("B")
 D.getElementById("A").beginElement()
 var t=2000
 window.setTimeout("B.beginElement()",t)
}
We start the animation 'A' and wait 2 seconds to start the animation 'B'.
function rebuild(evt){
	var Q=evt.target
	T=document.getElementById(Q.id+"T")
	T.firstChild.nodeValue=count++
	Q.beginElement();
}
Whenever either animation ends, we figure out which one it is. We then find the <text> tag inside its group and rewrite the text with the current value of the counter. We then restart that particular animation.
]]>
</script>
<rect x="0" y="0" height="100%"
 id="Canvas" width="100%" fill="#147"/>
<path id="P" d="M 0 200 C 100 0 200 400 300 200"
 fill="none" stroke="#804" stroke-width="3">
<animate attributeName="d" dur="1s"
 repeatCount="indefinite"
 values=" M -10 200 C 150 100 300 300 450 200;
 M -10 200 C 150 300 300 100 450 200;
 M -10 200 C 150 100 300 300 450 200"/>
</path>
A Bézier curve is defined as a path for the animation to follow with its <mpath>. The Bézier curve is itself animated so as to oscillate.
<g>
 <animateMotion dur="4s" id="A" rotate="auto"
 begin="indefinite" onend="rebuild(evt)">
 <mpath xlink:href="#P"/></animateMotion>
 <ellipse fill="yellow" cx="0" cy="0" rx="22"
 ry="14" stroke="#804" stroke-width="5"/>
 <text id="AT" x="-10" y="7" font-size="12pt">1</text>
</g>
<g opacity="0">
 <set attributeName="opacity" to="1" begin="B.begin" />
 <set attributeName="opacity" to="0" begin="B.end" />
 <animateMotion dur="3.3s" id="B" rotate="auto"
 begin="indefinite" onend="rebuild(evt)">
 <mpath xlink:href="#P"/>
 <ellipse fill="cyan" cx="0" cy="0" rx="22" ry="14"
 stroke="#804" stroke-width="5"/>
 <text id="BT" x="-10" y="7" font-size="12pt">2</text>
</g>
</svg>
Two groups are defined, each containing an ellipse, a text, and an animateMotion allowing the group to follow our oscillating path #P (as defined above). Whenever either animation reaches its end state, it triggers the rebuild function, which ultimately restarts the animation.

The second group has its opacity assigned and removed to avoid its phantom appearance in the upper left of the screen at those moments following the end of its animation but preceding its restart.

Illustration
two numbered ellipses at positions along a bezier curve
This example can be seen at http://srufaculty.sru.edu/david.dailey/svg/bezierovals.svg

pauseAnimations (and unpauseAnimations)

A JavaScript call to document.documentElement.pauseAnimations() will stop all SMIL animations within an SVG document, freezing objects at their current position. Likewise document.documentElement.unpauseAnimations() resumes those SMIL animations from whatever position they were paused. One technical note is that the Adobe SVG viewer in Internet Explorer sometimes seems to throw an uncaught exception causing the browser to crash occasionally when using these methods.

Mixing SMIL and scripted animation

This section (recommended by Erik Dahlstrom) needs still to be written. In the meantime, I point to a few examples of code that does this: