Tag Archives: tips

Performance & UX Considerations For Successful PhoneGap Apps

I think some of the most common questions people ask when developing PhoneGap apps are about performance. How do I make my app faster? How do I make it feel native? Any tips of making an app feel at-home on a particular platform? How do you match native OS “look and feel”? In this post, I’ll attempt to provide some insight on techniques for creating great PhoneGap apps, and also try to debunk a few myths floating around the “interwebs”…

I’ve already posted about workflow and app store approval processes, and in this post the primary focus is runtime performance.

When we are talking about performance, we are really talking about perceived performance and responsiveness from an end-user perspective. Since PhoneGap applications are based upon HTML & web views, you are limited by the performance of a web view on a particular platform. With that said, that doesn’t mean all HTML-based apps are inherently slow. There are very many successful apps with an HTML-based UI. Some even get featured in the app stores. We can’t make the web views faster, but we can help you make your HTML experience faster inside of that web view.

However if the web view isn’t fast enough for your taste, don’t forget that you can use PhoneGap as a subview of a native app. In this case, you can leverage native user interface elements when you need them, and leverage PhoneGap’s HTML/JS user interface when you need it. Then use PhoneGap/Cordova’s native-to-JS bridge to handle bidirectional communication between the native code and the JavaScript inside of the web view component. More on this later…

HTML/Web View Performance

First, let’s talk specifics about mobile HTML & web performance. There are a lot of common “tips” floating around, some of these are extremely valid, although others are misleading. Please allow me to clear some things up…

Hardware Acceleration

All over the the web, you can see generic statements telling you to force hardware acceleration whenever possible, in every scenario, just by adding a css translate3d transform style:

transform: translate3d(0,0,0);

While it is true that you can force GPU rendering of HTML DOM content, you really need to understand how it can impact the performance of your application. In some cases, this can greatly improve the performance of your app, while in other cases, it can create performance issues that are extremely difficult to track down. In other words: Do not use this for every case, and on every HTML DOM element.

You should be very selective when you use transtale3d to force hardware acceleration for a number of reasons.

First, whenever you are using translate3d, the content being accelerated takes up memory on the GPU. Over-use of GPU rendering on HTML DOM elements can cause your application to use up all of the available memory on the GPU. When this happens, your app could either crash completely without any error messages or warnings, or the GPU could swap memory content from device RAM or permanent storage. In either of these cases, you will have worse performance than you did without forced GPU rendering.

Second, whenever you apply a translate3d transform, this causes the content of the HTML DOM element to be uploaded on the GPU for rendering. In many cases, the GPU upload time is imperceptible, and the results are positive. However, if you apply the translate3d style to a very large and very complex HTML DOM element, a noticeable delay can be encountered, even on the latest and greatest devices and operating systems. The more complex the HTML DOM, the larger the delay. When this happens, the web view (or browser) UI thread can be completely locked up for that period. While the worst I’ve seen of this was less than a second, it still can have a profoundly negative impact when your entire UI locks up for 500ms. In addition, any time you make changes to a DOM element (content or style-wise), that texture has to be re-uploaded to the GPU.

Third, avoid nesting DOM elements that use translate3d. Let’s consider a case where you have lots of DOM elements inside of a <div> elements, multiple levels deep, and all of those elements, including the outtermost <div> use translate3d for GPU rendering. For the outter-most <div> to be rendered, all of it’s child elements have to be rendered. In order for all of those child elements to be rendered, all of their child elements have to be rendered, etc… This means that all of the child elements have to be uploaded to the GPU before the parent element can be rendered. This adds up very quickly. Nested elements that use the css translate3d GPU hack will take longer to load, and can take up significantly more memory, causing very poor performance, slow GPU upload times, or complete app crashes (see paragraphs above).

