Fwd: [web-animations] Simplifying timing groups

Apologies in advance for the long email. This is a complex topic and I'm
not sure that discussion on a mailing list does it justice. In particular,
I think some wires have been crossed along the way:

(1) Nobody is seriously considering "arbitrary timing functions" on Timed
Items, certainly not for the first version of the specification. There is a
restricted set that comprises essentially the same timing functions
currently available in CSS.

(2) Timing groups are functionally very similar to iterations. To argue
against timing functions on Timing Groups logically implies that Timing
Groups should not be allowed to iterate either.

Here are the similarities:
* Both timing functions and iterations manipulate the local time of the
child animations
* Neither (the current set of) timing functions nor iterations can
introduce more than two time inversions per segment.
* Both timing functions and iterations can cause the start/end point and
child iteration points to repeat.
* Neither timing functions nor iterations are visible from the perspective
of the child when looking at local time.

(3) Limited time inversions are not difficult to understand or deal with,
and they produce some really nice effects (like smooth changes of
direction). As mentioned above, iterations already produce inversions, and
we deal with this in the model just fine. What is tricky is unbounded
inversions - such as might be produced by a spring timing function. We do
not intend to introduce timing functions with unbounded inversions to this
version of the specification.

(4) Contrary to Brian's statements, there are several animations APIs that
currently provide group timing primitives of some sort. They tend to be
very similar in scope and behavior to the set of timing functions we
currently allow on Timing Groups. What is key about the restrictions in
these APIs is not that they prevent inversions - they don't. Instead (with
the exception of GSAP, see below for more detail), they prevent *unbounded*

(5) Removing timing functions from Timing Groups is not a simplification.
On the contrary, it significantly complicates the model, because it takes
what was a unified concept that applied to both Timing Groups and
Animations (the Timed Item abstraction) and forces it to be split into
different abstractions for groups and animations.

Three important concepts for specification simplicity are:
(A) API/concept surface area. Removing this feature actually increases
surface area, as an additional interface or concept needs to be introduced
to deal with the difference between Timing Groups and Animations.
(B) Consistency. Removing this feature introduces an inconsistency to the
set of things that can be animated by Players.
(C) Ease of use. Removing this feature makes some things that seem like
they should be easy very hard to do. For a concrete example, without this
feature it's impossible in the general case to take a complex animation
(that might e.g. be generated programatically, or from a library of
effects) and apply a simple easing to it.

To the extent that this thread has discussed complexity, it is mostly
talking about implementation complexity. Is it not explicitly against the
principles of the W3C to choose a slightly simpler implementation at the
expense of users?


The real issue is that unbounded inversions in a timing function can lead
to an unbounded number of events, and make it difficult to place those
events in document time. This issue is entirely theoretical as we don't
have timing functions with unbounded inversions, and because we already
need to deal with inversions due to the presence of iterations.
Nevertheless, I think there might be some things we could potentially look
at to simplify the location of these inversions, without breaking the
model. If people are interested, I'll make some suggestions in a follow up
to this email.

I've included some more specific responses below.

On Thu, Aug 8, 2013 at 4:30 PM, Brian Birtles <bbirtles@mozilla.com> wrote:

> Hi Shane,
> Thanks for your response. I'll follow up below.
> (2013/08/08 13:32), Shane Stephens wrote:
>> On Thu, Aug 8, 2013 at 1:25 PM, Brian Birtles <bbirtles@mozilla.com
>> <mailto:bbirtles@mozilla.com>> wrote:
>>     i. It means you can't convert from an animation's local time to the
>>     corresponding document time since timing functions are not always
>>     invertible. This introduces all sorts of complexity. For example,
>>     for event handling to report both a local time and document time for
>>     an event we have introduced uneased timing[1] which is not
>>     well-liked and as an alternative we have considered some fairly
>>     arbitrary approximations.
>> I think this is a misrepresentation. Every observable animation start,
>> end and iteration change has an exact corresponding document time. These
>> times are in general derivable from the timing functions.
> I think it's reasonably complex.

I'm glad you no longer believe that it can't be done :)

