Server Notice:


Public Pad Latest text of pad JNHdgLfAYm Saved Dec 18, 2012

Meeting 37:
Web animations minutes, 17 / 18 December 2012
Present: Dmitry Baranovskiy, Steve Block, Shane Stephens, Douglas Stockwell, Brian Birtles
1. Status update
2. Keyframe revision
3. Comparing with QML
4. Comparing with Core Animation
5. Feedback from Rob Arnold
6. Null animation function
7. Interaction with script
8. Time sources
9. Fixing Anim.duration vs Anim.timing.duration
10. The chopping block
11. GroupedAnimationEffect
12. Override stylesheets
13. Animation stack & Keyframe animations
14. Default fill mode - feedback suggests "both" instead of "forward"
15. Grouping - I wrote a document for this and all and it's missing :(
* Writing up events stuff
* travelling, polyfill hacking, talking to interested Googlers about Web Animations
I've revised the keyframes interfaces with the following changes:
* Add ctors
* Redone the way we interpret values to the ctor since sequence<DOMString> and sequence<KeyframesDictionary> are not distinguishable
* Add a distribute method to space all the keyframes out evenly
This distribute method in particular hopefully makes it easier to add/remove keyframes whilst keeping them equally spaced.
(I had another proposal here where we allowed null offsets that meant 'space me out evenly' but it was complex so I dropped it.)
Shane: LGTM.
Some observations:
* Many features are similar, even the parallel and sequence animations are similar. That's good news.
* SpringAnimation element
   -- could be realised with the proposed SmoothTimingFunc and then add some syntactic sugar to make bouncing animations easier
   -- I've asked Pomax (author of for some help with SmoothTimingFunc.
* Includes a PauseAnimation -- temporal equivalent of a spacer gif
   -- worth adding?
   -- Shane: I have an alternative approach that is simpler than a new primitive (and also useful for the trigger stuff :))
* Terminology
   -- They don't have groups. Should we just call ParAnimGroup, ParAnim?
   -- Shane: I think distinguishing between groups and root animations is useful.
*  Uses ms (integer) for duration. window.setTimeout etc. use ms as well.  Perhaps ms are more common on the web platform? Should we use ms  instead? And if so, is an integer too imprecise?
Shane: No! Seconds are really easy to use and make code look good.
Brian: some data gathering...
window.setTimeout - long - milliseconds
window.setInterval - long - milliseconds
XMLHttpRequest.timeout - unsigned long - milliseconds
HTMLMediaElement.duration - unrestricted double - seconds
Date.getTime - milliseconds
PerformanceTiming.* - milliseconds
PerformanceNavigationTiming.* - DOMHighResTimeStamp (double) - milliseconds
CSS Animation/TransitionEvent.elapsedTime - float - seconds
Agreed to stick with seconds (double) particular to line up with HTMLMediaElement and CSS animation/transition events and also because longs do not offer enough precision.
I've had a bit of a look over Core Animation to see how similar we are:
At a glance it looks like we're pretty close since it's based on SMIL and CSS Transitions/Animations is based on Core Animation.
Some naming suggestions:
  • Core Animation uses beginTime and timeOffset -- it has the same double-offset!!!! We are not crazy!
  • Some renaming possibilities here:
  • s/startTime/beginTime/
  • startTime: 'start' just seems friendlier, matches HTML5 text tracks
  • beginTime: matches CoreAnimation, close to SVG (begin attr)
  • NOTE: HTMLMediaElement has startDate (was startTimeOffset/startTime/initialTime) which is the initial offset into the resource
  • s/startDelay/beginOffset/
  • startDelay: close to CSS (animation-delay)
  • beginDelay: relationship to beginTime is obvious (assuming we go with that), close to CSS (animation-delay)
  • beginOffset: relationship to beginTime is obvious (assuming we go with that), close to Core Animation (timeOffset)
  • NOTE: HTMLMediaElement has startDate (was startTimeOffset/startTime/initialTime) which is the offset into the resource
