<br />
<b>Warning</b>:  Declaration of Jetpack_IXR_Client::query() should be compatible with IXR_Client::query(...$args) in <b>/home/clients/7267bc096562fcdb78c0ab60d3ac51fb/web/blog/wp-content/plugins/jetpack/class.jetpack-ixr-client.php</b> on line <b>91</b><br />
{"id":824,"date":"2016-06-20T17:02:49","date_gmt":"2016-06-20T17:02:49","guid":{"rendered":"http:\/\/barradeau.com\/blog\/?p=824"},"modified":"2016-06-30T17:31:36","modified_gmt":"2016-06-30T17:31:36","slug":"the-great-animal-orchestra","status":"publish","type":"post","link":"http:\/\/barradeau.com\/blog\/?p=824","title":{"rendered":"Le Grand Orchestre Des Animaux"},"content":{"rendered":"<p>I\u00a0worked for the past 3 months\u00a0on a\u00a0website with the good people at\u00a0<a href=\"http:\/\/www.upian.com\/\" target=\"_blank\">Upian<\/a>\u00a0for the good people of\u00a0<a href=\"http:\/\/fondation.cartier.com\/\" target=\"_blank\">L<\/a><a href=\"http:\/\/fondation.cartier.com\/\" target=\"_blank\">a Fondation Cartier, pour l&#8217;art contemporain<\/a>.<\/p>\n<p>It is called: <a href=\"http:\/\/www.legrandorchestredesanimaux.com\/\" target=\"_blank\">Le Grand Orchestre Des Animaux<\/a>\u00a0(The Great Animal Orchestra) and is\u00a0based on the work of <a href=\"http:\/\/www.wildsanctuary.com\/\" target=\"_blank\">Bernie Krause<\/a>, a man who\u00a0dedicated his life to recording the sounds of nature and raising awareness\u00a0about its\u00a0degradation through time in a striking\u00a0manner.<\/p>\n<p>This project is not advertising. I&#8217;m used to short lived and rather pointless websites, this one is\u00a0supposed to live (virtually) forever and conveys a powerful message. This changes the way we did it, we had more time, we discussed a lot. As a dev, I had a word to say about how I thought things should go, even on the conceptual level. It is unfrequent and\u00a0<strong>very<\/strong> enjoyable.<\/p>\n<h2>Sound<\/h2>\n<p>The\u00a0core material of the website is sound, it&#8217;s the\u00a0<em>raison d&#8217;\u00eatre<\/em>, the pivot of this experience. First we discussed about using the WebAudio API to perform spectrograms, cross blends, 3d\u00a0panning and all sorts of weird filtering but it didn&#8217;t quite fit with the work of Bernie Krause. Instead\u00a0we chose a more <em>respectful<\/em> approach to the sound material and no real-time alteration.<\/p>\n<p>I was not in charge of the sound production\u00a0&#8211;\u00a0we left it to a professional\u00a0&#8211;\u00a0yet I know that he\u00a0used \u00a0the spectrograms to isolate animal species and split each track into sets of\u00a0tracks by frequencies. the sound frequencies are distributed like the instruments in an orchestra, as seen from above, the <em>slices<\/em> or <strong>partitions<\/strong> represent given sets of frequencies corresponding to differents sets of animals vocalizations that in turn correspond to sets of instruments in an actual orchestra (the intro video explains this quite clearly).<\/p>\n<p>here&#8217;s how the frequencies are distributed\u00a0in the orchestra, each type of animal occupies a given space or as Bernie Krause puts it, an &#8220;acoustic niche&#8221;.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-907\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/layout.png\" alt=\"layout\" width=\"1079\" height=\"607\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/layout.png 1079w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/layout-300x169.png 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/layout-768x432.png 768w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/layout-1024x576.png 1024w\" sizes=\"(max-width: 1079px) 100vw, 1079px\" \/><\/p>\n<p>The mobile version uses the WebAudio API (WAA) to get slightly better playback synchronisation but the desktop uses regular Audio tags to remain compatible with older browsers. The WAA\u00a0comes at a cost as each audio track must be somehow\u00a0<em>decompressed<\/em>\u00a0once loaded.\u00a0As all we needed\u00a0was crossfades, there was little reason to use the WAA.\u00a0The rest is a fairly regular multitrack playback &#8220;engine&#8221;, with multitrack resync\u00a0and a selective batch\u00a0crossfade.<\/p>\n<p>For a better balance\u00a0of the tracks, I came to lower the overall volume of all\u00a0track\u00a0and have\u00a0the ability\u00a0to set each track&#8217;s volume separately, the JSON looked like:<\/p>\n<pre class=\"\">\"volumes\" : [\r\n  { \"default\": 0.715, \"hover\":1.000, \"out\": 0.25 },\r\n  { \"default\": 0.715, \"hover\":1.000, \"out\": 0.25 },\r\n  { \"default\": 0.715, \"hover\":0.858, \"out\": 0.25 },\r\n  { \"default\": 0.715, \"hover\":0.715, \"out\": 0.25 },\r\n  { \"default\": 0.715, \"hover\":0.715, \"out\": 0.25 },\r\n  { \"default\": 0.715, \"hover\":0.715, \"out\": 0.25 }\r\n],<\/pre>\n<p>the &#8220;default&#8221; state volume is not 1 but 0.715 which roughly corresponds to a global volume turned down by 40%.\u00a0The &#8220;hover&#8221; volume\u00a0emphasizes individual\u00a0tracks on rollover (or tap), some are so low that they needed to be amplified up to 140% which corresponds to a volume of 1. Apart from this, all I did was to use <a href=\"https:\/\/github.com\/goldfire\/howler.js\/\" target=\"_blank\">Howler<\/a>\u00a0and Circular easeIn \/ easeOut crossfades as they sound more natural than linear easings.<\/p>\n<h2>Graphics<\/h2>\n<p>At first sight it may not seem like much as there is no fancy 3D graphics or rock&#8217;n roll animations but it&#8217;s one of the most complex websites I had to work on. I&#8217;ll\u00a0review some of the solutions that were adopted so that the app works as well as possible on various platforms.<\/p>\n<p>Below is one of the original designs, one would call it a <em>simple<\/em> setup yet if you look carefully, many\u00a0different <em>layers<\/em>\u00a0build up the final image.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-827\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/design.jpg\" alt=\"design\" width=\"1548\" height=\"1050\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/design.jpg 1548w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/design-300x203.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/design-768x521.jpg 768w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/design-1024x695.jpg 1024w\" sizes=\"(max-width: 1548px) 100vw, 1548px\" \/><\/p>\n<p>A gradient in the background that appears in the central disc, a blue-ish grain, a drop shadow to emphasize the central disc, bright semi-transparent outlines, \u00a0a colored halo at the bottom of the page to emphasize the playback controls, some colored halos where it&#8217;s hard to\u00a0tell whether they&#8217;re part of the foreground or\u00a0the background, a greyscale, semi transparent image in the background and some subtle hues variations and overlays of sprites with drop shadows. All of which contribute to the elegance of the final picture.<\/p>\n<p>As usual, before starting the production and in order to obtain an\u00a0identical\u00a0cross-browser \/ cross platforms render of the visual, I decided to perform some R&amp;D.<\/p>\n<h3>in the browsers&#8217; jungle<\/h3>\n<p>My first thought was to use SVG as <a href=\"http:\/\/caniuse.com\/#search=SVG\" target=\"_blank\">it is well supported across browsers<\/a>\u00a0and allows for blend modes and other filters (desaturation, blur, shadows) that were present here and there in the original designs.\u00a0here&#8217;s what my\u00a0test page looked like on 4 different browsers (left to right, top bottom:\u00a0FireFox, Edge, Chrome &amp;\u00a0Internet Explorer 11, PC, Windows 10 ).<\/p>\n<p><a href=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/Capture-d\u00e9cran-2016-03-18-12.04.00.png\" target=\"_blank\" data-rel=\"lightbox-image-0\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-826 size-full\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/Capture-d\u00e9cran-2016-03-18-12.04.00.png\" alt=\"Capture d'\u00e9cran 2016-03-18 12.04.00\" width=\"1920\" height=\"1080\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/Capture-d\u00e9cran-2016-03-18-12.04.00.png 1920w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/Capture-d\u00e9cran-2016-03-18-12.04.00-300x169.png 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/Capture-d\u00e9cran-2016-03-18-12.04.00-768x432.png 768w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/Capture-d\u00e9cran-2016-03-18-12.04.00-1024x576.png 1024w\" sizes=\"(max-width: 1920px) 100vw, 1920px\" \/><\/a><\/p>\n<p>It&#8217;s easy to spot some of the differences but also to miss some\u00a0of them, here&#8217;s a list from the most obvious to the most subtle:<\/p>\n<ul>\n<li style=\"font-weight: 400;\">the rendering of the blue animals pictograms, they use an &#8220;additive\u00a0blendmode&#8221;, from a browser to the next the behaviour changes quite badly ; sometimes they&#8217;ll blend nicely, sometimes they&#8217;re opaque, sometimes it&#8217;s a bit of each (FF, top left). In addition the vector shapes themselves are not rendered correctly (top left duck).<\/li>\n<li style=\"font-weight: 400;\">the color gradient disc, same as above, the blendmode is supported in various ways depending on the browser. On IE it becomes very dull and basically brown, on FF, the opacity is not properly computed and the gradient covers its background too much.<\/li>\n<li style=\"font-weight: 400;\">the semi transparent disc at the back should behave &#8220;normally&#8221; as\u00a0no blendmode is applied to it, halas, the bottom right version is slightly lighter than the others (probably due to the additive overly being actually computed as opposed to the others).<\/li>\n<li style=\"font-weight: 400;\">the first line of pictograms uses an &#8220;effect&#8221; ; I drew them with a texture supposed to mimic a watercolor. Surprisingly, the result was pretty close\u00a0among\u00a0browsers yet all browsers but IE\u00a0(bottom right) tend to make the edges crispy and pixelated.<\/li>\n<li style=\"font-weight: 400;\">the cat&#8217;s &#8220;slices&#8221; (the geometric spiral) does not behave the same, on IE &amp; Edge (right column), some slices simply disappeared (near the circle&#8217;s center top).<\/li>\n<li style=\"font-weight: 400;\">The cat picture is a color picture, desaturated on the fly and again there are some variations as to how the conversion is done.<\/li>\n<\/ul>\n<p>Multiplied by the number of browsers, by the number of devices and by the number of OSes, there were way too many differences and unknown factors as to how the visuals\u00a0would be rendered.<\/p>\n<p>We had to rework the pages designs practically from scratch, which pushed the\u00a0art directors out of (and quite far away from) their comfort zone.\u00a0They reworked all the designs without using blendmodes at all and using no other &#8220;effect&#8221; than opacity variations. Somehow,\u00a0this\u00a0is a very <em>pure<\/em> way of building the visuals which suited the website&#8217;s purpose quite well ; it would be like using only natural pigments in an artwork.<\/p>\n<h3>layers. lots of them.<\/h3>\n<p>With a\u00a0naive approach, it took\u00a0about 20+ layers to build the final visual. After regrouping some draw calls into batches, <em>only<\/em> 7 to 8 remained, namely:<\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: center;\">1\u00a0background<\/p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-839\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/0.jpg\" alt=\"0\" width=\"800\" height=\"550\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/0.jpg 800w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/0-300x206.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/0-768x528.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/p>\n<p style=\"text-align: center;\">2\u00a0color grain<\/p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-840\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/1.jpg\" alt=\"1\" width=\"800\" height=\"550\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/1.jpg 800w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/1-300x206.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/1-768x528.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/p>\n<p style=\"text-align: center;\">3\u00a0desaturated semi transparent satellite view<\/p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-841\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/2.jpg\" alt=\"2\" width=\"800\" height=\"550\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/2.jpg 800w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/2-300x206.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/2-768x528.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/p>\n<p style=\"text-align: center;\">4\u00a0color halos<\/p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-842\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/3.jpg\" alt=\"3\" width=\"800\" height=\"550\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/3.jpg 800w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/3-300x206.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/3-768x528.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/p>\n<p style=\"text-align: center;\">5 abstract\u00a0pictograms<\/p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-843\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/4.jpg\" alt=\"4\" width=\"800\" height=\"550\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/4.jpg 800w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/4-300x206.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/4-768x528.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/p>\n<p style=\"text-align: center;\">6 central disc<\/p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-844\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/5.jpg\" alt=\"5\" width=\"800\" height=\"550\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/5.jpg 800w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/5-300x206.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/5-768x528.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/p>\n<p style=\"text-align: center;\">7 footer color gradient<\/p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-845\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/6.jpg\" alt=\"6\" width=\"800\" height=\"550\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/6.jpg 800w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/6-300x206.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/6-768x528.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/p>\n<p style=\"text-align: center;\">+<\/p>\n<p style=\"text-align: center;\">8 potentially\u00a0an overlay<\/p>\n<p>this\u00a0GIF\u00a0sums up how the image is built.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-831\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/layers.gif\" alt=\"layers\" width=\"800\" height=\"550\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>Further improvements included:<\/p>\n<ul>\n<li>each vector based layer is drawn (background and colors halos mostly) is rendered at a 16th of its display scale, the bilinear interpolation is performed by the hardware and preserves the smoothness of the gradients while sparing some resources<\/li>\n<li>the last pass of the color halos\u00a0is a grain pattern, it is drawn at full resolution instead of a 16th\u00a0to prevent artefacts<\/li>\n<li>when a <em>picture<\/em>\u00a0needs to be drawn (halo layer or overlays), it&#8217;s written directly on the final canvas at full scale<\/li>\n<li>the grain are small (256\u00b2 pixels) canvases used as patterns by the context<\/li>\n<li>every layer is maintained and updated offscreen<\/li>\n<li>all\u00a0layer are\u00a0blitted to a single canvas<\/li>\n<\/ul>\n<p>Despite the efforts we made to get it to work as fast as possible on mobile devices (mostly downgrading the visual quality by removing layers), the website will work best on desktop.<\/p>\n<p>On a side note, I discovered that an image pattern (ctx.createPattern)\u00a0created on a context\u00a0could be used on another context\u00a0which is handy when you want to share resources. Also, the gradient patterns are very cheap to setup (fractions of a millisecond), this was good news for what follows.<\/p>\n<h3>gradients. lots of them.<\/h3>\n<p>In the above example, the color halos layer is by far the most complex ; it can be made of up to 12 different gradients, each of which can be made of 3 to 9 color &amp;\u00a0alpha steps and can contain image overlays.\u00a0Overmore, each module offers 3 variations of these settings.<\/p>\n<p>Instead of importing rastered images of the gradients, we chose to encode the gradients in JSON files and to redraw them on fly. This way we could easily perform color transitions between the different &#8220;times&#8221;\u00a0of each module.<\/p>\n<p>Here&#8217;s a sample of the JSON\u00a0describing the background, the color halos, the overlays and the footer gradient of the second module (<strong>[&#8230;]<\/strong> means there&#8217;s more).<\/p>\n<pre class=\"lang:js decode:true\">\"backgroundColors\" : [\r\n\u00a0{\r\n\u00a0\u00a0\u00a0\"name\":\"m2_yellow_blue_bg_2\",\r\n\u00a0\u00a0\u00a0\"type\" : \"linear\",\r\n\u00a0\u00a0\u00a0\"angle\" : 120,\r\n\u00a0\u00a0\u00a0\"colorsteps\" : [\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0, \"r\": 45, \"g\": 90, \"b\": 119, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.25, \"r\": 45, \"g\": 90, \"b\": 119, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.51, \"r\": 103, \"g\": 128, \"b\": 156, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.73, \"r\": 181, \"g\": 158, \"b\": 99, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":1, \"r\": 181, \"g\": 158, \"b\": 99, \"a\": 1 }\r\n\u00a0\u00a0\u00a0],\r\n\u00a0\u00a0\u00a0\"alphas\": [ 1, 0, 0 ]\r\n\u00a0},\r\n\u00a0[...]\r\n\u00a0{\r\n\u00a0\u00a0\u00a0\"name\":\"m2_dk_nd_lg_multi_bg_1\",\r\n\u00a0\u00a0\u00a0\"type\" : \"linear\",\r\n\u00a0\u00a0\u00a0\"angle\" : 105,\r\n\u00a0\u00a0\u00a0\"colorsteps\" : [\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0, \"r\": 98, \"g\": 94, \"b\": 0, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.19, \"r\": 98, \"g\": 94, \"b\": 0, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.38, \"r\": 28, \"g\": 19, \"b\": 0, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.45, \"r\": 45, \"g\": 35, \"b\": 0, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.52, \"r\": 45, \"g\": 35, \"b\": 0, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.59, \"r\": 202, \"g\": 94, \"b\": 44, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.63, \"r\": 255, \"g\": 66, \"b\": 46, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.88, \"r\": 11, \"g\": 10, \"b\": 10, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":1, \"r\": 11, \"g\": 10, \"b\": 10, \"a\": 1 }\r\n\u00a0\u00a0\u00a0],\r\n\u00a0\u00a0\u00a0\"alphas\": [ 0, 1, 1 ]\r\n\u00a0}\r\n],\r\n\"halos\" : [\r\n\u00a0{\r\n\u00a0\u00a0\u00a0\"name\":\"m2_blue_white_red_hl_3\",\r\n\u00a0\u00a0\u00a0\"type\" : \"linear\",\r\n\u00a0\u00a0\u00a0\"angle\" : -120,\r\n\u00a0\u00a0\u00a0\"colorsteps\" : [\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0, \"r\": 130, \"g\": 102, \"b\": 95, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.10, \"r\": 130, \"g\": 102, \"b\": 95, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.30, \"r\": 117, \"g\": 115, \"b\": 125, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.51, \"r\": 103, \"g\": 128, \"b\": 156, \"a\": 0 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.68, \"r\": 68, \"g\": 70, \"b\": 103, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.81, \"r\": 218, \"g\": 171, \"b\": 254, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":1, \"r\": 218, \"g\": 171, \"b\": 254, \"a\": 1 }\r\n\u00a0\u00a0\u00a0],\r\n\u00a0\u00a0\u00a0\"alphas\" : \u00a0[ 0.79, 0, 0 ],\r\n\u00a0\u00a0\u00a0\"time\":0\r\n\u00a0},\r\n\u00a0[...]\r\n\u00a0{\r\n\u00a0\u00a0\u00a0\"name\":\"m2_multi_hl_7\",\r\n\u00a0\u00a0\u00a0\"type\" : \"linear\",\r\n\u00a0\u00a0\u00a0\"angle\" : -117,\r\n\u00a0\u00a0\u00a0\"colorsteps\" : [\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0, \"r\": 88, \"g\": 41, \"b\": 0, \"a\": 0 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.12, \"r\": 88, \"g\": 41, \"b\": 0, \"a\": 0 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.21, \"r\": 0, \"g\": 0, \"b\": 0, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.25, \"r\": 118, \"g\": 44, \"b\": 19, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.30, \"r\": 125, \"g\": 32, \"b\": 0, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.51, \"r\": 88, \"g\": 41, \"b\": 0, \"a\": 0 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.75, \"r\": 180, \"g\": 153, \"b\": 72, \"a\": 0.50 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.80, \"r\": 180, \"g\": 153, \"b\": 72, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":1, \"r\": 180, \"g\": 153, \"b\": 72, \"a\": 1 }\r\n\u00a0\u00a0\u00a0],\r\n\u00a0\u00a0\u00a0\"alphas\": [ 0, 0.93, 0.93 ]\r\n\u00a0},\r\n\u00a0{\r\n\u00a0\u00a0\u00a0\"name\":\"m2_black_hl14\",\r\n\u00a0\u00a0\u00a0\"type\" : \"linear\",\r\n\u00a0\u00a0\u00a0\"angle\" : 90,\r\n\u00a0\u00a0\u00a0\"colorsteps\" : [\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0, \"r\": 0, \"g\": 0, \"b\": 0, \"a\": 0 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.40, \"r\": 0, \"g\": 0, \"b\": 0, \"a\": 0 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":0.63, \"r\": 0, \"g\": 0, \"b\": 0, \"a\": 1 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0{ \"step\":1, \"r\": 255, \"g\": 255, \"b\": 255, \"a\": 0 }\r\n\u00a0\u00a0\u00a0],\r\n\u00a0\u00a0\u00a0\"alphas\": [ 0.25, 0, 0 ]\r\n\u00a0}\r\n\r\n],\r\n\r\n\"overlays\" : [\r\n\u00a0{\r\n\u00a0\u00a0\u00a0\"name\":\"m3_vignette_effect_ol_15\",\r\n\u00a0\u00a0\u00a0\"type\" : \"img\",\r\n\u00a0\u00a0\u00a0\"url\": \"assets\/img\/modules\/module2\/m2_vignette_effect_ol_15.png\",\r\n\u00a0\u00a0\u00a0\"alphas\": [ 1, 1, 1 ]\r\n\u00a0},\r\n\u00a0{\r\n\u00a0\u00a0\u00a0\"name\":\"m3_vignette_effect_ol_16\",\r\n\u00a0\u00a0\u00a0\"url\": \"assets\/img\/modules\/module2\/m2_vignette_effect_ol_16.png\",\r\n\u00a0\u00a0\u00a0\"type\" : \"img\",\r\n\u00a0\u00a0\u00a0\"alphas\": [ 0.26, 0, 0 ]\r\n\u00a0}\r\n],\r\n\r\n\"haloFooter\" : [\r\n\u00a0[ \"#3c1b2c\", \"#fd6c64\", \"#fd6c64\", \"#3c1b2c\" ],\r\n\u00a0[ \"#4f1c1e\", \"#ec905a\", \"#ec905a\", \"#4f1c1e\" ],\r\n\u00a0[ \"#202020\", \"#4f1c1e\", \"#4f1c1e\", \"#202020\" ]\r\n],<\/pre>\n<p>This verbose format\u00a0is quite far from\u00a0art direction but allowed us to fine tune some settings directly\u00a0in the browsers. It was nice to have graphists and art directors who played the game ; for once we were using the target media to approach the original design instead of trying to reproduce Photoshop files in the browser.<\/p>\n<p>This tedious,\u00a0careful and\u00a0time-consuming\u00a0work allowed us \u00a0to stay as close\u00a0as possible to the original designs, on all platforms.<\/p>\n<h2>Animation<\/h2>\n<p>From the very beginning, I knew there would be a great deal of animations, the abstract shapes representing the animals would have to &#8220;live&#8221;.\u00a0To achieve this we used various techniques ranging from traditional to procedural animation.<\/p>\n<h3>SVG?\u00a0NOPE!<\/h3>\n<p>As mentioned above, my first tests were done in SVG. theoretically, SVG is well supported and allows for both rich rendering and provide unified animation system. Now that&#8217;s the\u00a0<em>theory<\/em>&#8230;<\/p>\n<p>in practice it&#8217;s slugishly slow, even on the finest browsers, the filters \/ features are vendor dependant, some simply do not exist or do not work. Even though the shapes are supposed to be cleverly handled in memory (at least for translations and rotations).<\/p>\n<p>We needed a lot of dynamic shapes from the\u00a0&#8220;partitions&#8221; of the home page to each and every gradient that are constantly animated in the background. So for this specific use case, the SVG was not the appropriate tool and I chose to <em>blit<\/em> a canvas, like cool cats do.<\/p>\n<p>Many a time,\u00a0I dreamt of having the right to use a WebGL context because you know&#8230; WebGL&#8230;<\/p>\n<h3>respecting the media<\/h3>\n<p>The sound is crucial here, so in order not to\u00a0<em>misrepresent<\/em> the sound material and on the contrary underline its\u00a0qualities, we chose a traditional approach based on timelines. In After Effects, we created a\u00a0timeline for each family of abstract symbol of each\u00a0soundtrack\u00a0of each module. here&#8217;s what a composition looked like:<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-875\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/ae_layout.jpg\" alt=\"ae_layout\" width=\"1920\" height=\"1041\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/ae_layout.jpg 1920w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/ae_layout-300x163.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/ae_layout-768x416.jpg 768w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/ae_layout-1024x555.jpg 1024w\" sizes=\"(max-width: 1920px) 100vw, 1920px\" \/><\/p>\n<p>the grey shapes represent the individual species found\u00a0in each soundtrack, the orange shapes are sound tracks that contain different species. Each red square is a placeholder that receives transformations over time. there are 4 types of transforms that can be stored and reflected at runtime:<\/p>\n<ul>\n<li style=\"font-weight: 400;\">position: describes an XY <em>offset<\/em> around the\u00a0origin rather than absolute coordinates<\/li>\n<li style=\"font-weight: 400;\">rotation: describes the min\/max rotations at a given time<\/li>\n<li style=\"font-weight: 400;\">scale: same for scale on the 2 axes<\/li>\n<li style=\"font-weight: 400;\">opacity: used to trigger the sprites playback<\/li>\n<\/ul>\n<p>When the timelines are ready, we use a script to collect all the keyframes of each timeline and store them as a JSON file. <a href=\"https:\/\/gist.github.com\/nicoptere\/6198923eb1a8803ae7cea45cd4145219\" target=\"_blank\">The After Effects keyframes&#8217; export script is available here<\/a> if you&#8217;re interested.<\/p>\n<p>After having done a prototype, we agreed this was the way to go and I started creating the timelines for the module 1 (6 tracks) manually. Quickly enough, it appeared that\u00a0some soundtracks contained way too many events to be processed manually. So I tried a procedural approach\u00a0based on the sound&#8217;s spectrogram ;\u00a0that&#8217;s how Bernie Krause himself isolates the different species inside a sound.<\/p>\n<p>below is an example of a track automatically analysed:<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-857\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/spectrogram.jpg\" alt=\"spectrogram\" width=\"1068\" height=\"915\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/spectrogram.jpg 1068w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/spectrogram-300x257.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/spectrogram-768x658.jpg 768w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/spectrogram-1024x877.jpg 1024w\" sizes=\"(max-width: 1068px) 100vw, 1068px\" \/><\/p>\n<p>the top picture is a &#8220;scan&#8221; of the soundtrack, the vertical grey lines represent the seconds, the greyish cloud at the bottom is the spectrogram. the colored lines\u00a0represent various &#8220;events&#8221; extracted from the sound, they depend on arbitrary thresholds:\u00a0yellow lines represent the insects, red\u00a0lines represent the birds. the bottom graph\u00a0is a smoothed representation of the series of events.<\/p>\n<p>As magical as this may seem, we\u00a0didn&#8217;t use this technique. For one, the thresholds have to be set up manually, empirically but mostly\u00a0the results were too <em>mechanical<\/em>, some <em>clearly audible<\/em> events were not detected while other minor events were given way too much room.<\/p>\n<p>Back to After EFfects\u00a0and manual work, here&#8217;s a sample of the timelines of the 1st module\u00a0(there is 4 times more keys)<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-855\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/module1.1.png\" alt=\"module1.1\" width=\"2594\" height=\"1419\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/module1.1.png 2594w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/module1.1-300x164.png 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/module1.1-768x420.png 768w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/module1.1-1024x560.png 1024w\" sizes=\"(max-width: 2594px) 100vw, 2594px\" \/><\/p>\n<p>or the 4th module\u00a0(three times more keys in total)<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-856\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/module4.1.png\" alt=\"module4.1\" width=\"2594\" height=\"1419\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/module4.1.png 2594w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/module4.1-300x164.png 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/module4.1-768x420.png 768w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/module4.1-1024x560.png 1024w\" sizes=\"(max-width: 2594px) 100vw, 2594px\" \/><\/p>\n<p>This tedious, precise and careful work allowed us to remain very close to what is actually <em>felt<\/em> when listening to the tracks. When an animal shouts or whispers, there is a visual response.<\/p>\n<h3>colossal masses<\/h3>\n<p>The timelines allow us to control precisely each &#8220;family&#8221; of shapes, to trigger discrete and\/or continuous events at any given time on one or more dimensions (position, rotation, scale and timelines). As each family is represented by many individuals, we had to find a way to batch process them rather than controlling them individually.<\/p>\n<p>My first idea was to define polygonal zones corresponding to the actual frequencies distributions of the various species and distribute the individuals\u00a0at random, using a rough &#8220;polygon contains&#8221; method and a PRNG (so that we obtain the same distributions every time). Again this produced unwanted artefacts like superimposition and disgraceful distributions like the one below.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-880\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/distribution.jpg\" alt=\"distribution\" width=\"1200\" height=\"970\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/distribution.jpg 1200w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/distribution-300x243.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/distribution-768x621.jpg 768w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/distribution-1024x828.jpg 1024w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/p>\n<p>there are ways to prevent superimposition (a Poisson Disc Distribution for instance) yet\u00a0I don&#8217;t know of any algorithm to make &#8220;good looking&#8221; distributions. So for each module, we manually created the initial distributions in Illustrator by using symbols and transforming them to match the artworks distribution. Then we exported the result to Flash where we collected all the transform matrices of both the sprites and their shadows.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-859\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/flash.jpg\" alt=\"flash\" width=\"1920\" height=\"1038\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/flash.jpg 1920w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/flash-300x162.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/flash-768x415.jpg 768w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/flash-1024x554.jpg 1024w\" sizes=\"(max-width: 1920px) 100vw, 1920px\" \/><\/p>\n<p>In yellow, the\u00a0pictograms rendered in white, in red, their shadows.<\/p>\n<p>this is the script used to collect all the transforms matrices and store them as <em>a,b,c,d,tx,ty<\/em> values.<\/p>\n<pre class=\"\">import flash.display.MovieClip;\r\nimport flash.events.Event;\r\nimport flash.geom.Matrix;\r\n\r\n\/\/collects all clips transforms\r\nvar frames:Object = {};\r\nfor( var i:int = 0; i&lt; this.numChildren; i++ )\r\n{\r\n    var mc:DisplayObject = this.getChildAt( i );\r\n    var n:String = mc.name;\r\n    if( n.lastIndexOf( \"instance\" ) != -1 )continue;\r\n    frames[n] = [];\r\n}\r\n\r\nvar scope:MainTimeline = this;\r\nfunction oef(e:Event):void\r\n{\r\n    for( var i:int = 0; i&lt; this.numChildren; i++ )\r\n    {\r\n        var mc:DisplayObject = this.getChildAt( i );\r\n        var n:String = mc.name;\r\n        if( n.lastIndexOf( \"instance\" ) != -1 )continue;\r\n        var mat:Matrix = mc.transform.matrix;\r\n        frames[n].push( mat.a, mat.b, mat.c, mat.d, mat.tx, mat.ty );\r\n    }\r\n    var precision:Number = 10000;\r\n    var str:String = '\"transforms\" : {\\n';\r\n    for( var key:String in frames )\r\n    {\r\n        str += '\"' + key + '\" : [';\r\n        var values:Array = frames[ key ];\r\n        for( i = 0; i &lt; values.length; i++ )\r\n        {\r\n            str += Math.round( Number( values[i] ) * precision ) \/ precision+ ',';\r\n        }\r\n        str = str.substr( 0, str.length - 1 );\r\n        str += '],\\n';\r\n    }\r\n    str = str.substr( 0, str.length - 2 ) + '\\n}\\n' ;\r\n    trace( str );\r\n\r\n    this.removeEventListener( Event.ENTER_FRAME, oef );\r\n    stop();\r\n}\r\nthis.addEventListener( Event.ENTER_FRAME, oef );<\/pre>\n<p>The benefit was that we could also use powerful shape animation features along with the &#8220;spritesheet export&#8221; of Flash\u00a0which provided a solid pipeline for\u00a0assets creation. On a side note,\u00a0Flash, like most Adobe products export UTF<strong>-16<\/strong> encoded JSONs, it&#8217;s ok\u00a0to use them on most browsers but Firefox says NO, this was an extra clean up step\u00a0we regularly forgot about.<\/p>\n<p>We obtained a compact format to describe many clips&#8217; transforms\u00a0(for instance, there are 281 clips on the module 1)<\/p>\n<pre class=\"lang:js decode:true\">\"transforms\" : {\r\n\u00a0\u00a0\"t6_insects\" : [-0.1025,-0.0417,0.1109,-0.2727,1157.45,443.5,-0.0985,0.0505,-0. [...]\r\n\u00a0\u00a0\"t5_insects_shdw\" : [0.0041,0.3295,-0.7869,0.0098,1010.05,377.9,-0.281,0.248,-0 [...]\r\n\u00a0\u00a0\"t4_birds2\" : [0.3489,-0.251,-0.2901,-0.4032,860.65,152.75,0.3799,-0.2009,0.255 [...]\r\n\u00a0\u00a0\"t2_frogs_shdw\" : [1,0,0,1,240.3,469.3,0.9405,0,0,0.8254,289.55,483.3,-0.8718,0 [...]\r\n\u00a0\u00a0\"t4_birds1\" : [-0.3702,-0.0634,-0.0926,0.5413,910.6,263.75,0.3217,0.0551,-0.100 [...]\r\n\u00a0\u00a0\"t6_insects_shdw\" : [-0.1025,-0.0417,0.1109,-0.2727,1152.45,448.5,-0.0985,0.050 [...]\r\n\u00a0\u00a0\"t2_mammal_shdw\" : [1,0,0,1,370.7,511.05,0.6059,-0.4201,0.4201,0.6059,297,548.5 [...]\r\n\u00a0\u00a0\"t2_wind_shdw\" : [0.506,-0.1745,0.3388,0.9826,179.8,401.85,0.6263,-0.0018,0.001 [...]\r\n\u00a0\u00a0\"t6_small_insects_shdw\" : [-0.7581,0,0,-0.7581,1039.35,801.65,0.6071,0,0,0.6071 [...]\r\n\u00a0\u00a0\"t1_wind_shdw\" : [0.9481,-0.3181,0.3181,0.9481,293.4,736.2,0.7138,-0.1925,0.194 [...]\r\n\u00a0\u00a0\"t4_birds1_shdw\" : [-0.3702,-0.0634,-0.0926,0.5413,905.6,268.75,0.3217,0.0551,- [...]\r\n\u00a0\u00a0\"t6_small_insects\" : [-0.7581,0,0,-0.7581,1044.35,796.65,0.6071,0,0,0.6071,1155 [...]\r\n\u00a0\u00a0\"t4_birds3_shdw\" : [0.2767,0.0008,-0.0017,0.5529,881.6,172.05,-0.1937,0.1754,0. [...]\r\n\u00a0\u00a0\"t4_birds2_shdw\" : [0.3489,-0.251,-0.2901,-0.4032,855.65,157.75,0.3799,-0.2009, [...]\r\n\u00a0\u00a0\"t3_birds3\" : [0.394,-0.0695,0.1543,0.8752,702.65,187.65,0.3414,0.3206,-0.5794, [...]\r\n\u00a0\u00a0\"t2_mammal\" : [1,0,0,1,375.7,506.05,0.6059,-0.4201,0.4201,0.6059,302,543.55,0.5 [...]\r\n\u00a0\u00a0\"t3_birds2\" : [0.8214,0.2414,-0.2414,0.8214,508.8,65.75,0.5126,-0.0298,0.0514,0 [...]\r\n\u00a0\u00a0\"t3_birds1_shdw\" : [0.7409,-0.4748,-0.5083,-0.7932,463.4,180.55,1.0013,0.2431,0 [...]\r\n\u00a0\u00a0\"t3_birds1\" : [0.7409,-0.4748,-0.5083,-0.7932,468.4,175.55,1.0013,0.2431,0.0549 [...]\r\n\u00a0\u00a0\"t2_wind\" : [0.506,-0.1745,0.3388,0.9826,184.8,396.85,0.6263,-0.0018,0.0018,0.6 [...]\r\n\u00a0\u00a0\"t3_birds3_shdw\" : [0.394,-0.0695,0.1543,0.8752,697.65,192.65,0.3414,0.3206,-0. [...]\r\n\u00a0\u00a0\"t4_insects\" : [0.3238,0.0609,-0.0722,0.3842,797.1,316.85,0.0947,0.183,-0.3472, [...]\r\n\u00a0\u00a0\"t6_bat\" : [-0.0287,-0.1662,-0.8116,0.1403,934.75,852.55,-0.1158,-0.1908,-0.397 [...]\r\n\u00a0\u00a0\"t6_bat_shdw\" : [-0.0287,-0.1662,-0.8116,0.1403,929.75,857.55,-0.1158,-0.1908,- [...]\r\n\u00a0\u00a0\"t3_birds2_shdw\" : [0.8214,0.2414,-0.2414,0.8214,503.8,70.75,0.5126,-0.0298,0.0 [...]\r\n\u00a0\u00a0\"t4_insects_shdw\" : [0.3238,0.0609,-0.0722,0.3842,792.1,321.85,0.0947,0.183,-0. [...]\r\n\u00a0\u00a0\"t4_birds3\" : [0.2767,0.0008,-0.0017,0.5529,886.6,167.05,-0.1937,0.1754,0.1927, [...]\r\n\u00a0\u00a0\"t5_insects\" : [0.0041,0.3295,-0.7869,0.0098,1015.05,372.9,-0.281,0.248,-0.453, [...]\r\n\u00a0\u00a0\"t1_wind\" : [0.9481,-0.3181,0.3181,0.9481,298.4,731.2,0.7138,-0.1925,0.1944,0.7 [...]\r\n\u00a0\u00a0\"t2_frogs\" : [1,0,0,1,245.3,464.3,0.9405,0,0,0.8254,294.55,478.3,-0.8718,0,0,-0 [...]\r\n},<\/pre>\n<p>Each individual is represented by 6 digits, not only does this format allow to store the transforms (position, rotation, scale) in a compact way but it also allows to render them faster as it is very close to the way the canvas 2D works. The following is a snippet from\u00a0the render loop:<\/p>\n<pre class=\"lang:js decode:true\">\/\/save the context's state\r\nctx.save();\r\n\r\n\/\/apply this clip's transform from the 6 digits\r\nctx.setTransform( p.a, p.b, p.c, p.d, p.x, p.y );\r\n\r\n\/\/blits the sprite onto the canvas\r\n\/\/\"re\" holds the sprite's coordinates on the spritesheet\r\nctx.drawImage( scope.spritesheet,\r\n    re.x, re.y, re.w, re.h,\r\n    -re.w \/ 2, -re.h \/ 2, re.w, re.h\r\n);\r\n\/\/get ready for the next object\r\nctx.restore();<\/pre>\n<p>Even so, the high number of elements to draw was causing poor performance on\u00a0older hardware and &#8211; of course &#8211; mobile devices so we opted for a &#8220;dynamic decimation&#8221; strategy based on the user&#8217;s screen resolution. The bigger the screen, the more numerous the individuals, the smaller the screen the fewer, consider the following screenshots:<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-861\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/desktop.jpg\" alt=\"desktop\" width=\"800\" height=\"514\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/desktop.jpg 800w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/desktop-300x193.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/desktop-768x493.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/> <img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-862\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/ipad.jpg\" alt=\"ipad\" width=\"800\" height=\"514\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/ipad.jpg 800w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/ipad-300x193.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/ipad-768x493.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-863\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/iphone.jpg\" alt=\"iphone\" width=\"800\" height=\"514\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/iphone.jpg 800w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/iphone-300x193.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/iphone-768x493.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/p>\n<p>I was really sad not to be able to do better than this on mobile devices but the\u00a0framerate dropped so low that we didn&#8217;t have a choice.<\/p>\n<p>This &#8220;dynamic decimation&#8221; feature is also used intentionally in the third module to represent the &#8211; actual &#8211; decimation of animal species in the Lincoln Meadow.<\/p>\n<h3>a\u00a0touch of\u00a0procedural animation<\/h3>\n<p>To add some extra life to the pictograms, we&#8217;ve added a touch of code. In order for the playback of timeline animations not to be too mechanical, we introduced some randomness ; when an event collected from After Effects is triggered, all the members of a family play it with a little time offset, this is\u00a0very visible on the bigger sprites and gives a sensation of uniqueness.<\/p>\n<p>To get a more natural motion, we&#8217;ve added a &#8220;motion noise&#8221; ; a\u00a0constant\u00a0infra-noise based on vector fields where each sprite would move according to its weight (based on the surface of the sprite).\u00a0The texture used to create the vector field provides <em>various<\/em>\u00a0qualities of motion. A visual explanation might be simpler to grasp ; imagine that the following pictures are satellite views where mountains are white and the valleys are dark.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-865\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/noise1.jpg\" alt=\"noise1\" width=\"512\" height=\"512\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/noise1.jpg 512w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/noise1-150x150.jpg 150w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/noise1-300x300.jpg 300w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/p>\n<p>the above would be sharp mountains\u00a0and below would be eroded volcanoes.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-866\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/noise2.jpg\" alt=\"noise2\" width=\"512\" height=\"512\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/noise2.jpg 300w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/noise2-150x150.jpg 150w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/p>\n<p>Now imagine that each animal constantly goes up and down this landscape, their speed will vary depending on the slope and their\u00a0weight. That&#8217;s exactly\u00a0how the <em>motion noise<\/em> is computed.\u00a0We also added a mouse interaction ; the animals will try to flee when the user clicks\u00a0and\u00a0with this addition to the\u00a0animations, the path they use will be slightly different when they flee and when they come back to their original position.<\/p>\n<p>computing the vector field is straight forward, it&#8217;s about getting the central difference of each pixels and is done as follow:<\/p>\n<pre class=\"lang:js decode:true\">function getPixel( data, width, x, y )\r\n{\r\n    var id = ( x + width * y ) * 4;\r\n    var r = data[id];\r\n    var g = data[id+1];\r\n    var b = data[id+2];\r\n    var a = data[id+3];\r\n    return {r:r, g:g, b:b, a:a }\r\n}\r\n\r\nvar iw = img.width;\r\nvar ih = img.height;\r\nvar src = src == null ? getContext(iw, ih) : src;\r\nsrc.canvas.width = iw;\r\nsrc.canvas.height = ih;\r\nsrc.drawImage(img, 0, 0, iw, ih);\r\n\r\nvar data = new Float32Array( iw * ih * 2 );\r\nvar imgData = src.getImageData(0,0, iw, ih);\r\nvar dat = imgData.data;\r\nvar i = dat.length;\r\nvar did = 0;\r\nwhile( i-=4 ){\r\n\r\n    var id = i\/4;\r\n    var px = id % iw;\r\n    var py = ~~( id \/ iw );\r\n\r\n    var xl = getPixel( dat, iw, Math.max( 0,px-1 ), py ).r;\r\n    var xr = getPixel( dat, iw, Math.min( iw-1, px+1 ), py ).r;\r\n    var yt = getPixel( dat, iw, px, Math.max( 0, py-1 ) ).r;\r\n    var yb = getPixel( dat, iw, px, Math.min( iw-1, py+1 ) ).r;\r\n\r\n    data[ did++ ] = ( xl - xr ) \/ 0xFF;\r\n    data[ did++ ] = ( yt - yb ) \/ 0xFF;\r\n}<\/pre>\n<p>now<em>\u00a0data<\/em> contains the central difference for\u00a0each pixel, note that I&#8217;m only using the red value as I&#8217;m using a greyscale image so the getPixel() returning an object is overkill here but this is N-dimensional so we could use a RGB noise to\u00a0obtain a 3D central difference (or even a RGBA of course).<\/p>\n<p>now to get the direction in which a point should go, we only need to sample <em>data<\/em> at the appropriate location, something along the lines of:<\/p>\n<pre class=\"lang:js decode:true\">var i = ~~( p.x ) % iw;\r\nvar j = ~~( p.y ) % ih;\r\nvar id = ( ( i + j * iw ) * 2 );\r\np.x += data[ id ];\r\np.y += data[ id + 1 ];<\/pre>\n<p>Depending on the texture, we added more <em>water like<\/em> or <em>wind like<\/em> motions. Here&#8217;s an example, where the dots tend to come back to their origin.<br \/>\n<iframe loading=\"lazy\" width=\"300\" height=\"150\" style=\"width: 100%; height: 600px;\" src=\"http:\/\/www.barradeau.com\/dump\/fcbk\/index.html\"><\/iframe><br \/>\n<a href=\"http:\/\/codepen.io\/nicoptere\/pen\/vKxbOv\" target=\"_blank\">the code is available here<\/a><\/p>\n<h2>To wrap it up<\/h2>\n<p>This website is more complex than it looks, it required the production of a great number of &#8220;custom\u00a0assets&#8221; specifically designed to solve technical problems. The team was very dedicated to find the best possible solution while making no concession on the quality of the result (be it the sounds, the graphics or\u00a0\u00a0the animations). It&#8217;s the first time that I see so much talent and so much dedication in making the\u00a0project as good as possible\u00a0and\u00a0it is very gratifying to have been part of this adventure.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I\u00a0worked for the past 3 months\u00a0on a\u00a0website with the good people at\u00a0Upian\u00a0for the good people of\u00a0La Fondation Cartier, pour l&#8217;art contemporain. It is called: Le Grand Orchestre Des Animaux\u00a0(The Great Animal Orchestra) and is\u00a0based on the work of Bernie Krause, a man who\u00a0dedicated his life to recording the sounds of nature and raising awareness\u00a0about its\u00a0degradation &#8230; <span class=\"more\"><a class=\"more-link\" href=\"http:\/\/barradeau.com\/blog\/?p=824\">[Read more&#8230;]<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":910,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"sharing_disabled":false,"spay_email":"","jetpack_publicize_message":""},"categories":[3],"tags":[],"jetpack_featured_media_url":"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/06\/cover.jpg","jetpack_publicize_connections":[],"jetpack_shortlink":"https:\/\/wp.me\/p4oXhx-di","_links":{"self":[{"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/824"}],"collection":[{"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=824"}],"version-history":[{"count":50,"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/824\/revisions"}],"predecessor-version":[{"id":905,"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/824\/revisions\/905"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/media\/910"}],"wp:attachment":[{"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=824"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=824"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=824"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}