Fourth, consider the maximum texture size for the GPU. If the DOM element is larger than the maximum texture size supported by the GPU (in either width or height), then you will encounter very poor performance and visual artifacts. Have you ever seen HTML DOM elements flicker during an animation or when scrolling, when using translate3d? If so, then there’s a good chance that the DOM element’s dimensions are larger than the texture size supported by the device’s GPU. The maximum texture size on many mobile devices is 1024×1024, but varies by platform. The maximum texture size on an iPad 2 is 2048×2048, where the maximum texture on the iPad 3/4 is 4096×4096. If the device maximum texture size is 1024, and you DOM element’s height is 1025 pixels, you will have those pesky flickering and performance issues.

Note: In some platforms, you can also have flickering due to the browser implementation, which can often be corrected by setting backface-visibility css. See details here.

Fifth, remember that you are on a mobile device. Mobile devices tend to have slower CPUs, slower bus, and less memory than desktop devices. While you may get great results leveraging translate3d on the desktop, it could kill your experience on mobile. In addition, different platforms have different levels of support for hardware acceleration, and performance can vary.

Translate3d can definitely yield positive results, but can also yield negative results too, depending upon how it is used.  Use it wisely, and always, always, always test on a device.

The Impact of Content Reflow

If you’ve never heard of “reflow” before, you should pay attention. “Reflow” is the browser process for calculating positions and geometries for HTML DOM elements. This includes measurement of width & height, the wrapping of text, the flow/relative position of DOM elements, and basically everything related.

The calculations used by the reflow processes can be computationally expensive, and you want to minimize this cost and the amount of content reflow to achieve optimal performance. If you’ve ever animated the width of a HTML DOM element, and seen the framerate drop to to 5 FPS or lower, then you’ve seen the impact of reflow processes in action.

Reflow processes are invoked any time DOM content changes, DOM elements are resized, CSS positioning/padding/margins are changed, etc… While you may not notice any impact of reflow processes on desktop browsers, reflow can have a significant performance impact on mobile devices. You can never get rid of reflow processes altogether (well, unless your content absolutely never changes), but you can mitigate the impact of those relflow processes.

If you’d like to learn more about reflow processes, I strongly suggest reading these:

If you’ve seen lists of tips for PhoneGap & Mobile web apps, you may have seen statements like “minimize DOM nodes”, “avoid deeply nested html”, use css animations, preload images, etc… The reason that these are suggested is that they are tips for minimizing the impact and occurrence of reflow operations.

  • Minimize DOM node instances – The fewer DOM nodes, the less amount of nodes that have to be measured and calculated in reflow operations.
  • Avoid deeply nested HTML DOM structures – The deeper the HTML structure, the more complex and more expensive reflow operations. In addition, changes at the lowest child level will cause reflow operations all the way up the tree, ending up more computationally expensive.
  • Use CSS transforms – In addition to forcing hardware acceleration as discussed above, you can alter HTML DOM elements *without* invoking reflow processes if you change them using CSS3 transform styles. This could be translation on the X, Y, or Z axis, scaling, or rotation. However, be careful not to overuse css translate3d for the reasons mentioned above.
  • Use CSS animations & transitions - CSS animations and transitions can definitely make things faster. However this is not a “catch-all” that covers every case. If you use CSS transitions with properties that introduce reflow operations (such as width or height changes), then your performance could stutter. You can achieve fluid animations by leveraging CSS transitions, in conjunction with css transforms (as mentioned above) b/c they alter the visual appearance, but don’t invoke reflow operations.
  • Use fixed widths and heights for DOM elements - If you don’t change the size of content, you don’t invoke reflow operations. This could be for <div> (or other) elements, or it could apply to the loading of images. If you don’t predefine an image size, and wait until the image is loaded, then there is an expensive reflow operation when the image loads. If you have multiple images, this adds up.
  • Preload images or assets used in CSS styles – This is a benefit for two reasons. First, your images will already be ready when you need them. This prevents a delay or flicker when waiting for content. Second, if you already have images or other assets preloaded into memory before they are needed, then you don’t have a secondary reflow operation that could occur after the image/assets have been loaded (1st reflow is when DOM is initially calculated, 2nd when its recalculated on load).
  • Be smart with your HTML DOM elements – Let’s say you want to create and populate a <table> element based on data you have in a JavaScript array. It is very expensive to first append a <table> element, then loop over the array and individually append rows to the existing DOM on every loop iteration. Rather, you could loop over the array, and create the HTML DOM element for the table only. Then, after the loop has been completed, append the <table> to the existing HTML DOM. Every time that you append DOM elements, you invoke a reflow operation. You want to minimize these operations.