Agreed to stick with startDelay and startTime
Rationale: delay is a lot clearer than offset. "beginDelay" sounds silly. Therefore "startDelay". Therefore "startTime".
  • Core Animation uses fillMode where we have fill
  • fillMode: matches enum name (FillMode), Core Animation (fillMode), CSS (animation-fill-mode)
  • fill: matches SVG, shorter
  • 2012-11-30[Brian]: Renamed fill to fillMode
  • Core Animation uses timingFunction where we have timingFunc (see above where I suggest we go with timingFunction)
  • 2012-11-29[Brian]: Renamed
  • This was discussed informally at the time and included in the minutes from 2012-12-11.
  • Core Animation uses speed where we have playbackRate
  • the reason we went with playbackRate was to match HTMLMediaElement. I think that wins.
  • Our Timing object is represented in CoreAnimation by a MediaTiming interface
Some general sense that an interface may be preferable here. Will consider when we discuss how to refactor defaults and so on.
  • Core Animation uses parent time, active local time (=our item time), basic local time (=our iteration time)
  • There's  no animation time but even in our model, animation time is really  an  implementation detail. In terms of the API it's irrelevant. Maybe we  should try to hide it a bit more. The time spaces diagram appears quite  complicated.
  • Should we rename?
  • iteration time -> basic time (SMIL calls this simple time)
  • item time -> active time (SMIL calls this active time)
iteration time is more descriptive than basic time
item time is not great (Shane: but still better than active time! Doug: you lie!)
How about local time?
We'll give local time a try
decided that the walls of local time should be painted red.
Shane, Steve: If we have a time window then we could have timeWindow.activeTime instead of animation time
rename animation interval to active interval.
I  think it would be helpful to have examples to use examples for  individual features (ex: seeking; I havent seen a need for that yet)  and indicate what areas the API is intended to be used for (games, rich  content, ui?)
Yes. We should put this in a different document (a primer). We will also increase the number of examples in the specification, but these will be for the purpose of illuminating how the specification works rather than justifying the decisions made in the specification.
I  am sadly quite well aware that CSSOM for Transitions/Animations is  painful so having a good API there which addresses mutation (O(n²) to  remove all keyframes of an animation in WebKit) and triggering (no  awkward forced synchronous style calc) would be helpful.
This ties into Shane's desire to expose web animations objects as CSS/SVG manipulators.
"One  thing that I do think is important but not addressed in the spec is  concurrency (I assume that this is what you meant by hardware  accelerated animations). In particular, asynchronous animations are  highly useful in covering up main thread lag due to events or  implementations beyond the author's control. This is key on mobile where  performance expectations are high due to a direct manipulation UI but  processing power is weak. As a motivating example, consider trying to  implement a feed of mixed content (say, Facebook's newsfeed) with native  quality scrolling. Things like garbage collection, image decoding,  style/layout from new content being inserted and time mysteriously  unaccounted for (thanks Chrome!) disrupt the framerate  during/transitioning into fling scrolling (which can use CSS animations  for better physics / dependable scroll position events). The current and  proposed APIs seem to assume some sort of synchronous execution between  the JS and on screen animation; for this reason I like asynchronous  events so that the animation thread isn't stalled by content."
  • I  (Brian) followed this up and Rob provided the following clarification:  "I think there are some implicit assumptions in the API such as the  relation between the start of an animation and when you tell it to  start; with asynchronous animations, you'll need to account for a delay  (due to vsync, IPC overhead, etc...) and the page may want to take that  into account when computing its UI."
Some discussion on synchronicity of events and the possible use of observers.
> Brian to continue speccing events without observers.
> Shane to show events model (once specced) to observers guys and see what they think.
This could also be about the delay between calling start() and when the animation first updates. This would cause a jump.
We might add a feature in a future version for start this animation asap but whatever you do, start from the beginning
  base it off a trigger?
I  do like that you can compute the animation in JS with your own internal  structure and then have the browser query that instead of shoving it  into the CSSOM (creating a lot of garbage via string allocation).
  • I  asked, I just want to check I've understood this part. Are you  referring to CustomAnimFunc? If that's the case, then the user-supplied  function just applies the effect itself. It doesn't return anything to  the browser.
  • To  which Rob responded: Oh, I thought that it was to allow JS to specify  the animation curve so that the browser can asynchronously render it.  This is an example of where I mean that the spec does not seem to permit  an asynchronous (multithreaded) implementation.