> For example, in Gecko when we evaluate cubic bezier timing functions we
> approximate the curve with straight-line segments to find the t for a given
> x. If we go in reverse where a given y may have multiple t's, we can't rely
> on a table of fixed chord lengths since they may obscure some crossings of
> a given y. I think it could be challenging to define this in a way that is
> consistently implemented and performant if we allow arbitrary timing
> functions. We could probably do something reasonable for a restricted set
> of timing functions though.

You're talking about the impact of a specification decision on a specific
implementation, rather than focusing on keeping the model simple. For an
alternate data point, Blink uses recursive bezier splitting to find points
on and lengths of beziers. This approach does not suffer from the
shortcomings that a table of fixed chord lengths does.

Also: we already have a very restricted set of timing functions. Nobody is
suggesting we allow 'arbitrary timing functions', whatever they are
supposed to be. The inversions in the very simple set we have are easy to
characterize and will not impact performance (even with a table of fixed
chord lengths).

Another example of complexity is stepped timing functions where we have to
> be careful how we define which x to return for a given y since a single
> input may map to a whole range of outputs. I think the 'correct' answer
> will depend on a number of factors, possibly the type of event to be
> dispatched or the playback direction.

Yes, this is something we'd need to think through.

> So I think, while probably solveable, it's complex enough to defer to a
> later stage.

We shouldn't defer features because one implementation is forced to modify
the way it approximates beziers. This is not the appropriate definition of
complexity to use when making these decisions.

>      It also introduces some as-yet unresolved questions such, "If a
>>     timing function on a group causes a child to oscillate between being
>>     in and out of the active phase repeatedly, perhaps even a dozen
>>     times in 100ms for a spring function, do we dispatch events for
>>     every one of those transitions?" (And if we have real spring
>>     functions the number of crossings would depend on how you evaluate
>>     the function and could be huge.)
>> If we have short iteration durations and lots of iterations we have the
>> same problem.
> The issue is really the question about whether we should report all those
> starts and ends at all. Sometimes its useful to have them, sometimes its
> not.
> I can give a concrete example of this if needed but I think it's simpler
> if we design the model so the question never arises in the first place.

Are you suggesting that neither iterations not timing functions produce
intermediate start and end events? Whatever one does, the other should do.

>      iii. It complicates implementation since this feature makes it
>>     difficult to decompose timing hierarchies into simple animations
>>     that could be passed off to another process or even another API.
>>     Without this feature, any timing hierarchy could potentially be
>>     flattened down to a series of very simple sub-animations. (For
>>     example, an iteration count of a group would be represented by
>>     producing several sub-animations, one for each iteration. A
>>     direction setting on a group could be represented by flipping the
>>     playback rate on some of those sub-animations etc. On the other
>>     hand, splitting a timing function on a parent into pieces, then
>>     combining the appropriate piece with any timing functions already
>>     applied to the child's sub-animations, is far more complex.)
>> This is false. It is entirely possible to decompose timing hierarchies
>> with timing functions on groups. Given that timing functions need to be
>> represented in the simple sub-animations anyway, it is not much of a
>> stretch at all to allow a stacked timing function to be used here.
> The claim was it is complex, not impossible.

Stating that "without this feature, any timing hierarchy could potentially
be flattened.." strongly implies that with the feature it could not.

> I still think it's a reasonable bit of work. Even if you don't split the
> timing functions but just inherit the whole list of functions along with
> appropriate offsets you still have to effectively apply playback rate,
> direction changes etc. in *in between* the functions in order to produce
> the same result. You could probably achieve that by modifying the offsets
> into the curves but it's starting to get complicated. (Then if you're
> interfacing onto any system that doesn't support a stack of timing
> functions you have to composite the functions together which is probably
> only possible by approximation.)

I am very uncomfortable reducing the viability of the API simply because of
a theoretical system which you need to interface with but that you aren't
allowed to modify. In practice there are a number of approaches one could
take to support animations off-main-thread, including splitting animations
on animation boundaries, splitting animations on inversion boundaries
(which is your proposal for iterations anyway), and simply supporting a
subset of the animations model on the secondary thread.

     * GSAP, which has an impressive feature set, has no 'ease' property
