Jump to content

atomictag last won the day on June 14 2013

atomictag had the most liked content!


  • Posts

  • Joined

  • Last visited

  • Days Won


Posts posted by atomictag

  1. Anyway, would you mind simply setting "z" to something small, like 0.1 and see if that helps you element(s) animate more smoothly on android? 



    I've tried all possible combinations of gpu-triggers (in CSS classes, from TweenMax.set, inside the Tween options...) - like those you suggest and other tricks (setting perspective, hiding backface visibility etc.). In my setup clearly the gpu is kicking in (the difference would otherwise be huge). Plain CSS3 transitions are noticeably smoother in some cases (esp. visible in relatively long-range translations of large elements - like in the above example).


    From what I can tell there is a connection between number of updates performed by GSAP and the smoothness of the result - which lead me to the conclusion that either frequent updates of DOM style attributes have a non-trivial negative performance impact on Android or simply the browser is optimized for fire-and-forget CSS3 transitions and it reacts a bit slower when it has to apply css property changes in rapid sequence.


    Other than that GSAP works well in many other scenarios also on Android - and even the page translation animation is not that bad after all. But the plain CSS3 transition is smoother and more fluid.

  2. I'd like to share my personal experience re TweenMax performance on mobile.


    On iOS (iPhone 4 - 5) GSAP runs smooth as oil - at least on par with CSS3 also on 3D transforms - but with a much, much greater flexibility and control. So there is no match on iOS, IMO.


    On Android (>= 4.1.2 - don't bother with earlier versions), however, there is some noticeable difference with plain CSS3 transitions even on high-end hardware and simple animations. The same slide-out animation using Transit.js (a simple wrapper around CSS3 transitions) is a visibly smoother than GSAP - but then of course all animation control is pretty much lost. This is causing me some headaches because on the one hand I'd love to build the entire animation framework of my app around GSAP, but on the other the animations that really need to be super-fast are better with CSS3 transitions. Dilemma!


    The example is very simple:

    // This is a full-screen <div> with position: absolute;
    $page = $(...);
    // Using Transit http://ricostacruz.com/jquery.transit/
    $page.transition({ x: '+=240', duration : 500 });
    // Using GSAP
    TweenMax.to($page, .5, { x: '+=240' });
    // NOTE: I notice the same results if translateZ/{ z: .1} is
    // used (in the CSS, in TweenMax.set or within the tween options)


    You could try the simple test above on any of the latest Galaxy 3, Galaxy 4, Galaxy Tab2, HTC One - and you'd see that the GSAP version stutters a bit (it's still "fast" - but not "smooth") unlike the CSS3 version. After some investigation I reached the conclusion that there are 2 possible sources for this behaviour:

    • Perhaps the Android browser/GPU rendering engine handles ranged transitions from A->B better than a series of discrete matrix updates A->A1->A2...->An->B
    • The bottleneck may actually be DOM access - as GSAP needs to write into the DOM on every update (and then the new style needs to be applied/rendered) while the CSS3 transition is issued only once at the beginning

    My current strategy on Android is to use GSAP whenever I need ultimate control - and fallback to Transit on "fire and forget" transitions (like the page slide above), but of course I wished there was a way to stick to GSAP  for everything...

    • Like 2
  3. Yup. All fixed now. With the latest 1.9.7 the Safari issues are gone and performance is up to the expected levels (actually higher than that - it's nearly a 100% increase on the 3000-shapes benchmark on iOS, no jokes). Guess this fix will make a lot of iOS developers happy in the future.


    We can now answer the original question in this long thread ;) :


    - yes, you can safely let the GS Kinetic Plugin (configured with the default autoDraw=true) handle all drawings for you as redraws are correctly batched

    - yes, it's also OK to use autoDraw=false in the GS Kinetic Plugin and invoke layer.batchDraw() (NOT draw!!) inside your tweens/timelines onUpdate callbacks (if you need to do something fancier than simply pushing a layer through)

    - the choice between the 2 options is pretty much up to convenience and use cases (for example, sometimes it's nice to know exactly where a draw() request is pushed in order to add debugging sprites etc... so you'd go for the second option - while in other cases you'll appreciate the convenience of not having to add tons of identical onUpdate callbacks to your tweens... and you'd go for the first one)

    - there is no particular performance impact in using one strategy or the other as they both queue up the layers to draw in an array and serialize draw() calls on each of them once per tick. If you really into the bits-counting business, use autoDraw=true instead of onUpdate

    - if you know your target platform has problems with RAF and defaults/fallbacks to setTimeout for tick handling, then you probably want to use autoDraw=true in the Kinetic plugin as this serializes computations and drawings within a single callback, which is likely far more reliable than having both GS and Kinetic to juggle with their own setTimeout (things can get out of sync and stutter).


    All for today.


    P.S. BTW, I stared at the result of the FPS test I made for a good 1/2 hour as I could not believe it would report 2x on Safari vs. Chrome. I started challenging math, the laws of physics and the universe as a whole :)

    • Like 3
  4. Perhaps this is related to the above:

    <div>FPS: <span id="fps"></span></div>
    document.body.onload = function() {
      var fpsEl = document.getElementById('fps');
      function updateFps(evt) {
        var fps = Math.floor(evt.target.frame / evt.target.time);
        fpsEl.textContent = fps;
      TweenMax.ticker.addEventListener('tick', updateFps, null, true);

    The above outputs a reasonable value on Chrome and Firefox (~60 FPS) but it's completely wrong on Safari on OSX and iOS (it is ~120 - pretty much twice as much as it should be).

  5. Jack,


    I investigated a bit the case and run a few tests on desktop (Mac) Safari - as it shows a much worse performance than Chrome and the same symptoms of iOS Safari. I am a bit puzzled by the result - so you may want to have a look at it. 


    It just did not make sense that the RAF pump above was going to draw more than the plugin's _onTick callback - as they both, by definition, run their callbacks at most once per RAF tick. So the problem was either than _onTick was getting called less than it should have (unlikely, as it is directly registered to the ticker) or it was getting called too much. It turns out it might be the latter.


    As strange as it may sound (at least to me), the _onTick callback of the Kinetic Plugin is called twice per RAF tick - but only on Safari (both OSX and iOS version). Therefore the apparent 

    slowness vs. the simplest possible drawing routine (= one and only one per RAF tick).

    And yes, I have checked and double checked - it's a RAF and not a setTimeout. 


    So I have setup a simple RAF callback much like the "pump" above. The assumption is that its callback is run sequentially with GS's ticker event dispatcher - as it cannot be otherwise (there is only one animation frame regardless from how many callbacks request it). In that callback I simply increase a counter on each run to mark the current animation frame - so I can track it in the Kinetic Plugin _onTick. From _onTick I log the counter, the number of times it's been called, the ticker's frame and the ticker's time. The benchmark is setup with 3000 shapes (but really any number would produce the same result) and our reference is Chrome on OSX.


    Results on Chrome OSX:

    RAF:1 TICK:1 FRAME:18 TIME:0.523
    RAF:2 TICK:2 FRAME:19 TIME:0.657
    RAF:3 TICK:3 FRAME:20 TIME:0.784
    RAF:4 TICK:4 FRAME:21 TIME:0.904
    RAF:5 TICK:5 FRAME:22 TIME:1.011
    RAF:6 TICK:6 FRAME:23 TIME:1.111
    RAF:7 TICK:7 FRAME:24 TIME:1.229
    RAF:8 TICK:8 FRAME:25 TIME:1.386
    RAF:9 TICK:9 FRAME:26 TIME:1.565
    RAF:10 TICK:10 FRAME:27 TIME:1.712

    The above shows that _onClick (the "TICK" counter) is called once per "RAF" iteration as expected. Now let's look at Safari (OSX or iOS - different values, but same result):

    RAF:0 TICK:1 FRAME:33 TIME:0.742
    RAF:1 TICK:2 FRAME:34 TIME:1.33
    RAF:1 TICK:3 FRAME:35 TIME:1.843
    RAF:2 TICK:4 FRAME:36 TIME:2.382
    RAF:2 TICK:5 FRAME:37 TIME:2.99
    RAF:3 TICK:6 FRAME:38 TIME:3.652
    RAF:3 TICK:7 FRAME:39 TIME:4.334
    RAF:4 TICK:8 FRAME:40 TIME:5.077
    RAF:4 TICK:9 FRAME:41 TIME:5.858
    RAF:5 TICK:10 FRAME:42 TIME:6.63
    RAF:5 TICK:11 FRAME:43 TIME:7.406
    RAF:6 TICK:12 FRAME:44 TIME:8.186
    RAF:6 TICK:13 FRAME:45 TIME:8.949
    RAF:7 TICK:14 FRAME:46 TIME:9.706
    RAF:7 TICK:15 FRAME:47 TIME:10.416
    RAF:8 TICK:16 FRAME:48 TIME:11.095
    RAF:8 TICK:17 FRAME:49 TIME:11.718
    RAF:9 TICK:18 FRAME:50 TIME:12.315
    RAF:9 TICK:19 FRAME:51 TIME:12.879

    From the result above you see that _onTick is called TWICE per animation frame instead of once. For this reason autoDraw=true shows a worse performance than a simple callback that draws every time a RAF kicks in: it draws the Kinetic layer twice per tick.


    Note that the above can be reproduced with just about any number of shapes, so it seems to affect every draw performed by the Kinetic plugin with autoDraw enabled.


    I have added frame and time to the logs above as they may give some hints on what the culprit could be. It seems to me that there might be some deeper issue here than we thought.



  6. @eric first off, thanks for bringing such an awesome library to life.


    Regarding Kinetic.Tween I am definitely an advocate of having Kinetic to provide its tweening functionality out of the box. It just makes so much sense - and it keeps Kinetic dependency-free and ready-to-use by most people who want to learn and use the library. So thanks for going through the effort of adding it to the core library. It is not about Kinetic.Tween vs. GS - they both make sense for different reasons and it's great developers can have a choice. I am going with GS because it helps me to make complex things simple and keep my application workflow tidy. I am sure many other users will just love the way Kinetic.Tween works - and I am sure it will get better and better while still being very accessible. Performance is of course important - but that's never the only reason.


    I started this topic because I wanted to understand better what sort of drawing policy (KineticPlugin+autoDraw vs. Kinetic.Layer.batchDraw()) was going to bring the best results - as I couldn't make up my mind with the real-life applications I am building. Jack kindly cooked up an interesting - albeit purposely artificial - benchmark to test the options, which then we tweaked a bit. That benchmark incidentally shows a significant performance difference between Kinetic.Tween and GS - esp. on iOS (btw, I have also tried the edge version straight from GitHub - which is a little bit faster than stock 4.5.1). Again, this was no GS vs. Kinetic.Tween, but it is probably something you may want to have a look at.

  7. Ok, I think I got it. I modified the benchmark to use a "RAF pump", which is basically a dumb function that calls draw() and then RAF with itself as callback endlessly. This simulates what really happens in Kinetic's batchDraw():

      // autoDraw is set to false in the tweens
      // NO onUpdate callback is used as we will use the "pump" below to draw
      var RAF = window.requestAnimationFrame || ....
      function RAFPump() {
        RAF(function() {

    Now, I start the pump as soon as all tweens are created. This essentially schedules a draw upon every "tick". AFAICT this is precisely what happens when Kinetic's batch animation is used (in this simple one-layer case): the animation runs its own RAF pump (well, a bit more clever than the one above) and draws whatever layer has been scheduled for batch-drawing in between ticks. So with the "pump" we are now completely decoupled from Kinetic's drawing routines.


    The result? As expected, the behaviour is exactly the same shown by Kinetic's batchDraw - i.e. more frames drawn than we get with the GS Kinetic plugin in autoDraw mode.


    So it seems that, when autoDraw=true is used, in between _onTick callbacks in the GS Kinetic Plugin (which trigger the actual layer.draw()) there is room for one or more callbacks of the pump to kick in (most likely in-between tweens, I suppose, when the state of the shapes may be slightly inconsistent?). I am not too aware of the internals of GS, but this is the only reasonable explanation I can come out with. The result in this simple - and purposely artificial - benchmark is "more frames being drawn on screen" when a pump is used vs. drawing only after all tweens have been honoured for that time-slice. I am not sure this would be a desirable effect in real-life applications when interactivity is required - but again, we are looking at something rather artificial here.


    What do you think

  8. Thanks for sharing the details. You make an excellent point about the massive quantity of tweens causing the setTimeout fallback due to requestAnimationFrame not being able to fire frequently enough. Can you try calling TweenLite.ticker.useRAF(true) AFTER creating all the tweens (or a short time after that) and see if that makes a difference in the overall performance?


    It does not seem to make a difference (I tried tuning a few delays as well). As I mentioned above I don't see the setTimeout kicking in on iOS with 3000 shapes (it does however with more shapes or if the phone is connected to a debugger - but most likely because the browser is exhausted : ).

  9. No worries - there's no rush. So far:


    1) Using autoDraw=true combined with onUpdate->batchDraw is the slowest of all (6 screen updates vs. 8 with autoDraw=true alone vs. 13-14 with onUpdate->batchDraw alone. (3000 shapes, iPhone 5, iOS 6).


    2) No difference on page reload. I have used an instrumented version of GSAP with a log where the setTimeout fallback is activated and it does not seem to get hit. Same for Kineticjs: RAF is used there as well (for the batchDraw case).


    3) When autoDraw=true and no onUpdate callback is used (the "worst" case in this test), batchDraw does not get called at all. Conversely, when autoDraw=true and batchDraw is invoked from the onUpdate callback, draw() is never called by the Kinetic GSAP plugin (as expected) so it seems there is no overlap or conflict between the two.


    The same test run on Chrome/desktop shows that autoDraw=true calls draw() on average the same or more times than the animation that backs batchDraw() does - at around 100 redraws with 3000 shapes - so there autoDraw and batchDraw are roughly on par (autoDraw slightly better - but these values may not be that reliable). On Safari/desktop I see a lot less redraws (the number of redraws is not always a good indicator - but here it shows how long it takes for the browser to do a full cycle). BatchDraw consistently redraws 17 times. GSAP with the Kinetic plugin and autoDraw=true sets to 20/21 consistently. Still I have the impression the animation with batchDraw is more fluid - but it's just that, an impression.


    BTW, with 10000 shapes on Desktop Safari, TweenMax aften fallbacks to setTimeout (probably bc the browser is so busy when the animation is started that it doesn't get a chance to trigger a RAF within the 1s interval used by GSAP to detect possible RAF failures).


    It has to be said that what we are trying to do here is a pretty extreme case - there is no noticeable difference (to me) between the two options with hundreds of shapes. My test is also far from being scientific - so perhaps I should setup a more rigorous test.


    My conclusion so far is that this has nothing to do with GSAP and the GSAP Kinetic plugin redraw policy - which is very sensible and very fast (and GSAP itself is WAY faster than Kinetic.Tween).

    Most likely it's a combination of timing factors more than redraw policies.

  10. Thank you so much for your answer, Jack!

    I was under the impression autoDraw was set to true by default for a reason :)


    I ran the benchmarks on iOS 6 and the GSAP test runs well (understandably slowish given the nature of benchmark - but certainly acceptable). On the other hand the Kinetic.Tween benchmark (which is certainly slower than the GSAP one on my desktop) does not work at all on the phone with the default 800 shapes - as nothing gets animated.


    I managed to get the Tween benchmark to run decently with ~100 shapes, but even then the animation is not smooth and stutters quite often (as if the GC was kicking in many times). Above 100 shapes Kinetic.Tween's performance deteriorates visibly - and it stops working (= animation does not even start) at 200 shapes on my iPhone 5.


    No doubt GS is a winner vs. Kinetic.Tween (which, in fairness, Eric did in record time and it's therefore very early stage - and most likely never really tested on mobile). As a user of both GS and Kinetic I am super happy these 2 great libs can work in tandem as I don't think I would ever drop GSAP (albeit good basic Tween support is IMHO a must for any canvas framework for the simpler use-cases).


    Back to the BatchDraw topic, I tweaked the GSAP benchmark to use Kinetic's batchDraw instead of autoDraw:

    TweenLite.to(creature, 4, {
        kinetic:{x:900, y:Math.random()* 320, rotation:Math.PI*2, autoDraw:false },
        delay:Math.random() * 8,
        onUpdate: layer.batchDraw,
        onUpdateScope: layer});
    The jury is still out on this. With a low number of shapes (~100) no difference is really noticeable (at least to me). Using batchDraw seems to stutter a little bit from time to time (same stop-the-world GC effect described above). With the default 800 shapes my perception is that batchDraw produces a smoother animation that autoDraw, although it's probably still subjective.


    Raising the number of shapes up to 3000 (again, on iOS) reduces the animation to a crawl but it shows an interesting empiric fact: with autoDraw the canvas is redrawn 8 times within the allotted time, while with batchDraw the canvas is redrawn 13 times. With 5000 shapes, the autoDraw benchmark draws 5 times and the batchDraw one 8 times. In these cases explicit batchDraw performed inside the onUpdate callback is a winner vs. autoDraw - by ~60%.


    It seems that getting GS to post into the Kinetic layer animation queue has some advantages - although I am not entirely sure why (as they technically seem to do the same thing - just Kinetic has its own animation loop kicked by a RAF callback). You are probably the best one to understand/comment on this.


    Again, thanks a lot for your help on this. I much appreciate it!

    • Like 1
  11. Hi there,


    I am using GSAP with Kineticjs and I am very happy with the combo.

    One of the things I like most about GSAP - alongside its features and super performance - is the way it helps me organizing my code and overall workflow. I think Kinetic and GSAP are a very good match for all sort of canvas-based applications.


    I have a couple of questions/doubts about the Kineticjs GSAP plugin - which I use a lot (the autoRotate support provided by the kinetic+bezier plugins saved me literally hours of pain). The questions are quite Kinetic-specific but this forum feels like the right place to discuss this topic.


    The GSAP Kinetic plugin handles drawings automatically by default (unless "autoDraw" is set to false - that took me quite a while to find!). On the other hand Kinetic has recently introduced batchDraw as a way to minimize layer redrawing by "batching up" updates in a special animation. I am not 100% sure what the best drawing strategy is when the two libs are combined:


    - Should the GSAP plugin be changed to use layer.batchDraw instead of layer.draw by default with autoDraw=true? Or should that be configurable (and when would one pick one vs the other)?

    - One should set autoDraw=false and handle updates manually by calling layer.batchDraw onUpdate?


    I currently use the latter almost always as it feels like I am having more control on what gets drawn (for example when animating many groups/shapes belonging to the same layer simultaneously in different tweens/timelines). In my understanding this means each GSAP "tick" queues up drawing requests into a Kinetic Animation which then performs the actual drawing when its own RAF fires - and that does not seem very efficient (as there are 2 RAF at the same time). Please correct me if I got it all wrong.


    Ideally I would like the GSAP plugin to do "the right thing" with batching (it seems it sort of does already?) - but would that work reliably when shapes belonging to the same layer are animated by different tweens/timelines simultaneously (that's my most frequent use-case)?


    Many thanks to the group for any advice on this.

    Keep up with the great work.