Oh,  and if we could get instrumentation on how well the animation was played back (frame level timing would be sufficient) then that would really help our testing I suspect.
  • Rob  later helped with some ideas about how to get this data when animations are offloaded to the GPU, I suspect that it's not too tricky to get the data; exposing it to script may be hard. There is a GL extension (even available on some mobile devices) called GL_timer_query which lets you time the GPU pipeline. DirectX has had a similar API for a while.
Maybe a future version but some concern about all vendors being willing to expose this information
Does it make sense to allow Animation.animationFunction to be null-able?
The reason this comes up is that if you call
   new Animation(elem, { '-moz-transform': 'translate(-60)' });
This calls
   AnimationFunction.createFromProperties({'moz-transform': 'translate(-60)'});
On a UA that doesn't support '-moz-transform' the algorithm there will   mean createFromProperties returns null. I think this is correct   behaviour and there's an example in the spec of where the null  behaviour  can be useful.
But then, what is 'new Animation' supposed to do?
a) Just set animationFunction to null?
b) Throw an exception saying its bad input?
c) Make some sort of generic function to fill in its place?
I  don't think (c) is really great since I can't think of a suitable  function for that unless you make a KeyframesAnimationFunction with a  blank property and no frames?
(b)  warns you about the error but it will commonly mean that for a given   browser that the author hasn't tested the content with that doesn't   support said property, everything grinds to a halt. Whereas with (a) it   continues, it just does nothing.
The   downside of (a) is that there's no visible warning and every time you   access Animation.animationFunction you need to check if it's null or   not. That said, Animation.animationFunction is already of type   (AnimationFunction or CustomAnimationFunction) so you probably should  be  checking the type there anyway.
So my vote is (a) unless someone can suggest a good function for (c).
Note  that making an animation function with '-moz-transform' as the  property  EVEN on UAs that don't support it isn't an option due to the  way we process that property bag in order to avoid indeterminancy (it's  complicated).
Shane: I'm totally down with (a). It's the minimum specification effort approach :-D
I think we need to clearly specify three things regarding liveness and scripting:
  • If I change any of the values (e.g. the duration), do all the other timing values immediately update? (e.g. end time)
Brian: suggest yes
Shane: polyfill does
  • Does  the target property(ies) update? e.g. does the computed style of the  height I'm animating instantly update? (i.e. do we do a synchronous  sample in effect) or does it lazily update? (i.e. if I query it I'll get  the updated value but I won't see any change in rendering otherwise)
Brian: suggest no update until current script block completes
Shane: "until the script block finishes". Never mind, this is good enough.
  • In  a given script block is it possible for a clock tick to occur? (i.e.  can the value I get back for the currentTime of the global clock at the  start of a script execution block differ from the one I get back at the  end of the script execution block)
Brian: suggest no
Shane: there's a specific hack in the polyfill to implement no here :-)
Shane: ... so I think this is all LGTM.
Awaiting Shane's description of Timeline Time source transformations
Shane: Sorry, still no progress here :(
9. FIXING Anim.duration VS Anim.timing.duration
I really really don't like the fact that we have:
  • Anim.duration -- read-only, calculated iteration duration
  • Anim.timing.duration -- read/write, nullable, specified iteration duration
Authors are going to trip up on this and curse, "Who wrote this crazy spec!?!"
It would be ok if we had this pattern throughout the API and then authors could learn it once and deal with it. But we don't.
Also, we have the following situation:
  • As  described in the previous point, in order to align with  HTMLMediaElement, we possibly want a playbackRate that is stative and  one that's not? And possibly a defaultPlaybackRate?
  • For  reversing we currently change the playbackRate and fillMode. This   is     not great since it means (with current template behaviour) you   can't   reverse an animation without breaking the link to the template.
  • Templates     aside, it seems like you should be able to reverse an animation    without  changing its serialised state. i.e. it should be a runtime    control.   This might be solved by adding a locallyReversed member as     proposed   above, but it's somewhat confusing to have three  orthogonal   attributes   controlling direction (playbackRate,  direction,   locallyReversed).
Some possibilities:
  • A) Rename Anim.duration to Anim.calculatedDuration
  • Problem: breaks consistency with HTMLMediaElement
  • B) Follow HTML5's naming scheme here, i.e. collapse the Timing member into TimedItem itself so you'd have
  • Anim.duration
  • Anim.defaultDuration
  • Anim.playbackRate
  • Anim.defaultPlaybackRate
  • Anim.fillMode
  • Anim.defaultFillMode
  • Here the 'default' versions are the inputs
  • The non-default versions are calculated values
  • Setting the non-default versions overrides the default ones
  • Setting the non-default version to null reverts to the calculated value
  • When serialising, you'd typically serialise just the default values. The others should be considered run-time controls
  • Make 'duration' writeable
  • Problem: interface bloat (heaps of members in TimedItem)
  • Problem:     not quite the same as HTMLMediaElement. If you set   defaultPlaybackRate   in HTMLMediateElement it has no immediate effect   but here it would   (unless playbackRate was overriden)
  • Problem:     Using 'null' to reset stuff is a little hacky? There's also a little     bit of magic involved since you can't tell just from inspecting the     object whether the non-default values are overridden or are  calculated    from the default values (unless of course the values  differ)
  • C) Similar to above, but basically rename Anim.timing to Anim.default giving
  • Anim.duration
  • Anim.default.duration
  • Anim.playbackRate
  • Anim.default.playbackRate
  • etc.
  • Basically     copy those members from the Timing interface that could be useful to     override on a run-time basis to the TimedItem interface
  • As above, make Anim.duration writeable
  • Problem: Same problems with using 'null' to reset stuff
  • 'initial' might be better than 'default'?
  • D) Drop the defaults altogether and put everything directly onto the TimedItem interface. e.g.
  • Anim.duration
  • Anim.playbackRate
  • Anim.fillMode
  • Initially the values all reflect the computed values but can be overridden
  • Setting a value to null reverts to the computed value
  • i.e.     the initial inputs are magically stored away somewhere. It's   basically   the same as the previous suggestion but with 'default'   hidden
  • Problem: lots of magic, not really possible to serialize the animation apart from its runtime state
  • E) Just make Anim.duration writeable
  • Then authors don't really have to worry whether they set Anim.duration or Anim.timing.duration as long as they're consistent
  • Problem: doesn't help with the other cases
  • Problem:     not great. If a library starts setting Anim.duration while the  author    is in the habit of setting Anim.timing.duration they might be   surprised   that all their changes get clobbered.
  • F) Add accessors for stuff
  • Anim.duration
  • Anim.getInitialValue('duration')
  • Anim.revertValue('duration')
  • Problem: slower, more string manipulation