>     on TimelineLite / TimelineMax.
> GSAP allows you to tween both the currentTime and the timeScale on
> TimelineLite and TimelineMax, which is the same thing as a timing
> function.

> timeScale() is just the playback rate. progress(), time() and totalTime()
likewise correspond to setting the
> playback position.

> You can repeatedly call these methods to emulate a timing function but
that's the not same as having timing
> function on a group. You can do that in Web Animations too without having
timing functions on groups.

Actually, GSAP allows you to *tween* these values, which means you can
define an automatically running animation with a timing function applied to
it that modifies these values, which is functionally at least as powerful
as directly applying a timing function to the Timeline.

It's not even like this is an obscure feature: (a) it's part of
TimelineLite, and (b) the fact that you can do it is explicitly called out
in the documentation preamble for this class.

>      * WPF which has, by far, the most complicated animation API I have
>>     ever seen, only has easing functions on animations.
>> WPF provides at least the ability to accelerate and decelerate
>> Timelines, from which both Animations and TimelineGroups inherit.
> That's not arbitrary timing functions. That's the same as SMIL3.

...and Web Animations!

A combination of acceleration, deceleration, and auto-reversing provides
almost as much power as the cubic-bezier timing function we provide. There
are differences, but they are minor. Every other timing function we provide
has no inversions.

>      * SMIL3, as discussed later, which is infamous for its complexity,
>>     does not allow arbitrary timing functions on groups.
>> And yet SMIL3 does supply auto-reversing, which is not invertible.
> If you model it as additional iterations with direction set, then it's
> invertible in the same way that iterations are invertible--that is, you
> have to know the current iteration but that's generally an easy requirement
> to satisfy.

It is my strong contention that this is equally true of every timing
function we currently support in Web Animations.

> And SMIL3 still doesn't support arbitrary timing functions on groups.

What exactly are the arbitrary timing functions we support? Do you mean
parameterized timing functions like cubic-bezier? If so, then SMIL3 and WPF
support something that is functionally very similar.

>      This begs the question, do we really need this?
>> If you choose your features based on what everybody else does, then
>> given that 3/4 of the animations engines you supplied provide it, the
>> answer is clearly yes. However, I'd prefer to analyse whether this
>> feature is *useful* and proceed from there.
> None of those APIs allow arbitrary timing functions on groups.

... just like us.

Much more importantly, all 3 APIs provide a time-varying measure of control
over the flow of time in groups. If we adopt your suggestion then we won't.

>      * In general, provided the 'current iteration' of each parent is
>>     known, it is possible to convert from an animation's local time to
>>     document time which may be useful in a number of contexts (it may,
>>     for example, be something we expose via the API in future).
>> This is always possible. Note that sometimes multiple document times can
>> produce the same local time. Note further that this is already true in
>> the presence of iterations, without taking timing functions into account.
> That's why I included the condition, "provided the 'current iteration' of
> each parent is known."

Again, I strongly contend that a similar condition is all that is required
to fix the apparent problems with timing problems.

>      * There is no need for special overflow fill handling.
>> We already perform special overflow fill handling on Animations. Why are
>> TimingGroups different?
> It's a different mechanism. The handling for animations doesn't require
> any extra state to be passed around. In fact, if we were to remove
> arbitrary timing functions from groups this behaviour could become a
> property of the animations model.

There is extra state required - you now need to be able to specify what
happens out-of-bounds on an Animation. We had considerable discussion about
this in Tokyo. Adding a similar feature to Timed Items is a natural

>      * Media references are not required to support timing functions
>>     initially. If timing functions are useful there, we can determine
>>     the sort of timing functions that are readily implementable and
>>     restrict the set of timing functions permitted accordingly. For
>>     example, overshoot doesn't make much sense for media. Likewise, some
>>     features of chained timing functions probably don't make much sense
>>     for anything other than animations (like the keyframe alignment
>>     feature).
>> This is an orthogonal issue to your proposal.
> It's not orthogonal because if we have timing functions on groups we
> *must* be able to support easing media since we'll get inherited time that
> is eased, possibly multiple times. It's not clear that that's a reasonable
> requirement for all implementations. I'd be surprised if all platforms
> provide that sort of feature.