In general, you want to minimize the amount of work required by the browser to calculate layout and positioning of DOM elements.

Keep Graphics Simple

Designers love to make great looking mockups, but sometimes the implementation of such designs don’t always yield great performance. Overuse of CSS shadows and CSS gradients can impair runtime performance across different platforms & browser implementations. I’m not saying don’t use them at all, just use them wisely. For example, CSS gradients and shadows induce slower performance on Android devices than a compariable iOS device, based on the OS/system web view implementation.

Touch Interactivity

You’ve probably heard before that “mouse events are slow and touch events are fast”. This is absolutely a true statement. Instead of using “mousedown”, “mousemove”, “mouseup”, or “click” events, you should use touch events: “touchstart”, “touchmove” and “touchend”.

On mobile devices, mouse events have latency that is introduced by the operating system layer. The OS tries to detect if a gesture has occurred. If no gesture has occurred, it passes the event on to the web view as a mouse event. When you use touch events instead of mouse events, you recieve the event immediately, without the operating system-introduced latency.

You may have noticed that there is no equivalent of “click” for touch events… That’s right, there isn’t one. However, you can manually manage the touch events yourself to infer a click action, or you can leverage an existing library to infer “taps” or gestures. Here are a few for consideration: Zepto, FastClick, Hammer.js, iScroll (yes, iScroll removes the click delay for you inside the scrollable area)

JavaScript Optimizations

In essence, write efficient code that doesn’t block the UI thread. I could go on for a while on this, but instead, just follow language optimizations and best practices. Here are a few articles on the subject:

There are lots of facets and permutations to consider when building your apps, so just try to write decent code that you wouldn’t be embarrassed to show to your coder friends.

Native Performance

So, let’s say that you want a native user experience, native navigation between containers, but you also want the ease of content creation with HTML? Nobody said you can’t use PhoneGap as a subview of a native application. In fact, there are some VERY widely used apps in existence that employ this technique… If only I could tell you (NDA).

This approach requires native coding expertise, but you can use CordovaView as a subview inside of a native application on both iOS and Android platforms. This allows you to take advantage of native components when you want to, and leverage a PhoneGap/HTML UI when you want to. PhoneGap provides you with the native-to-JavaScript bridge for full bidirectional communication between the native and JavaScript layers.

When using HTML, it is really easy to create custom UIs, whether they are complex graphics, tabular data, or anything really. With the CordovaView approach, you let each layer (native/HTML) do what it does best.

You can learn more about embedding the CordovaView inside of a native application from the PhoneGap Docs.

UI & UX Considerations

At risk of sounding overly-generalized: You want to pay attention to building a quality user experience. If you don’t focus on quality, your application will suffer. I’ve already covered user interface and user experience considerations in detail, but I’ll highlight one common question that I encounter: “How do I make an app that exactly matches native look and feel for all platforms?”

My response: Don’t.

Let me clarify this point: Im not saying don’t build something that looks “at home” on a particular platform. I’m suggesting that you don’t try to match the operating system in every single detail. The main reasons being that 1) this is very difficult to do, and 2) if anything changes in the OS, it will be very obvious in your application.

You may or may not have heard of the “uncanny valley” effect. When applied to PhoneGap apps, if you get really, really close to behaving exactly like a native app, but there are minor differences, then it can immediately signal that “something is different” or “something is wrong” with your app, and can cause an undesired negative experience for the user.

