How I Fight: Stand Up To Cancer
Explore an interactive WebGL canvas full of hundreds of UGC inspired hand-drawn illustrations that demonstrates that each and every way we fight cancer is powerful.
Based on tweets sent by real people using the hashtag #howifight, we created more than 150 customized watercolor illustrations, each representing a very unique way to fight cancer.
Each illustration went through a selection process, as to best represent the original tweet.
This created an abundance of beautiful content, that was to be splayed out over a virtual canvas, allowing the user to explore and interact.
For the layout of the illustrations, we wanted to create a natural structure that didn’t feel grid-based or follow a visible pattern. With hundreds of objects to position, doing so manually would have been a very tedious task.
Our solution was to create a physics-based layout using our in-house physics engine. It randomly positioned the images following some pre-defined values. This let the computer do the hard work, and we were able to generate new, infinitely-large layouts in an instant to find those which we liked best.
Here is an extremely slowed-down version of how the simulation works under the hood. In the actual version, hundreds of circles are randomly placed instantaneously and the simulation lets them fall into place.
This meant that generating unique layouts for different devices and screen sizes was a painless task.
We saved the layout positions in a JSON file and simply referred to them whenever a user accessed the site, changing the order of the images to add another degree of randomness.
Often we find that putting the effort in early and choosing the more difficult or complex path towards a solution rewards us greatly in the long-run, ultimately saving time and not restricting ourselves creatively.
Following along with the theme of illustration, we created a watercolor animation style to make the apparition of the illustrations more natural and interesting in general.
Here is an animation our original solution using WebGL. The image is separated into greyscale and colour, and each is animated separately, bleeding in as though the ink had just been dropped onto the page.
This created a very dynamic animation, bringing the still illustration to life.
Generally, to achieve this type of animation, a sequence of images would be loaded and flipped through one-by-one. However, this would require a very heavy load, as an entire image sequence per illustration would be required. Instead, what we did here was to write custom WebGL shaders that allowed us to use a video of a mask animation, which was then applied to the image, revealing it slowly.
Here is the mask used for this example.
This meant that one mask could be used on many illustrations, and only one image per illustration would need to be loaded. Inside the shader, the image was separated into a grey and color version to add yet another level of complexity.
Although we loved this result, and the file size was very decent, we were having performance issues on certain devices due to creating many video instances. We tried converting the mask animation into a few separate image sequences instead of a video, which we then could place randomly and combine to create very dynamic, varied animations. Unfortunately however, we were still having issues on some devices while trying to generate and animate the masks for tens of illustrations at a time.
Our solution was to encapsulate the entire mask animation into a single image, and animating the values of this image to create the variation.
For example, we took the below animation…
…and separated the frames into separate images.
Then in one file, we overlaid the images, adjusting the darkness of each so that each frame had its own grey color value. For example, the first frame was completely black, and the last was almost white.
We could then animate the levels of the image, slowly revealing the mask. Here we illustrate the concept using photoshop.
Below you can find a very handy module of GLSL shader code that allowed us to do this in code, the inputs of which are similar to that of photoshop levels. We simply animated a few of the inputs to the desired levels, resulting in a mask turning from white to black over time.
Again, we could use several of these masks in a single animation, randomizing the position, rotation and timing to create infinite versions.
Although the final effect is somewhat less defined than the initial version, we found that it ran at full speed across devices, even while many were animating concurrently.
This really helped bring the image to life, and was simple and elegant enough to work across the board without a hitch.
Taking this one step further, we could have actually included 3 mask animations in one image by harnessing each of the red, green and blue values individually, resulting in something like the image below.
In order to make the website feel more tactile — like touching a real canvas — we applied a 3D push effect when a user clicked/touched the site. Although we kept it reasonably subtle, it really helped to lend a physical nature to the interaction.
Again based on WebGL, we initially projected the site onto a 3D object with many polygons, and animated the vertices of this object, which in turn ‘pushed’ the image in 3D space.
As a user moved their finger/cursor around the screen, the center of this bulge would shift accordingly. Then as they released, the bulge would again flatten.
Although this looked and worked great, again, on some devices performance was waning somewhat.
Our solution was to write another WebGL shader that would warp the positions of the pixels themselves in two dimensions to create a bulge effect.
The way this is achieved is by editing the positions of the pixels based on the position of the cursor, using a falloff curve applied in all directions to control the strength that those pixels are shifted towards the point.
As the user moves their cursor around, this point is updated and the pixels shift accordingly.
This method requires much less calculation on the CPU, and allowed us to keep the effect while maintaining a smooth experience.