All of our old drafts and meeting minutes specify that media doesn't (have
to) ease, and merely respects the overall boundaries of the time slice
allocated to it. This is even implemented in the polyfill. It works just

Additionally, media integration isn't even part of L1 any more.

By removing timing functions from groups we can restrict the easing
> available on media to that which seems useful and implementable (which may
> be none initially).

That sentence is equally true if you remove the part about timing functions

>  It's probably clear by now that I don't agree with your reasoning. Can
>> we come at this question from a use-case based analysis instead?
>> For a start, timing functions on groups make it easy to:
>> (1) keep the timing of a number of animations in sync (by applying the
>> function to a ParGroup)
>> (2) smear a timing function across a composite effect (for example,
>> applying an ease-in across a SeqGroup containing multiple child
>> animations)
>> (3) keeping consistent timing across a complex hierarchy of ParGroups
>> and SeqGroups
>> Without timing functions on groups, how would you provide this
>> functionality?
> There are definitely things that you can't do as easily without arbitrary
> timing functions on groups but I can't find a single animation engine
> anywhere that supports this functionality which suggests that for all
> current concrete use cases authors have managed fine without it.

We are not talking about arbitrary timing functions, we are talking about a
carefully controlled set. And in fact three of the four animations APIs
discussed support controlled easing on groups.

> Rik points out that Flash doesn't support this and yet somehow Flash
> authors have managed pretty well.

Rik's suggestion that Timing Groups are like Scenes leads me to believe
that Flash doesn't have a concept matching Timing Groups at all, so this is
hardly relevant.

Note that in Flash you can group a set of elements together then animate
them as a single object. When you do so, you can control the timing of the
group as a single entity. With your proposed change, this might be
achievable in limited circumstances (e.g. when all elements are part of a
well-defined DOM subtree and you are only animating the transform), but it
certainly isn't generally achievable.

 Last year in October when we demoed Web Animations we actually used this
> feature to ease both a door and its creaking sound. There are two things to
> note about this use case:
> a) It could easily be done by applying the timing function to the two
> child animations independently.
> b) It could *also* be done by allowing a restricted set of timing
> functions on groups.

I have implemented smooth animation along a path that responds to
keystrokes by changing the destination along that path. It is essentially
impossible without timing functions on groups. Timing functions with at
least one inversion are required in order to handle the case of a change of

I have also implemented and demoed some of the layout transitions ideas
that the CSSWG has talked about. Getting smooth layout transitions with
keyframes is again not impossible without timing functions on groups, but
it's much harder as timing fragments would need to be computed manually,
and effects split, by the user, at arbitrary points.

If you want to talk about complexity, supporting these kinds of behaviors
without timing functions on groups is *very* complex.

> I'm open to the possibility of adding restricted easing behaviour on
> groups like SMIL3 offers. I'd prefer to add that later but would be ok with
> adding it sooner if there is sufficient demand. I think that would address
> a number of the uses you outlined above without introducing many of the
> undesirable qualities of allowing arbitrary timing functions.

OK, let's add the restricted set of timing functions that we already have.
Nobody is asking for arbitrary timing functions.

> I just feel that introducing features that (a) introduce considerable
> complexity, (b) are provided by no other animation platform we've found
> yet, and (c) for which we have yet to find concrete use cases that can't be
> solved by other means, seems like over-engineering for a first version of a
> spec. I think we can add this later if needed.

(a) no, the feature provides limited additional implementation complexity
but actually simplifies the model
(b) no, analogues to the feature are provided by at least 3 other animation
platforms. Even Flash provides a limited version
(c) no, I have presented two concrete use cases above and I suspect if you
look at existing animation content you'll notice a lot more

    -Shane Stephens

> Best regards,
> Brian

Received on Thursday, 8 August 2013 12:33:10 UTC