The Physics of Loyalty

INTERSECT helps businesses create and deliver exceptional digital products that marry business objectives and user needs. Learn more.

In the midst of a brand revolution, Second Cup Coffee Co. is on a mission to be the coffee brand most passionately committed to quality and innovation.

At the heart of this mission is an app experience existing customers would love and that would attract new customers with easy payments, rewards, and a truly delightful interface. Integral to the experience is the main interface: a simple cup that gets filled with different coloured drops as the user earns points. To add surprise and delight, the UI design called for liquid that actually behaved like liquid. But not just any liquid — that would require sloshing and spilling — a stylized liquid that provided visual interest and an element of interactivity.

What may seem simple on the surface is actually an exciting engineering challenge — one that we solved using Google’s LiquidFun library.

screen_seven

Liquid Animation

LiquidFun

Google’s LiquidFun library does a lot of the hard work right out of the box. It’s based on the popular C++ Box2D physics library. Android developers can use a simplified Java wrapper that has been rewritten to accommodate particle effects. However, this often presents surprise inconveniences because the Java wrapper is missing many useful methods that are otherwise available at the C++ layer.

LiquidFun Paint Library

LiquidFun Paint is a free app and an open source library. We retrofitted its source code to work in our app, customizing the Java layer in order to produce a pear-shaped drop, and to let gravity drop it into the coffee cup. Integrating the particle renderer into the TextureView was a challenge, but the details of this implementation are not that exciting. For your convenience, I’ve made available an open source implementation of the LiquidFun TextureView similar to the one we used.

Early trials with the liquid, though elegant, didn’t fit into the style defined by the rest of the app:

screen_eight

The translucent edges of the liquid and the darker shading where the liquid pooled felt true to life, but the rest of the app was comprised of the solid tones and clean edges which defined the style. What we desired was something like this (actually, exactly this):

screen_six

Tweaking the LiquidFun Shaders

In order to get nice, crisp edges on the liquid in the cup, we had to customize the OpenGL shaders used by LiquidFun. To accomplish this, we first had to understand how the liquid shaders created their distinctive, liquid-y appearance in the first place.

Simulating a 2D liquid is a two-stage process. At its foundation, it is derived from simple one-dimensional particles colliding in 2D space. The particles themselves are generated by the Box2D library, and act as point collision objects. Their positions and movement are all calculated by LiquidFun’s particle module, and don’t need any customization unless you’re feeling adventurous.

liquid_particles_hd Box2D can calculate the dynamics of individual particles as they interact with one another.

Once the particles’ position is determined, an OpenGL shader draws a translucent image overtop each particle:

liquid_contours_hd Stage 1: The shader then draws a circular gradient over each point and will even tint it to a colour defined for each point.

It tints the gradient to the vertex colour of the particle. The resulting image is drawn to an invisible buffer. This completes the first stage. A second pass then takes the image stored in the buffer, blurs it, and crops all parts that are below a certain predefined threshold transparency:

liquid_contours_edges_hd Stage 2: The second shader crops all pixels above a line of equal transparency.

To crop a pixel means to make it invisible, i.e. perfectly transparent. This creates the appearance of a liquid with translucent edges.

To create the effect of a solid, cartoon-like liquid, we need to intercept the shader at both levels.

The first step was to get rid of the faded edge. This is accomplished by raising the transparency threshold at which the liquid is cropped. The shader that does this in the LiquidFun Paint library is located in (assets/shaders/screen.glslf) (glslf = OpenGL Shader Language, Fragment):

//assets/shaders/screen.glslf void main() { gl_FragColor = texture2D(uDiffuseTexture, vTexCoord);
// Alpha Threshold gl_FragColor.a = (gl_FragColor.a > uAlphaThreshold) ?
gl_FragColor.a : 0.0; }

Each ‘pixel’ of the output buffer is called a fragment, hence the shader is referred to as a fragment shader. In the above code, the last line checks the alpha (transparency) value of a fragment against a predefined threshold. If it is below the threshold, it sets its alpha to 0 (invisible).

The alpha threshold being used here is defined in a configuration file located in (assets/materials/particlerenderer.json):

//assets/materials/particlerenderer.json
...
"waterParticleToScreen": {
"alphaThreshold": 0.95
},
...

We set the alphaThreashold value to 0.92. Unfortunately, cropping at such a high threshold has the effect of removing most of the visible liquid:

liquid_contours_filled_small_hd Very little liquid is left when the fragments are cropped to such a high threshold.

To counteract this, we raised the size of each individual liquid particle by increasing the value of ‘particleSizeScale’ in the config file (assets/materials/particlerenderer.json):

//assets/materials/particlerenderer.json
...
"waterParticlePointSprite": {
"uDiffuseTexture": "textures/particle_blurred.png",
"particleSizeScale" : 4.5,
"weightScale": 0.001,
"weightRangeShift": 1.0,
"weightCutoff": 0.1
},
...

Finally, the values used for weightScale, weightRangeShift, and weightCutoff above create a flat color instead of a gradient. These values are applied in an the earlier-stage shader assets/shaders/water.glslv.

liquid_contours_filled_hd Enlarging the size of the individual particles counteracts the emaciated appearance of the liquid, giving it a fuller, healthier look.

Conclusion

Any designer or company could have had a typical points display or thermometer to measure progress. We made it about interaction and fun, as much as communicating progress as giving people something to play with while they eagerly wait in line to pay for their much needed pick-me-up. The team and the client were impressed with the final product, as the style and behaviour of the coffee drops support and celebrate the brand’s new image. We created a captivating effect that required very few trade-offs and concessions. Subtle (and not so subtle) touches like these help an app stand out in an over-crowded marketplace, and it is by experimenting with fringe and early technologies that we discover possibilities for innovative customer outreach.

The LiquidFun Paint app is available for testing on Google Play.

Check out the Second Cup Coffee Co.™ app on Google Play and the App Store for iPhone and Apple Watch.

Resources

A TextureView implementation of LiquidFun – open source library

An article on Android’s graphics architecture and pipeline. This is recommended reading for anyone who wants an in-depth understanding of Android’s buffer queues and openGL surfaces.

LiquidFun home page LiquidFun open source repo LiquidFun Paint home page LiquidFun Paint open source library

The open source library does not include the LiquidFun library itself. You need to download and add it to the LiquidFun Paint library above.

The LiquidFun Paint app is available for testing on Google Play.