Instead, I recommend creating a unique identity for your app (that still meets App Store guidelines). This includes look and feel, button styles, navigation, content, etc… Rather than mimicking every aspect of the native UX, build a cohesive experience around your identity or brand, and carry that identity through all of your mobile apps. People will associate this identity with your app, rather than comparing it to the native OS.

However, if you do want to mimic native look and feel, it can be done completely with CSS styles.

Test On Devices

I can’t emphasize this point enough.  Always test on a device. Always. I like to target older devices, because if you can make it fast on an older device, it will be even faster on a newer one. Be sure to test on-device on all platforms that you are trying to target. For iOS, I test on an iPhone 4 (not 4s) and and iPad 2. On Android, I use a Motorola Atrix, Nexus 7 tablet, Kindle Fire (first generation), and a Samsung Galaxy 10.1 (first generation). I test other platforms whenever I borrow a device, and pretty much use whatever I can get my hands on.  Heck, I’ve even gone into retail stores just to install an app on one of their display devices to see how it looks.

Mobile Web & PhoneGap HTML Dev Tips

Recently I’ve been spending a fair amount of time working on HTML-based applications – both mobile web and mobile applications using PhoneGap.   Regardless of whether you are targeting a mobile web browser or a mobile app using the PhoneGap container, you are still targeting a mobile web browser instance.  If you haven’t noticed, mobile web browsers can often have peculiarities with how content is rendered, or how you interact with that content.   This happens regardless of platform – iOS, Android, BlackBerry, etc…  All have quirks.  Here are a few tips that I have found useful for improving overall interaction and mobile HTML experiences.


Disclaimer: I’ve been targeting iOS and Android primarily, with BlackBerry support on some applications.  I don’t have a Windows Phone device to test with, so I can’t comment on support for the Windows platform.



AutoCorrect and AutoCapitalize

First things first: autocorrect and autocapitalize on Apple’s iOS can sometimes drive you to the brink of insanity.  This is especially the case if you have a text input where you are typing in a username, and it keeps “correcting” it for you (next thing you know, you are locked out of the app).   You can disable these features in web experiences by setting the “autocorrect” and “autocapitalize” attributes of an <input> instance.

Disabled AutoCorrect:

<input type="text" autocorrect="off" autocapitalize="on" />

Disabled AutoCapitalize:

<input type="text" autocorrect="on" autocapitalize="off" />

Managing the Keyboard

Have you ever experienced an an app or web site on a mobile device where you have to enter numeric data, and the default keyboard pops up. Before entering any text, you have you switch to the numeric input. Repeat that for 100 form inputs, and try to tell me that you aren’t frustrated… Luckily, you can manage the keyboard in mobile HTML experiences very easily using HTML5 Form elements.

Default Keyboard: Supported Everywhere

<input style="width: 400px;" type="text" value="default" />

Numeric Keyboard: Supported on iOS, Android & BlackBerry (QNX)

<input style="width: 400px;" type="number" value="numeric" />

Numeric Keyboard: Supported on iOS

<input style="width: 400px;" type="text" pattern="[0-9]*" value="numeric" />

Phone Keyboard: Supported on iOS

<input style="width: 400px;" type="tel" value="telephone" />

URL Keyboard: Supported on iOS & BlackBerry (QNX)

<input style="width: 400px;" type="url" value="url" />

Email Keyboard: Supported on iOS & BlackBerry (QNX)

<input style="width: 400px;" type="email" value="email" />

Disable User Selection

One way to easily determine that an application is really HTML is that everything on the UI is selectable and can be copied/pasted – Every single piece of text, every image, every link, etc… Not only is this annoying in some scenarios (and very useful in others), but there may be instances where you explicitly don’t want the user to be able to easily copy/paste content. You can disable user selection by applying the following CSS styles. Note: This works on iOS, and partially works on BlackBerry/QNX for the PlayBook. It did not work on Android in my testing.