Brian: Lean towards D or F or maybe C.
Shane:     D and F seem strictly worse than what we have now. C is OK - I don't     find using null to clear hacky (but should it be null OR undefined?).
Dmitry: I find using null or undefined to clear very “unjavascripty”.
It's worth considering this in light of templates (assuming we keep them).
  • We already decided to revisit how templates work regarding liveness.
  • We agreed it's preferable, if possible, to make properties individually overridable.
  • One difficulty however is how represent the 'inherited' and 'overriden' values in the API
  • We could, potentially, treat them in a similar way.
  • e.g. (1), if we go with C above
  • Anim.duration is calculated from Anim.default.duration is calculated from Anim.template.timing.duration.
  • Anim.duration -> Anim.default.duration -> Anim.template.timing.duration
  • Setting Anim.default.duration overrides the template but setting it to null reverts to the template value
  • e.g. (2), if we go with D above
  • Anim.duration     is calculated from the default duration passed in (even though this     isn't publicly visible) and it is calculated from       Anim.template.timing.duration
  • Setting     Anim.duration overrides everything. Setting it to null reverts to    the    template value. No way to override Anim.duration in a way that    is    serialized unless we simply serialize the runtime state.
  • In the case of F, not much changes actually.
Update:     I'm thinking of making TimedItem not include all this information  and    just putting in on Animation, ParGroup and SeqGroup. This is  probably    cleaner from an architectural point of view (i.e. just have a  concept   of  things that occur at a given time and then extend that to  things   that  interpolate over that time), makes more sense for  audio/video   items and  media controllers, and isolates this  functionality a bit  more  nicely.
Shane:     Unfortunately it also reduces the similarity of Animations to     Par/SeqGroups which is currently a nice feature of the model.
TimedItem is just
    currentTime (possibly inherited from TimeSource/Timeline?)
This opens up two broad possibilities:
  • Make an interface/interfaces to encapsulate all this:
  • e.g. TimingControl?
  • Includes everything below
  • Note that Core Animation includes a MediaTiming which basically includes all this
  • e.g. Several interfaces: RepeatableTiming, ScalableTiming
  • SpeedControl??
  • playbackRate
  • changePlaybackRate()
  • PauseControl
  • paused
  • locallyPaused
  • pause()
  • play()
  • timeDrift (Need in TimedItem?)
  • RepeatControl
  • iterationCount
  • iterationStart
  • iterationTime
  • currentIteration (read-only)
  • FillControl
  • fillMode
  • DirectionControl
  • direction
  • reverse()
  • ScaleControl
  • timingFunction
  • In light of the questions above about Animation.timing.duration vs Animation.duration here we would
  • Remove the Timing interface
  • Keep TimingDictionary and apply the members directly
  • Basically do D initially (but maybe without the 'null' behaviour?) and add F in future if it's needed.
  • We could probably include SpeedControl.playbackRate & SpeedControl.defaultPlaybackRate
  • For duration I think we'd just have the one on TimedItem. It always reports its calculated value but you can override it.
  • Later     if we really need the ability to tell if it has been overridden or     reset it to the default value we'll add extra resetToDef
  • Make a member: AnimationTiming
  • Basically, everything hangs off
I definitely prefer the interface approach above, but would like to reduce the number of interfaces somewhat.
Let's see:
Video: SpeedControl, PauseControl, SimpleRepeatControl?, FillControl?
ParGroup: All of them
SeqGroup: All of them
MediaController: SpeedControl, PauseControl
Animation: All of them
A simple property set: None (FillControl assumed)
  -- this could be represented as a property applied for a range of time where the end time is infinite
A property applied for a range of time: None (FillControl assumed)
* Does SpeedControl imply DirectionControl? So should we just conflate them?
* Do we need SimpleRepeatControl? Or just add loop directly to those
* Does video need FillControl? Probably not
Combining SpeedControl and DirectionControl we have:
  • SpeedControl
  • PauseControl
  • RepeatControl
  • FillControl
  • ScaleControl
Five items. Maybe that's ok? It breaks up the spec and allows us to address how each part works bit by bit?
Or TimingControl
   Includes SpeedControl, PauseControl, startDelay
   FillControl (Not sure about this one. Just put it in TimingControl and ignore it for MediaControllers?)
Shane:  My strong sense is that this is going in the wrong direction. It's     increasing the number of IDL interfaces we define (and hence   increasing   the complexity of the specification), while removing   functionality. I suggest we go with option A or C and keep what feels   from an implementation perspective a very solid set of relationships.
> Brian to come up with a different proposal and remove this one from future minutes.
What can we drop from v1 to make things simpler and faster to ship?
My (Brian's) suggestions:
  • Templates
  • Lots  of complexity here. I think if we make animations easily cloneable    and  add convenience methods that take a DOMNodeList/sequence and    generate  the necessary animations we'd get 80% of the convenience.    For controlling multiple running animations we have groups that cover    some of the cases.
  • The  main argument for keeping templates that I can see is because       implementations are going to have to implement something like    templates  anyway in order to manage changes to animations generated by   markup so  why not formalise this behaviour from the start?
  • Shane:  I have some feedback from web developers too - the "immediate" effect  of Animation objects is strange to them but the ability to create    Templated animations ameliorates this significantly.
  • Brian: Is this about animations playing as soon as you create them?
Doug, Steve and Shane had a bit of a discussion on what to do with templates: 
Brian: Looks good. I think we need to work out how defaults should work first, then consider how template-like behaviour would fit in.
  • Merging
  • It's  great but I'm not sure if it's necessary for currently pressing  use      cases. We should still work on it to make sure when we do go to  add   it    the architecture doesn't explode.
  • Shane:  We've agreed it's necessary to make "to" animations clean. I'm still   in  favor of including it because it doesn't add very much complexity   at  all, and the merge behavior still needs to be specified regardless.
Shane to add diagrams.
Need to talk about grouping and parameterized merge in more detail in a future meeting.
  • Sharing of Timing objects
  • Really, how often will this be done?
  • Shane:  if we don't allow timing objects to be shared, then *that* is when we    have to start specifying stuff. They share naturally.
  • Brian: In section 7 I'm proposing removing them for other reasons.
  • Shane: I think section 7 has moved to section 9. Section 9 is crazytown; but regardless of what it is called, there *will be* an object or objects with default values that are accessible from script. It is more natural to allow these to be shared than not. Having said that, the templating stuff that we were looking at before might in fact imply these should not be sharable anyway.
  • TimingFuncCallback
  • This  adds a lot of complexity (just see the massive TODO there) and if we  have SmoothTimingFunc then I think we can cover most cases.
  • 2012-11-29[Brian]: I already removed this feature
  • Shane: I support this
  • TimedItem.animDuration
  • Easy to calculate, but necessary?
  • Shane: probably not. Also tiny. Defer removal unless we have to?
  • TimedItem.locallyPaused
  • Again,  simple, but necessary? Would pause(), play() and unpause() be enough?  (We currently don't have unpause but MediaControllers do. The  difference  is basically that it wouldn't auto-rewind.)
  • Shane: this falls out *SO CLEANLY* in code! I'd be reluctant to remove.
  • Brian: It doesn't mean you remove the code, just that you hide it from view.
  • Maybe mark at risk?
11. GroupedAnimationEffect
Genuine question, is it needed or should it be merged with KeyframeAnimationEffect?
What  is it used for that couldn't be achieved by making  KeyframeAnimationEffect support targetting multiple properties like you  can do in CSS?
Of  course, you could re-use timing to apply to a PathAnimationEffect and  KeyframeAnimationEffect, but do you do that often enough that you  couldn't just set up a group for that?
I  guess doing so would be contingent on being able to represent that  arrangement in SVG? Or maybe not since changes to Animations don't  reflect back to SVG.
Shane: I'm not fussed either way. We do have a request to support a group constructor that has keyframe offset as the primary key (rather than property) - it would be natural to put this on a group; but less so on a keyframes object directly I think.
Shane: Also it's important to keep in mind that groups were initially included for the purpose of grouping operations inside the compositor. I've got some proposals that would stop this from happening - we should at least review those before committing to removing groups.
Also, I'd like to make KeyframeAnimationEffect a bit more friendly to use by allowing operations such as:
   effect.frames.add(0.3, 'left', '100px');
   // Overwrites the frame at 0.3 if there is one
Currently, you need to do:
  effect.frames.add({ property: 'left', offset: 0.3, value: '100px' });
It might even be worth supporting:
  var frame = effect.frames['0.3'];
Returns the last frame with offset 0.3 if there is one. If there is none, does the interpolation and returns a new frame?
Shane: Is this sort of thing something we should defer until v2? It would sit cleanly on top of a more primitive API (and could be polyfilled very nicely in the short term).
> Leave this for now (but make an annotation that these may be possible future improvements)
How are we going here? What was the issue even?
Shane: The issue is that Animations and Transitions in CSS inject into different places in the stylesheet stack. It doesn't actually matter too much though - time sources basically make this problem go away because we can have a time source per injection point.
> We're looking good at the moment
(Continuing actions from a previous meeting regarding merging and behaviour where no keyframe is defined)
what should [{offset: 0.5, left: 100px}] do?
( polyfill currently does this: [{offset: 0, left: 0px}, {offset: 0.5, left: 100px}, {offset: 1, left: 0px}] )
( CSS animations snapshot document state and fill in missing values with that )
> We will revisit this later
Feedback suggests "both" instead of "forward"
> Mark as an issue in the spec
Next meeting: Mon Dec 7, 17:30 PST / Tues 8 Jan 12:30 AEDST  / Tues 8 Jan 10:30 JST @