<style>
* {
-webkit-touch-callout: none;
-webkit-user-select: none;
}
</style>

The -webkit-touch-callout css rule disables the callout, and the -webkit-user-select rule disables the ability to select content within an element. More details on webkit css rules from the Mobile Safari CSS Reference. More detail about disabling copy/paste on iOS is available at StackOverflow.com.

Disable Zoom

If you want your content to feel like an app instead of a web page, then I strongly suggest that you disable gestures for pinch/zoom and panning for all use cases where pinch/zoom is not required. The easiest way to do this is to set the viewport size to device-width and and disable user scaling through the HTML metadata tag.

<meta name="viewport" content="width=device-width, user-scalable=no" />

You can read further detail on the viewport metadata tag from the Apple Safari HTML Reference, or the Mozilla reference.

On a Phone? Integrate With It

Your application can dial phone numbers very easily. Just use a standard web location, but use the “tel:<phonenumber>” URI format.

Test it with Apple Customer Support: 800-275-2273

<a href="tel:800-275-2273">800-275-2273</a>

This technique works on both Android and iOS devices, and I assume other platforms. However, I don’t have the devices to test all of them.

Touch Based Scrolling

Touch-based scrolling is critical to having an application that feels native. I dont mean that the whole page should be able to scroll… Your browser will be able to take care of that alone. Instead I mean that you should be able to scroll individual elements so that they mimic clipped views, lists, or large blocks of content. You should be able to scroll content where it is, and not have to scroll an entire page to reveal something in only one area of the screen. You should minimize scrolling when it may cause poor UX scenarios. This is especially the case in tablet-based applications which have a larger UI than phone-based applications.

Luckily, this is also really easy. I personally prefer the open source iScroll JavaScript library from cubiq.org. iScroll works really well on iOS, Android and BlackBerry – I haven’t tested other platforms, but you can test them out yourself: http://code.google.com/p/iscroll-js/source/browse/#hg%2Fexamples%2Fcarousel

Remove “click” Delays

“Click” events on HTML elements on mobile devices generally have a delay that is caused by the operating system logic used to capture gestural input based on touch events. Depending on the device, this could be 300-500 MS. While this doesn’t sound like much, it is very noticeable. The workaround is to use touch events instead of mouse events: touchStart, touchMove, touchEnd. You can learn more about touch events from html5rocks.com. There’s also a great script from cubiq that adds touch events for you to optimize the experience for onClick event handlers on iOS devices.

Add To Home Screen

If you want your web app to fee like a real app and take up the full screen without using PhoneGap as an application container, then you can always add it to the device’s home screen. Although this can only be done manually through the mobile browser, there are a few open source scripts to guide the user through this processs: cubiq.org or mobile-bookmark-bubble should get you started.

Use Hardware Acceleration

Animations will generally be smoother and faster if your content is hardware accelerated (and the device supports hardware acceleration). You can make html elements hardware accelerated just by adding the translate3d(x,y,z) css style to the element (be sure to set all three x, y, and z attributes otherwise hardware acceleration may not be applied. If you don’t want any translation changes, you can use the translate3d CSS rule with all zero values: translate3d(0,0,0).

transform: translate3d(0,0,0);
-webkit-transform: translate3d(0,0,0);

In your development/testing, you can even visualize which content is hardware accelerated in both desktop and mobile Safari using the technique shown at http://mir.aculo.us/.

Make You Apps Fast

Last, but certainly not least, make your apps fast. Follow best practices, and be efficient in code execution and the loading of assets (both local and remote). Here are a few links to get you going in the right direction:

I hope these get you moving in the right direction! If you have read this, and aren’t sure what it all means, check out the Adobe Developer Connection to ramp up on HTML5, or theexpressiveweb.com to see what HTML5 & CSS3 can do.